Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / EntityModel / SchemaObjectModel / ReturnType.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ReturnType.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       [....]
7 // @backupOwner [....]
8 //---------------------------------------------------------------------
9
10 namespace System.Data.EntityModel.SchemaObjectModel
11 {
12     using System.Collections.Generic;
13     using System.Data.Entity;
14     using System.Data.Metadata.Edm;
15     using System.Diagnostics;
16     using System.Globalization;
17     using System.Linq;
18     using System.Text;
19     using System.Xml;
20     using Som = System.Data.EntityModel.SchemaObjectModel;
21
22     class ReturnType : ModelFunctionTypeElement
23     {
24         private CollectionKind _collectionKind = CollectionKind.None;
25         private bool _isRefType;
26         private string _unresolvedEntitySet = null;
27         private bool _entitySetPathDefined = false;
28         private ModelFunctionTypeElement _typeSubElement = null;
29         private EntityContainerEntitySet _entitySet = null;
30
31         #region constructor
32         /// <summary>
33         /// 
34         /// </summary>
35         /// <param name="parentElement"></param>
36         internal ReturnType(Function parentElement)
37             : base(parentElement)
38         {
39             _typeUsageBuilder = new TypeUsageBuilder(this);
40         }
41         #endregion
42
43         #region Properties
44
45         internal bool IsRefType
46         {
47             get { return _isRefType; }
48         }
49
50         internal CollectionKind CollectionKind
51         {
52             get { return _collectionKind; }
53         }
54
55         internal EntityContainerEntitySet EntitySet
56         {
57             get { return _entitySet; }
58         }
59
60         internal bool EntitySetPathDefined
61         {
62             get { return _entitySetPathDefined; }
63         }
64
65         internal ModelFunctionTypeElement SubElement
66         {
67             get { return _typeSubElement; }
68         }
69
70         internal override TypeUsage TypeUsage
71         {
72             get
73             {
74                 if (_typeSubElement != null)
75                 {
76                     return _typeSubElement.GetTypeUsage();
77                 }
78                 else if (_typeUsage != null)
79                 {
80                     return _typeUsage;
81                 }
82                 else if (base.TypeUsage == null)
83                 {
84                     return null;
85                 }
86                 else if (_collectionKind != CollectionKind.None)
87                 {
88                     return TypeUsage.Create(new CollectionType(base.TypeUsage));
89                 }
90                 else
91                 {
92                     return base.TypeUsage;
93                 }
94             }
95         }
96
97         #endregion
98
99         internal override SchemaElement Clone(SchemaElement parentElement)
100         {
101             ReturnType parameter = new ReturnType((Function)parentElement);
102             parameter._type = _type;
103             parameter.Name = this.Name;
104             parameter._typeUsageBuilder = this._typeUsageBuilder;
105             parameter._unresolvedType = this._unresolvedType;
106             parameter._unresolvedEntitySet = this._unresolvedEntitySet;
107             parameter._entitySetPathDefined = this._entitySetPathDefined;
108             parameter._entitySet = this._entitySet;
109             return parameter;
110         }
111
112         protected override bool HandleAttribute(XmlReader reader)
113         {
114             if (base.HandleAttribute(reader))
115             {
116                 return true;
117             }
118             else if (CanHandleAttribute(reader, XmlConstants.TypeElement))
119             {
120                 HandleTypeAttribute(reader);
121                 return true;
122             }
123             else if (CanHandleAttribute(reader, XmlConstants.EntitySet))
124             {
125                 HandleEntitySetAttribute(reader);
126                 return true;
127             }
128             else if (CanHandleAttribute(reader, XmlConstants.EntitySetPath))
129             {
130                 HandleEntitySetPathAttribute(reader);
131                 return true;
132             }
133             else if (_typeUsageBuilder.HandleAttribute(reader))
134             {
135                 return true;
136             }
137
138             return false;
139         }
140
141         internal bool ResolveNestedTypeNames(Converter.ConversionCache convertedItemCache, Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
142         {
143             Debug.Assert(_typeSubElement != null, "Nested type expected.");
144             return _typeSubElement.ResolveNameAndSetTypeUsage(convertedItemCache, newGlobalItems);
145         }
146
147         #region Private Methods
148
149         /// <summary>
150         /// 
151         /// </summary>
152         /// <param name="reader"></param>
153         private void HandleTypeAttribute(XmlReader reader)
154         {
155             Debug.Assert(reader != null);
156             Debug.Assert(UnresolvedType == null);
157
158             string type;
159             if (!Utils.GetString(Schema, reader, out type))
160                 return;
161             TypeModifier typeModifier;
162             
163             Function.RemoveTypeModifier(ref type, out typeModifier, out _isRefType);
164
165             switch (typeModifier)
166             {
167                 case TypeModifier.Array:
168                     _collectionKind = CollectionKind.Bag;
169                     break;
170                 default:
171                     Debug.Assert(typeModifier == TypeModifier.None, string.Format(CultureInfo.CurrentCulture, "Type is not valid for property {0}: {1}. The modifier for the type cannot be used in this context.", FQName, reader.Value));
172                     break;
173             }
174
175             if (!Utils.ValidateDottedName(Schema, reader, type))
176                 return;
177
178             UnresolvedType = type;
179         }
180
181         private void HandleEntitySetAttribute(XmlReader reader)
182         {
183             Debug.Assert(reader != null);
184             string entitySetName;
185             if (Utils.GetString(Schema, reader, out entitySetName))
186             {
187                 _unresolvedEntitySet = entitySetName;
188             }
189         }
190
191         private void HandleEntitySetPathAttribute(XmlReader reader)
192         {
193             Debug.Assert(reader != null);
194             string entitySetPath;
195             if (Utils.GetString(Schema, reader, out entitySetPath))
196             {
197                 // EF does not support this EDM 3.0 attribute, we only use it for validation.
198                 _entitySetPathDefined = true;
199             }
200         }
201
202         protected override bool HandleElement(XmlReader reader)
203         {
204             if (base.HandleElement(reader))
205             {
206                 return true;
207             }
208             else if (CanHandleElement(reader, XmlConstants.CollectionType))
209             {
210                 HandleCollectionTypeElement(reader);
211                 return true;
212             }
213             else if (CanHandleElement(reader, XmlConstants.ReferenceType))
214             {
215                 HandleReferenceTypeElement(reader);
216                 return true;
217             }
218             else if (CanHandleElement(reader, XmlConstants.TypeRef))
219             {
220                 HandleTypeRefElement(reader);
221                 return true;
222             }
223             else if (CanHandleElement(reader, XmlConstants.RowType))
224             {
225                 HandleRowTypeElement(reader);
226                 return true;
227             }
228
229             return false;
230         }
231
232         protected void HandleCollectionTypeElement(XmlReader reader)
233         {
234             Debug.Assert(reader != null);
235
236             var subElement = new CollectionTypeElement(this);
237             subElement.Parse(reader);
238             _typeSubElement = subElement;
239         }
240
241         protected void HandleReferenceTypeElement(XmlReader reader)
242         {
243             Debug.Assert(reader != null);
244
245             var subElement = new ReferenceTypeElement(this);
246             subElement.Parse(reader);
247             _typeSubElement = subElement;
248         }
249
250         protected void HandleTypeRefElement(XmlReader reader)
251         {
252             Debug.Assert(reader != null);
253
254             var subElement = new TypeRefElement(this);
255             subElement.Parse(reader);
256             _typeSubElement = subElement;
257         }
258
259         protected void HandleRowTypeElement(XmlReader reader)
260         {
261             Debug.Assert(reader != null);
262
263             var subElement = new RowTypeElement(this);
264             subElement.Parse(reader);
265             _typeSubElement = subElement;
266         }
267
268         #endregion
269
270         internal override void ResolveTopLevelNames()
271         {
272             // If type was defined as an attribute: <ReturnType Type="int"/>
273             if (_unresolvedType != null)
274             {
275                 base.ResolveTopLevelNames();
276             }
277
278             // If type was defined as a subelement: <ReturnType><CollectionType>...</CollectionType></ReturnType>
279             if (_typeSubElement != null)
280             {
281                 Debug.Assert(!this.ParentElement.IsFunctionImport, "FunctionImports can't have sub elements in their return types, so we should NEVER see them here");
282                 _typeSubElement.ResolveTopLevelNames();
283             }
284
285             if (this.ParentElement.IsFunctionImport && _unresolvedEntitySet != null)
286             {
287                 ((FunctionImportElement)this.ParentElement).ResolveEntitySet(this, _unresolvedEntitySet, ref _entitySet);
288             }
289         }
290
291         internal override void Validate()
292         {
293             base.Validate();
294
295             ValidationHelper.ValidateTypeDeclaration(this, _type, _typeSubElement);
296             ValidationHelper.ValidateFacets(this, _type, _typeUsageBuilder);
297             if (_isRefType)
298             {
299                 ValidationHelper.ValidateRefType(this, _type);
300             }
301
302             if (Schema.DataModel != SchemaDataModelOption.EntityDataModel)
303             {
304                 Debug.Assert(Schema.DataModel == SchemaDataModelOption.ProviderDataModel ||
305                              Schema.DataModel == SchemaDataModelOption.ProviderManifestModel, "Unexpected data model");
306
307                 if (Schema.DataModel == SchemaDataModelOption.ProviderManifestModel)
308                 {
309                     // Only scalar return type is allowed for functions in provider manifest.
310                     if (_type != null && (_type is ScalarType == false || _collectionKind != CollectionKind.None) ||
311                         _typeSubElement != null && _typeSubElement.Type is ScalarType == false)
312                     {
313                         string typeName = "";
314                         if (_type != null)
315                         {
316                             typeName = Function.GetTypeNameForErrorMessage(_type, _collectionKind, _isRefType);
317                         }
318                         else if (_typeSubElement != null)
319                         {
320                             typeName = _typeSubElement.FQName;
321                         }
322                         AddError(ErrorCode.FunctionWithNonEdmTypeNotSupported,
323                                  EdmSchemaErrorSeverity.Error,
324                                  this,
325                                  System.Data.Entity.Strings.FunctionWithNonEdmPrimitiveTypeNotSupported(typeName, this.ParentElement.FQName));
326                     }
327                 }
328                 else // SchemaDataModelOption.ProviderDataModel
329                 {
330                     Debug.Assert(Schema.DataModel == SchemaDataModelOption.ProviderDataModel, "Unexpected data model");
331
332                     // In SSDL, function may only return a primitive type or a collection of rows.
333                     if (_type != null)
334                     {
335                         // It is not possible to define a collection of rows via a type attribute, hence any collection is not allowed.
336                         if (_type is ScalarType == false || _collectionKind != CollectionKind.None)
337                         {
338                             AddError(ErrorCode.FunctionWithNonPrimitiveTypeNotSupported,
339                                      EdmSchemaErrorSeverity.Error,
340                                      this,
341                                      System.Data.Entity.Strings.FunctionWithNonPrimitiveTypeNotSupported(_isRefType ? _unresolvedType : _type.FQName, this.ParentElement.FQName));
342                         }
343                     }
344                     else if (_typeSubElement != null)
345                     {
346                         if (_typeSubElement.Type is ScalarType == false)
347                         {
348                             if (Schema.SchemaVersion < XmlConstants.StoreVersionForV3)
349                             {
350                                 // Before V3 provider model functions only supported scalar return types.
351                                 AddError(ErrorCode.FunctionWithNonPrimitiveTypeNotSupported,
352                                          EdmSchemaErrorSeverity.Error,
353                                          this,
354                                          System.Data.Entity.Strings.FunctionWithNonPrimitiveTypeNotSupported(_typeSubElement.FQName, this.ParentElement.FQName));
355                             }
356                             else
357                             {
358                                 // Starting from V3, TVFs must return collection of rows and row props can be only primitive types.
359                                 // The "collection of rows" is the only option in SSDL function ReturnType subelement thus it's enforced on the XSD level,
360                                 // so we can assume it here. The only thing we need to check is the type of the row properties.
361                                 var collection = _typeSubElement as CollectionTypeElement;
362                                 Debug.Assert(collection != null, "Can't find <CollectionType> inside TVF <ReturnType> element");
363                                 if (collection != null)
364                                 {
365                                     var row = collection.SubElement as RowTypeElement;
366                                     Debug.Assert(row != null, "Can't find <RowType> inside TVF <ReturnType><CollectionType> element");
367                                     if (row != null)
368                                     {
369                                         if (row.Properties.Any(p => !p.ValidateIsScalar()))
370                                         {
371                                             AddError(ErrorCode.TVFReturnTypeRowHasNonScalarProperty,
372                                                      EdmSchemaErrorSeverity.Error,
373                                                      this,
374                                                      System.Data.Entity.Strings.TVFReturnTypeRowHasNonScalarProperty);
375                                         }
376                                     }
377                                 }
378                             }
379
380                         }
381                         // else type is ScalarType which is supported in all version
382                     }
383                 }
384             }
385
386             if (_typeSubElement != null)
387             {
388                 _typeSubElement.Validate();
389             }
390         }
391
392         internal override void WriteIdentity(StringBuilder builder) { }
393
394         internal override TypeUsage GetTypeUsage()
395         {
396             return TypeUsage;
397         }
398
399         internal override bool ResolveNameAndSetTypeUsage(Converter.ConversionCache convertedItemCache, Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
400         {
401             Debug.Fail("This method was not called from anywhere in the code before. If you got here you need to update this method and possibly ResolveNestedTypeNames()"); 
402
403             return false;
404         }
405     }
406 }