abde1abf26af8755f07ff94337daf3886374c55e
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Metadata / ObjectHelper.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ObjectHelper.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 using System.Data.Mapping;
11 using System.Diagnostics;
12 using System.Collections;
13 using System.Collections.Generic;
14 using System.Collections.ObjectModel;
15 using System.Text;
16 using System.Data.Common.Utils;
17 using System.IO;
18
19 namespace System.Data.Metadata.Edm
20 {
21     /// <summary>
22     /// Helper Class for EDM Metadata - this class contains all the helper methods
23     /// which needs access to internal methods. The other partial class contains all 
24     /// helper methods which just uses public methods/properties. The reason why we 
25     /// did this for allowing view gen to happen at compile time - all the helper
26     /// methods that view gen or mapping uses are in the other helper class. Rest of the
27     /// methods are in this class
28     /// </summary>
29     internal static partial class Helper
30     {
31         #region Fields
32         // List of all the static empty list used all over the code
33         internal static readonly ReadOnlyCollection<KeyValuePair<string, object>> EmptyKeyValueStringObjectList = new ReadOnlyCollection<KeyValuePair<string, object>>(new KeyValuePair<string, object>[0]);
34         internal static readonly ReadOnlyCollection<string> EmptyStringList = new ReadOnlyCollection<string>(new string[0]);
35         internal static readonly ReadOnlyCollection<FacetDescription> EmptyFacetDescriptionEnumerable = new ReadOnlyCollection<FacetDescription>(new FacetDescription[0]);
36         internal static readonly ReadOnlyCollection<EdmFunction> EmptyEdmFunctionReadOnlyCollection = new ReadOnlyCollection<EdmFunction>(new EdmFunction[0]);
37         internal static readonly ReadOnlyCollection<PrimitiveType> EmptyPrimitiveTypeReadOnlyCollection = new ReadOnlyCollection<PrimitiveType>(new PrimitiveType[0]);
38         internal static readonly KeyValuePair<string, object>[] EmptyKeyValueStringObjectArray = new KeyValuePair<string, object>[0];
39
40         internal const char PeriodSymbol = '.';
41         internal const char CommaSymbol = ',';
42         #endregion
43
44         #region Methods
45
46         /// <summary>
47         /// Returns the single error message from the list of errors
48         /// </summary>
49         /// <param name="errors"></param>
50         /// <returns></returns>
51         static internal string CombineErrorMessage(IEnumerable<System.Data.Metadata.Edm.EdmSchemaError> errors)
52         {
53             Debug.Assert(errors != null);
54             StringBuilder sb = new StringBuilder(System.Environment.NewLine);
55             int count = 0;
56             foreach (System.Data.Metadata.Edm.EdmSchemaError error in errors)
57             {
58                 //Don't append a new line at the beginning of the messages
59                 if ((count++) != 0)
60                 {
61                     sb.Append(System.Environment.NewLine);
62                 }
63                 sb.Append(error.ToString());
64                 
65             }
66             Debug.Assert(count!=0, "Empty Error List");
67             return sb.ToString();
68         }
69
70         /// <summary>
71         /// Returns the single error message from the list of errors
72         /// </summary>
73         /// <param name="errors"></param>
74         /// <returns></returns>
75         static internal string CombineErrorMessage(IEnumerable<EdmItemError> errors) {
76             StringBuilder sb = new StringBuilder(System.Environment.NewLine);
77             int count = 0;
78             foreach (EdmItemError error in errors) {
79                 // Only add the new line if this is not the first error
80                 if ((count++) != 0)
81                 {
82                     sb.Append(System.Environment.NewLine);
83                 }                
84                 sb.Append(error.Message);                
85             }
86
87             return sb.ToString();
88         }
89
90         // requires: enumerations must have the same number of members
91         // effects: returns paired enumeration values
92         internal static IEnumerable<KeyValuePair<T, S>> PairEnumerations<T, S>(IBaseList<T> left, IEnumerable<S> right)
93         {
94             IEnumerator leftEnumerator = left.GetEnumerator();
95             IEnumerator<S> rightEnumerator = right.GetEnumerator();
96
97             while (leftEnumerator.MoveNext() && rightEnumerator.MoveNext())
98             {
99                 yield return new KeyValuePair<T, S>((T)leftEnumerator.Current, rightEnumerator.Current);
100             }
101
102             yield break;
103         }
104
105         /// <summary>
106         /// Returns a model (C-Space) typeusage for the given typeusage. if the type is already in c-space, it returns
107         /// the given typeusage. The typeUsage returned is created by invoking the provider service to map from provider
108         /// specific type to model type.
109         /// </summary>
110         /// <param name="typeUsage">typeusage</param>
111         /// <returns>the respective Model (C-Space) typeusage</returns>
112         static internal TypeUsage GetModelTypeUsage(TypeUsage typeUsage)
113         {
114             return typeUsage.GetModelTypeUsage();
115         }
116
117         /// <summary>
118         /// Returns a model (C-Space) typeusage for the given member typeusage. if the type is already in c-space, it returns
119         /// the given typeusage. The typeUsage returned is created by invoking the provider service to map from provider
120         /// specific type to model type.
121         /// </summary>
122         /// <param name="member">EdmMember</param>
123         /// <returns>the respective Model (C-Space) typeusage</returns>
124         static internal TypeUsage GetModelTypeUsage(EdmMember member)
125         {
126             return GetModelTypeUsage(member.TypeUsage);
127         }
128
129         /// <summary>
130         /// Checks if the edm type in the cspace type usage maps to some sspace type (called it S1). If S1 is equivalent or
131         /// promotable to the store type in sspace type usage, then it creates a new type usage with S1 and copies all facets
132         /// if necessary
133         /// </summary>
134         /// <param name="edmProperty">Edm property containing the cspace member type information</param>
135         /// <param name="columnProperty">edm property containing the sspace member type information</param>
136         /// <param name="fileName">name of the mapping file from which this information was loaded from</param>
137         /// <param name="lineInfo">lineInfo containing the line information about the cspace and sspace property mapping</param>
138         /// <param name="parsingErrors">List of parsing errors - we need to add any new error to this list</param>
139         /// <param name="storeItemCollection">store item collection</param>
140         /// <returns></returns>
141         internal static TypeUsage ValidateAndConvertTypeUsage(EdmProperty edmProperty, 
142             EdmProperty columnProperty, Xml.IXmlLineInfo lineInfo, string sourceLocation, 
143             List<EdmSchemaError> parsingErrors, StoreItemCollection storeItemCollection)
144         {
145             Debug.Assert(edmProperty.TypeUsage.EdmType.DataSpace == DataSpace.CSpace, "cspace property must have a cspace type");
146             Debug.Assert(columnProperty.TypeUsage.EdmType.DataSpace == DataSpace.SSpace, "sspace type usage must have a sspace type");
147             Debug.Assert(
148                 Helper.IsScalarType(edmProperty.TypeUsage.EdmType), 
149                 "cspace property must be of a primitive or enumeration type");
150             Debug.Assert(Helper.IsPrimitiveType(columnProperty.TypeUsage.EdmType), "sspace property must contain a primitive type");
151
152             TypeUsage mappedStoreType = ValidateAndConvertTypeUsage(edmProperty, 
153                                                                     lineInfo, 
154                                                                     sourceLocation,
155                                                                     edmProperty.TypeUsage,
156                                                                     columnProperty.TypeUsage, 
157                                                                     parsingErrors, 
158                                                                     storeItemCollection);
159
160             return mappedStoreType;
161         }
162
163         internal static TypeUsage ValidateAndConvertTypeUsage(EdmMember edmMember,
164                                                                   Xml.IXmlLineInfo lineInfo,
165                                                                   string sourceLocation,
166                                                                   TypeUsage cspaceType,
167                                                                   TypeUsage sspaceType,
168                                                                   List<EdmSchemaError> parsingErrors,
169                                                                   StoreItemCollection storeItemCollection)
170         {
171             // if we are already C-Space, dont call the provider. this can happen for functions.
172             TypeUsage modelEquivalentSspace = sspaceType;
173             if (sspaceType.EdmType.DataSpace == DataSpace.SSpace)
174             {
175                 modelEquivalentSspace = sspaceType.GetModelTypeUsage();
176             }
177
178             // check that cspace type is subtype of c-space equivalent type from the ssdl definition
179             if (ValidateScalarTypesAreCompatible(cspaceType, modelEquivalentSspace))
180             {
181                 return modelEquivalentSspace;
182             }
183             return null;
184         }
185         #endregion
186
187         /// <summary>
188         /// Validates whether cspace and sspace types are compatible.
189         /// </summary>
190         /// <param name="cspaceType">Type in C-Space. Must be a primitive or enumeration type.</param>
191         /// <param name="storeType">C-Space equivalent of S-space Type. Must be a primitive type.</param>
192         /// <returns>
193         /// <c>true</c> if the types are compatible. <c>false</c> otherwise.
194         /// </returns>
195         /// <remarks>
196         /// This methods validate whether cspace and sspace types are compatible. The types are
197         /// compatible if:
198         /// both are primitive and the cspace type is a subtype of sspace type 
199         /// or
200         /// cspace type is an enumeration type whose underlying type is a subtype of sspace type.
201         /// </remarks>
202         private static bool ValidateScalarTypesAreCompatible(TypeUsage cspaceType, TypeUsage storeType)
203         {
204             Debug.Assert(cspaceType != null, "cspaceType != null");
205             Debug.Assert(storeType != null, "storeType != null");
206             Debug.Assert(cspaceType.EdmType.DataSpace == DataSpace.CSpace, "cspace property must have a cspace type");
207             Debug.Assert(storeType.EdmType.DataSpace == DataSpace.CSpace, "storeType type usage must have a sspace type");
208             Debug.Assert(
209                 Helper.IsScalarType(cspaceType.EdmType),
210                 "cspace property must be of a primitive or enumeration type");
211             Debug.Assert(Helper.IsPrimitiveType(storeType.EdmType), "storeType property must be a primitive type");
212
213             if (Helper.IsEnumType(cspaceType.EdmType))
214             {
215                 // For enum cspace type check whether its underlying type is a subtype of the store type. Note that
216                 // TypeSemantics.IsSubTypeOf uses only TypeUsage.EdmType for primitive types so there is no need to copy facets 
217                 // from the enum type property to the underlying type TypeUsage created here since they wouldn't be used anyways.
218                 return TypeSemantics.IsSubTypeOf(TypeUsage.Create(Helper.GetUnderlyingEdmTypeForEnumType(cspaceType.EdmType)), storeType); 
219             }
220
221             return TypeSemantics.IsSubTypeOf(cspaceType, storeType);
222         }
223     }   
224 }