Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity.Design / System / Data / Entity / Design / Common / MetadataItemSerializer.cs
1 //---------------------------------------------------------------------
2 // <copyright file="MetadataItemSerializer.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 using System;
10 using System.Data.Common;
11 using System.Collections.Generic;
12 using System.Text;
13 using System.Data.Metadata.Edm;
14 using System.Reflection;
15 using System.Diagnostics;
16 using System.Xml;
17 using System.Globalization;
18 using System.IO;
19 using System.Data.Entity.Design.SsdlGenerator;
20 using System.Linq;
21
22 namespace System.Data.Entity.Design.Common
23 {
24     /// <summary>
25     /// This class is reponsible for serailizing Edm Metadata out to the appropriate file .csdl or .ssdl
26     /// </summary>
27     internal class MetadataItemSerializer
28     {
29         public static readonly EdmType NoSpecificTypeSentinal = MetadataItem.GetBuiltInType(BuiltInTypeKind.EdmType);
30
31         private bool _isModel;
32         private ErrorsLookup _errorsLookup;
33         private XmlWriter _writer;
34         private Version _schemaVersion;
35
36         private MetadataItemSerializer(XmlWriter writer, bool isModel, ErrorsLookup errorsLookup, Version schemaVersion)
37         {
38             _writer = writer;
39             _isModel = isModel;
40             _errorsLookup = errorsLookup;
41             _schemaVersion = schemaVersion;
42         }
43
44         public class ErrorsLookup : Dictionary<MetadataItem, List<EdmSchemaError>> { }
45
46         internal readonly string EdmNamespace = "Edm";
47
48         public static void WriteXml(XmlWriter writer, ItemCollection collection, string namespaceToWrite, Version schemaVersion, params KeyValuePair<string, string> [] xmlPrefixToNamespaces)
49         {
50             WriteXml(writer, collection, namespaceToWrite, new ErrorsLookup(), new List<EdmType>(), null, null, schemaVersion, xmlPrefixToNamespaces);
51         }
52
53         internal static void WriteXml(XmlWriter writer, ItemCollection collection, string namespaceToWrite, ErrorsLookup errorsLookup, List<EdmType> commentedOutItems, string provider, string providerManifestToken, Version schemaVersion, params KeyValuePair<string, string>[] xmlPrefixToNamespaces)
54         {
55             Debug.Assert(writer != null, "writer parameter is null");
56             Debug.Assert(collection != null, "collection parameter is null");
57             Debug.Assert(errorsLookup != null, "errorsLookup parameter is null");
58             Debug.Assert(!string.IsNullOrEmpty(namespaceToWrite), "namespaceToWrite parameter is null or empty");
59             
60             MetadataItemSerializer serializer = new MetadataItemSerializer(writer, collection.DataSpace == DataSpace.CSpace, errorsLookup, schemaVersion);
61
62             serializer.ValidateNamespace(namespaceToWrite);
63             serializer.WriteSchemaElement(namespaceToWrite, provider, providerManifestToken, xmlPrefixToNamespaces);
64             serializer.WriteErrorsComment(NoSpecificTypeSentinal);
65             foreach (EntityContainer item in collection.GetItems<EntityContainer>())
66             {
67                 serializer.WriteEntityContainerElement(item);
68             }
69
70             foreach (EdmType type in collection.GetItems<EdmType>())
71             {
72                 // is it in the right space (c or s)
73                 // does it have the right namespace?
74                 if (type.NamespaceName == namespaceToWrite)
75                 {
76                     serializer.WriteTypeElement(type);
77                 }
78             }
79
80             if(commentedOutItems.Count > 0)
81             {
82                 StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
83                 XmlWriterSettings settings = new XmlWriterSettings();
84                 settings.Indent = true;
85                 settings.OmitXmlDeclaration = true;
86                 // we can have more than one commented out type
87                 // which will look like multiple root elements, so this is a fragment
88                 settings.ConformanceLevel = ConformanceLevel.Fragment;
89                 XmlWriter commentWriter = XmlWriter.Create(stringWriter, settings);
90                 MetadataItemSerializer commmentSerializer = new MetadataItemSerializer(commentWriter, collection.DataSpace == DataSpace.CSpace, errorsLookup, schemaVersion);
91                 foreach (EdmType type in commentedOutItems)
92                 {
93                     commmentSerializer.WriteTypeElement(type);
94                 }
95                 commentWriter.Flush();
96                 //This is not the cleanest thing to do but XmlTextWriter 
97                 //does not allow writing xml comment characters while writing a comment.
98                 //and since we know exactly the string we write, this is pretty safe.
99                 string comment = RemoveXmlCommentCharacters(stringWriter);
100                 writer.WriteComment(comment);
101             }
102             writer.WriteEndElement();
103         }
104
105         private static string RemoveXmlCommentCharacters(StringWriter stringWriter)
106         {
107             string comment = stringWriter.GetStringBuilder().ToString();
108             while (comment.Contains(XmlConstants.XmlCommentStartString))
109             {
110                 comment = comment.Replace(XmlConstants.XmlCommentStartString, String.Empty);
111             }
112             while (comment.Contains(XmlConstants.XmlCommentEndString))
113             {
114                 comment = comment.Replace(XmlConstants.XmlCommentEndString, String.Empty);
115             }
116             return comment;
117         }
118
119         private void ValidateNamespace(string namespaceToWrite)
120         {
121             if (EdmItemCollection.IsSystemNamespace(MetadataItem.EdmProviderManifest, namespaceToWrite))
122             {
123                 throw EDesignUtil.EdmReservedNamespace(namespaceToWrite);
124             }
125         }
126
127         private void WriteTypeElement(EdmType type)
128         {
129             WriteErrorsComment(type);
130             switch (type.BuiltInTypeKind)
131             {
132                 case BuiltInTypeKind.EntityType:
133                     WriteEntityTypeElement((EntityType)type);
134                     break;
135                 case BuiltInTypeKind.AssociationType:
136                     WriteAssociationTypeElement((AssociationType)type);
137                     break;
138                 case BuiltInTypeKind.EdmFunction:
139                     WriteFunctionElement((EdmFunction)type);
140                     break;
141                 case BuiltInTypeKind.ComplexType:
142                     WriteComplexTypeElement((ComplexType)type);
143                     break;
144                 case BuiltInTypeKind.RowType:
145                     WriteRowTypeElement((RowType)type);
146                     break;
147                 default:
148                     throw EDesignUtil.NonSerializableType(type.BuiltInTypeKind);
149             }
150         }
151
152         private void WriteFunctionElement(EdmFunction function)
153         {
154             _writer.WriteStartElement(function.IsFunctionImport ? XmlConstants.FunctionImport : XmlConstants.Function);
155             _writer.WriteAttributeString(XmlConstants.Name, function.Name);
156
157             // Write function ReturnType as attribute if possible.
158             bool returnParameterHandled = false;
159             if (function.ReturnParameter != null)
160             {
161                 var returnTypeUsage = function.ReturnParameter.TypeUsage;
162                 bool collection = returnTypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType;
163                 if (collection)
164                 {
165                     Debug.Assert(_schemaVersion >= EntityFrameworkVersions.Version3, "_schemaVersion >= EntityFrameworkVersions.Version3");
166                     returnTypeUsage = ((CollectionType)returnTypeUsage.EdmType).TypeUsage;
167                 }
168                 if (TypeSemantics.IsPrimitiveType(returnTypeUsage) || TypeSemantics.IsNominalType(returnTypeUsage))
169                 {
170                     string typeName = GetFullName(returnTypeUsage.EdmType);
171                     if (collection)
172                     {
173                         typeName = "Collection(" + typeName + ")";
174                     }
175                     _writer.WriteAttributeString(XmlConstants.ReturnType, typeName);
176                     returnParameterHandled = true;
177                 }
178             }
179
180             if (!_isModel)
181             {
182                 _writer.WriteAttributeString(XmlConstants.AggregateAttribute, GetAttributeValueString(function.AggregateAttribute));
183                 _writer.WriteAttributeString(XmlConstants.BuiltInAttribute, GetAttributeValueString(function.BuiltInAttribute));
184                 _writer.WriteAttributeString(XmlConstants.NiladicFunction, GetAttributeValueString(function.NiladicFunctionAttribute));
185                 _writer.WriteAttributeString(XmlConstants.IsComposable, GetAttributeValueString(function.IsComposableAttribute));
186                 _writer.WriteAttributeString(XmlConstants.ParameterTypeSemantics, GetAttributeValueString(function.ParameterTypeSemanticsAttribute));
187             }
188             else if (function.IsFunctionImport && function.IsComposableAttribute)
189             {
190                 Debug.Assert(_schemaVersion >= EntityFrameworkVersions.Version3, "_schemaVersion >= EntityFrameworkVersions.Version3");
191                 _writer.WriteAttributeString(XmlConstants.IsComposable, GetAttributeValueString(true));
192             }
193             
194             if (function.StoreFunctionNameAttribute != null)
195             {
196                 _writer.WriteAttributeString(XmlConstants.StoreFunctionName, function.StoreFunctionNameAttribute);
197             }
198             
199             if(function.CommandTextAttribute != null)
200             {
201                 Debug.Assert(!_isModel, "Serialization of CommandTextAttribute is not supported for CSDL.");
202                 _writer.WriteAttributeString(XmlConstants.CommandText, function.CommandTextAttribute);
203             }
204
205             if (function.Schema != null)
206             {
207                 _writer.WriteAttributeString(XmlConstants.Schema, function.Schema);
208             }
209
210             foreach (FunctionParameter parameter in function.Parameters)
211             {
212                 WriteFunctionParameterElement(parameter);
213             }
214
215             // Write function ReturnType subelement if needed.
216             if (function.ReturnParameter != null && !returnParameterHandled)
217             {
218                 // Handle a TVF in s-space: Collection(RowType)
219                 if (function.ReturnParameter.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType)
220                 {
221                     Debug.Assert(_schemaVersion >= EntityFrameworkVersions.Version3 && !_isModel, "_schemaVersion >= EntityFrameworkVersions.Version3 && !_isModel");
222                     var elementType = ((CollectionType)function.ReturnParameter.TypeUsage.EdmType).TypeUsage.EdmType;
223                     Debug.Assert(elementType.BuiltInTypeKind == BuiltInTypeKind.RowType, "TVF return type is expected to be Collection(RowType)");
224                     var rowType = (RowType)elementType;
225                     _writer.WriteStartElement(XmlConstants.ReturnType);
226                     _writer.WriteStartElement(XmlConstants.CollectionType);
227                     WriteTypeElement(rowType);
228                     _writer.WriteEndElement();
229                     _writer.WriteEndElement();
230                     returnParameterHandled = true;
231                 }
232             }
233
234             Debug.Assert(function.ReturnParameter == null || returnParameterHandled, "ReturnParameter was not handled.");
235             _writer.WriteEndElement();
236         }
237
238         private void WriteFunctionParameterElement(FunctionParameter parameter)
239         {
240             _writer.WriteStartElement(XmlConstants.Parameter);
241             _writer.WriteAttributeString(XmlConstants.Name, parameter.Name);
242             _writer.WriteAttributeString(XmlConstants.TypeAttribute, GetFullName(parameter.TypeUsage.EdmType));
243             if (!_isModel)
244             {
245                 _writer.WriteAttributeString(XmlConstants.Mode, GetAttributeValueString(parameter.Mode));
246             }
247             _writer.WriteEndElement();
248         }
249
250
251         private void WriteComplexTypeElement(ComplexType complexType)
252         {
253             _writer.WriteStartElement(XmlConstants.ComplexType);
254             _writer.WriteAttributeString(XmlConstants.Name, complexType.Name);
255             if (complexType.BaseType != null)
256             {
257                 _writer.WriteAttributeString(XmlConstants.BaseType, GetFullName(complexType.BaseType));
258             }
259
260             foreach (EdmMember member in complexType.GetDeclaredOnlyMembers<EdmMember>())
261             {
262                 WritePropertyElement(member);
263             }
264             _writer.WriteEndElement();
265         }
266
267         private void WriteAssociationTypeElement(AssociationType associationType)
268         {
269             _writer.WriteStartElement(XmlConstants.Association);
270             _writer.WriteAttributeString(XmlConstants.Name, associationType.Name);
271             foreach (RelationshipEndMember end in associationType.RelationshipEndMembers)
272             {
273                 WriteRelationshipEndElement(end);
274             }
275
276             foreach (ReferentialConstraint constraint in associationType.ReferentialConstraints)
277             {
278                 WriteReferentialConstraintElement(constraint);
279             }
280
281             _writer.WriteEndElement();
282         }
283
284         private void WriteRowTypeElement(RowType rowType)
285         {
286             _writer.WriteStartElement(XmlConstants.RowType);
287             foreach (var property in rowType.Properties)
288             {
289                 WritePropertyElement(property);
290             }
291             _writer.WriteEndElement();
292         }
293
294         private void WriteReferentialConstraintElement(ReferentialConstraint constraint)
295         {
296             _writer.WriteStartElement(XmlConstants.ReferentialConstraint);
297             WriteReferentialConstraintRoleElement(XmlConstants.PrincipalRole, constraint.FromRole, constraint.FromProperties);
298             WriteReferentialConstraintRoleElement(XmlConstants.DependentRole, constraint.ToRole, constraint.ToProperties);
299             _writer.WriteEndElement();
300         }
301
302         private void WriteReferentialConstraintRoleElement(string nodeName, RelationshipEndMember end, IList<EdmProperty> properties)
303         {
304             // Generate the principal and dependent role nodes
305             _writer.WriteStartElement(nodeName);
306             _writer.WriteAttributeString(XmlConstants.Role, end.Name);
307             for (int i = 0; i < properties.Count; i++)
308             {
309                 _writer.WriteStartElement(XmlConstants.PropertyRef);
310                 _writer.WriteAttributeString(XmlConstants.Name, properties[i].Name);
311                 _writer.WriteEndElement();
312             }
313             _writer.WriteEndElement();
314         }
315
316         private void WriteRelationshipEndElement(RelationshipEndMember end)
317         {
318             _writer.WriteStartElement(XmlConstants.End);
319             _writer.WriteAttributeString(XmlConstants.Role, end.Name);
320
321             string typeName = GetFullName(((RefType)end.TypeUsage.EdmType).ElementType);
322             _writer.WriteAttributeString(XmlConstants.TypeAttribute, typeName);
323             _writer.WriteAttributeString(XmlConstants.Multiplicity, GetXmlMultiplicity(end.RelationshipMultiplicity));
324             if (end.DeleteBehavior != OperationAction.None)
325             {
326                 WriteOperationActionElement(XmlConstants.OnDelete, end.DeleteBehavior);
327             }
328             _writer.WriteEndElement();
329         }
330
331         private void WriteOperationActionElement(string elementName, OperationAction operationAction)
332         {
333             _writer.WriteStartElement(elementName);
334             _writer.WriteAttributeString(XmlConstants.Action, operationAction.ToString());
335             _writer.WriteEndElement();
336         }
337
338         private string GetXmlMultiplicity(RelationshipMultiplicity relationshipMultiplicity)
339         {
340             switch(relationshipMultiplicity)
341             {
342                 case RelationshipMultiplicity.Many:
343                     return "*";
344                 case RelationshipMultiplicity.One:
345                     return "1";
346                 case RelationshipMultiplicity.ZeroOrOne:
347                     return "0..1";
348                 default:
349                     Debug.Fail("Did you add a new RelationshipMultiplicity?");
350                     return string.Empty;
351             }
352         }
353
354         private void WriteEntityTypeElement(EntityType entityType)
355         {
356             _writer.WriteStartElement(XmlConstants.EntityType);
357             _writer.WriteAttributeString(XmlConstants.Name, entityType.Name);
358             if (entityType.BaseType != null)
359             {
360                 _writer.WriteAttributeString(XmlConstants.BaseType, GetFullName(entityType.BaseType));
361             }
362
363             if (entityType.Abstract)
364             {
365                 _writer.WriteAttributeString(XmlConstants.Abstract, XmlConstants.True);
366             }
367
368             if (entityType.KeyMembers.Count != 0 && 
369                 entityType.KeyMembers[0].DeclaringType == entityType) // they are declared on this entity
370             {
371                 _writer.WriteStartElement(XmlConstants.Key);
372                 for (int i = 0; i < entityType.KeyMembers.Count; i++)
373                 {
374                     _writer.WriteStartElement(XmlConstants.PropertyRef);
375                     _writer.WriteAttributeString(XmlConstants.Name, entityType.KeyMembers[i].Name);
376                     _writer.WriteEndElement();
377                 }
378                 _writer.WriteEndElement();
379             }
380
381             foreach (EdmProperty member in entityType.GetDeclaredOnlyMembers<EdmProperty>())
382             {
383                 WritePropertyElement(member);
384             }
385             
386             foreach (NavigationProperty navigationProperty in entityType.NavigationProperties )
387             {
388                 if (navigationProperty.DeclaringType == entityType)
389                 {
390                     WriteNavigationPropertyElement(navigationProperty);
391                 }
392             }
393             _writer.WriteEndElement();
394         }
395
396         private void WriteErrorsComment(EdmType type)
397         {
398             List<EdmSchemaError> errors;
399             if (_errorsLookup.TryGetValue(type, out errors))
400             {
401                 Debug.Assert(errors.Count > 0, "how did we get an empty errors collection?");
402
403                 StringBuilder builder = new StringBuilder();
404                 builder.AppendLine(Strings.MetadataItemErrorsFoundDuringGeneration);
405                 foreach (EdmSchemaError error in errors)
406                 {
407                     builder.AppendLine(error.ToString());
408                 }
409                 _writer.WriteComment(builder.ToString());
410             }
411         }
412
413         private void WriteNavigationPropertyElement(NavigationProperty member)
414         {
415             _writer.WriteStartElement(XmlConstants.NavigationProperty);
416             _writer.WriteAttributeString(XmlConstants.Name, member.Name);
417             _writer.WriteAttributeString(XmlConstants.Relationship, member.RelationshipType.FullName);
418             _writer.WriteAttributeString(XmlConstants.FromRole, member.FromEndMember.Name);
419             _writer.WriteAttributeString(XmlConstants.ToRole, member.ToEndMember.Name);
420             _writer.WriteEndElement();
421         }
422
423         private void WritePropertyElement(EdmMember member)
424         {
425             _writer.WriteStartElement(XmlConstants.Property);
426             _writer.WriteAttributeString(XmlConstants.Name, member.Name);
427             _writer.WriteAttributeString(XmlConstants.TypeAttribute, GetTypeName(member.TypeUsage));
428             WritePropertyTypeFacets(member.TypeUsage);
429
430             //
431             // Generate "annotation:StoreGeneratedPattern="Identity"" for model schema
432             //
433             if (_isModel && member.MetadataProperties.Contains(DesignXmlConstants.EdmAnnotationNamespace + ":" + DesignXmlConstants.StoreGeneratedPattern))
434             {
435                 _writer.WriteAttributeString(
436                     TranslateFacetNameToAttributeName(
437                         DesignXmlConstants.StoreGeneratedPattern),
438                     DesignXmlConstants.EdmAnnotationNamespace, 
439                     GetAttributeValueString(
440                         member.MetadataProperties[DesignXmlConstants.EdmAnnotationNamespace + ":" + DesignXmlConstants.StoreGeneratedPattern].Value));
441             }
442
443             _writer.WriteEndElement();
444         }
445
446         private void WritePropertyTypeFacets(TypeUsage typeUsage)
447         {
448             // we need to use the facets for this particular provider, not the ones that they type
449             // may have been converted to (think CSDL types converted to provider types)
450             EdmType type = GetEdmType(typeUsage);
451             IEnumerable<FacetDescription> providerDescriptions = GetAssociatedFacetDescriptions(type);
452
453             foreach (Facet facet in typeUsage.Facets)
454             {
455                 FacetDescription providerFacetDescription = null;
456                 if (IsSpecialFacet(facet))
457                 {
458                     providerFacetDescription = facet.Description;
459                 }
460                 else
461                 {
462                     foreach (FacetDescription description in providerDescriptions)
463                     {
464                         if (description.FacetName == facet.Name)
465                         {
466                             providerFacetDescription = description;
467                             break;
468                         }
469                     }
470                 }
471
472                 //
473                 // Don't emit this facet if we shouldn't
474                 //
475                 if (SkipFacet(facet, providerFacetDescription))
476                 {
477                     continue;
478                 }
479
480                 //
481                 // Special case for MaxLength facet value of "Max"
482                 //
483                 if (_isModel && 
484                     type.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType)
485                 {
486                     PrimitiveType primitiveType = (PrimitiveType)type;
487
488                     if ((primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.String ||
489                          primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.Binary) &&
490                         facet.Name == DbProviderManifest.MaxLengthFacetName &&
491                         Helper.IsUnboundedFacetValue(facet))
492                     {
493                         _writer.WriteAttributeString(TranslateFacetNameToAttributeName(facet.Name), XmlConstants.Max);
494                         continue;
495                     }
496                 }
497
498                 _writer.WriteAttributeString(TranslateFacetNameToAttributeName(facet.Name), GetAttributeValueString(facet.Value));
499             }
500         }
501
502         
503         private string TranslateFacetNameToAttributeName(string facetName)
504         {
505             if(DbProviderManifest.DefaultValueFacetName == facetName)
506             {
507                 return XmlConstants.DefaultValueAttribute;
508             }
509
510             return facetName;
511         }
512
513         /// <summary>
514         /// Should this facet be skipped ?
515         /// A facet should be skipped if it satsifies one of the following
516         ///   - the providerFacetDescription is null - (ie) the provider knows of no such facet
517         ///   - the facetDescription indicates that the facet must have a constant value
518         ///   - the facet value is null
519         ///   - the facet value is the default value for the facet, and the facet is not required
520         ///   - we're emitting a model schema, and the facet in question is one of the following
521         ///       - MaxLength, FixedLength, Unicode, Collation, Precision, Scale, DateTimeKind
522         /// </summary>
523         /// <param name="facet">the facet in question</param>
524         /// <param name="providerFacetDescription">facet description in the provider</param>
525         /// <returns>true, if the facet should be skipped</returns>
526         private bool SkipFacet(Facet facet, FacetDescription providerFacetDescription)
527         {
528             //
529             // if the provider doesn't recognize it, it will complain
530             // when it sees it; so don't put it in
531             //
532             if (providerFacetDescription == null) 
533             {
534                 return true;
535             }
536             // skip it if it is constant for the current provider
537             if (providerFacetDescription.IsConstant)
538             {
539                 return true;
540             }
541
542             //
543             // Null facets can and should be omitted
544             //
545             if (facet.Value == null)
546             {
547                 return true;
548             }
549
550             //
551             // skip if it is not required, and has the default value
552             //
553             if (!providerFacetDescription.IsRequired &&
554                 facet.Value.Equals(providerFacetDescription.DefaultValue))
555             {
556                 return true;
557             }
558
559             return false;
560         }
561
562         private bool IsSpecialFacet(Facet facet)
563         {
564             if(_isModel)
565             {
566                 return (facet.Name == "ClientAutoGenerated" ||
567                         facet.Name == EdmProviderManifest.ConcurrencyModeFacetName ||
568                         facet.Name == XmlConstants.StoreGeneratedPattern ||
569                         facet.Name == DbProviderManifest.CollationFacetName);
570             }
571             else
572             {
573                 return (facet.Name == EdmProviderManifest.StoreGeneratedPatternFacetName || 
574                         facet.Name == DbProviderManifest.CollationFacetName);
575             }
576         }
577
578         private IEnumerable<FacetDescription> GetAssociatedFacetDescriptions(EdmType type)
579         {
580             MethodInfo mi = typeof(EdmType).GetMethod("GetAssociatedFacetDescriptions", BindingFlags.NonPublic | BindingFlags.Instance);
581             Debug.Assert(mi != null, "Method GetAssociatedFacetDescriptions is missing");
582             return (IEnumerable<FacetDescription>)mi.Invoke(type, new object[0]);
583         }
584
585         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
586         private string GetAttributeValueString(object o)
587         {
588             if (o.GetType() == typeof(bool))
589             {
590                 return o.ToString().ToLower(CultureInfo.InvariantCulture);
591             }
592             else
593             {
594                 return o.ToString();
595             }
596         }
597
598         private EdmType GetEdmType(TypeUsage typeUsage)
599         {
600             if (_isModel)
601             {
602                 return GetModelType(typeUsage.EdmType);
603             }
604             else
605             {
606                 return typeUsage.EdmType;
607             }
608         }
609         private string GetTypeName(TypeUsage typeUsage)
610         {
611             EdmType type = GetEdmType(typeUsage);
612             if (type.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType)
613             {
614                 return type.Name;
615             }
616             else
617             {
618                 return GetFullName(type);
619             }
620         }
621
622         private EdmType GetModelType(EdmType edmType)
623         {
624             if (edmType.BuiltInTypeKind != BuiltInTypeKind.PrimitiveType)
625             {
626                 return edmType;
627             }
628
629             while (edmType != null && edmType.NamespaceName != EdmNamespace)
630             {
631                 edmType = edmType.BaseType;
632             }
633
634             return edmType;
635         }
636
637         private void WriteSchemaElement(string schemaNamespace, string provider, string providerManifestToken, params KeyValuePair<string, string>[] xmlPrefixToNamespaces)
638         {
639             string xmlNamespace = EntityFrameworkVersions.GetSchemaNamespace(_schemaVersion, _isModel ? DataSpace.CSpace : DataSpace.SSpace);
640             _writer.WriteStartElement(XmlConstants.Schema, xmlNamespace);
641             _writer.WriteAttributeString(XmlConstants.Namespace, schemaNamespace);
642             _writer.WriteAttributeString(XmlConstants.Alias, "Self");
643             if (_isModel && _schemaVersion >= EntityFrameworkVersions.Version3)
644             {
645                 _writer.WriteAttributeString(XmlConstants.UseStrongSpatialTypes, XmlConstants.AnnotationNamespace, XmlConstants.False);
646             }
647             if (!_isModel)
648             {
649                 if (!string.IsNullOrEmpty(provider))
650                 {
651                     _writer.WriteAttributeString(XmlConstants.Provider, provider);
652                 }
653
654                 if (!string.IsNullOrEmpty(providerManifestToken))
655                 {
656                     _writer.WriteAttributeString(XmlConstants.ProviderManifestToken, providerManifestToken);
657                 }
658             }
659
660             // write out the extra xml namespaces and their pretty prefix
661             foreach (KeyValuePair<string, string> xmlPrefixToNamespace in xmlPrefixToNamespaces)
662             {
663                 // see http://www.w3.org/TR/2006/REC-xml-names-20060816/
664                 _writer.WriteAttributeString("xmlns", xmlPrefixToNamespace.Key, null, xmlPrefixToNamespace.Value);
665             }
666         }
667
668         private void WriteEntityContainerElement(EntityContainer container)
669         {
670             _writer.WriteStartElement(XmlConstants.EntityContainer);
671             _writer.WriteAttributeString(XmlConstants.Name, container.Name);
672
673             //
674             // Generate "annotation:LazyLoadingEnabled="true"" for model schema
675             //
676             if (_isModel && container.MetadataProperties.Contains(DesignXmlConstants.EdmAnnotationNamespace + ":" + DesignXmlConstants.LazyLoadingEnabled))
677             {
678                 _writer.WriteAttributeString(
679                     TranslateFacetNameToAttributeName(
680                         DesignXmlConstants.LazyLoadingEnabled),
681                     DesignXmlConstants.EdmAnnotationNamespace,
682                     GetAttributeValueString(
683                         container.MetadataProperties[DesignXmlConstants.EdmAnnotationNamespace + ":" + DesignXmlConstants.LazyLoadingEnabled].Value));
684             }
685
686             foreach (EntitySetBase set in container.BaseEntitySets)
687             {
688                 switch (set.BuiltInTypeKind)
689                 {
690                     case BuiltInTypeKind.EntitySet:
691                         WriteEntitySetElement((EntitySet)set);
692                         break;
693                     case BuiltInTypeKind.AssociationSet:
694                         WriteAssociationSetElement((AssociationSet)set);
695                         break;
696                     default:
697                         throw EDesignUtil.NonSerializableType(set.BuiltInTypeKind);
698                 }
699             }
700
701             foreach (EdmFunction functionImport in container.FunctionImports.Where(fi => fi.IsComposableAttribute))
702             {
703                 WriteFunctionElement(functionImport);
704             }
705             
706             _writer.WriteEndElement();
707         }
708
709         private void WriteAssociationSetElement(AssociationSet associationSet)
710         {
711             _writer.WriteStartElement(XmlConstants.AssociationSet);
712             _writer.WriteAttributeString(XmlConstants.Name, associationSet.Name);
713             _writer.WriteAttributeString(XmlConstants.Association, GetFullName(associationSet.ElementType));
714             
715             foreach (AssociationSetEnd end in associationSet.AssociationSetEnds)
716             {
717                 WriteAssociationSetEndElement(end);
718             }
719             _writer.WriteEndElement();
720         }
721
722         private void WriteAssociationSetEndElement(AssociationSetEnd end)
723         {
724             _writer.WriteStartElement(XmlConstants.End);
725             _writer.WriteAttributeString(XmlConstants.Role, end.Name);
726             _writer.WriteAttributeString(XmlConstants.EntitySet, end.EntitySet.Name);
727             _writer.WriteEndElement();
728         }
729
730         private void WriteEntitySetElement(EntitySet entitySet)
731         {
732             _writer.WriteStartElement(XmlConstants.EntitySet);
733             _writer.WriteAttributeString(XmlConstants.Name, entitySet.Name);
734             _writer.WriteAttributeString(XmlConstants.EntityType, GetFullName(entitySet.ElementType));
735             WriteExtendedPropertyAttributes(entitySet);
736
737             MetadataProperty property;
738             if (entitySet.MetadataProperties.TryGetValue(XmlConstants.DefiningQuery, false, out property) &&
739                 property.Value != null)
740             {
741                 _writer.WriteStartElement(XmlConstants.DefiningQuery);
742                 _writer.WriteString(entitySet.DefiningQuery);
743                 _writer.WriteEndElement();
744             }
745             else
746             {
747                 if (entitySet.MetadataProperties.TryGetValue(XmlConstants.Schema, false, out property) &&
748                     property.Value != null)
749                 {
750                     _writer.WriteAttributeString(property.Name, property.Value.ToString());
751                 }
752
753                 if (entitySet.MetadataProperties.TryGetValue(XmlConstants.Table, false, out property) &&
754                     property.Value != null)
755                 {
756                     _writer.WriteAttributeString(property.Name, property.Value.ToString());
757                 }
758             }
759
760
761             _writer.WriteEndElement();
762         }
763
764         private void WriteExtendedPropertyAttributes(MetadataItem item)
765         {
766             foreach (MetadataProperty property in item.MetadataProperties.Where(p => p.PropertyKind == PropertyKind.Extended))
767             {
768                 string xmlNamespace, attributeName;
769                 if (MetadataUtil.TrySplitExtendedMetadataPropertyName(property.Name, out xmlNamespace, out attributeName))
770                 {
771                     _writer.WriteAttributeString(attributeName, xmlNamespace, property.Value.ToString());
772                 }
773             }
774         }
775
776         private string GetFullName(EdmType type)
777         {
778             string namespaceName = null;
779             string name;
780             string modifierFormat = null;
781
782             if (type.BuiltInTypeKind == BuiltInTypeKind.CollectionType)
783             {
784                 type = ((CollectionType)type).TypeUsage.EdmType;
785                 modifierFormat = "Collection({0})";
786             }
787
788             if (type.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType)
789             {
790                 // primitive types are not required to be qualified   
791                 name = type.Name;
792             }
793             else
794             {
795                 namespaceName = type.NamespaceName;
796                 name = type.Name;
797             }
798
799             string qualifiedTypeName;
800             if (namespaceName == null)
801             {
802                 qualifiedTypeName = name;
803             }
804             else
805             {
806                 qualifiedTypeName = namespaceName + "." + name;
807             }
808
809             if (modifierFormat != null)
810             {
811                 qualifiedTypeName = string.Format(CultureInfo.InvariantCulture, modifierFormat, qualifiedTypeName);
812             }
813
814             return qualifiedTypeName;
815          }
816
817     }
818 }