/ODataLib/OData/Dev10/Microsoft/Data/OData/CollectionWithoutExpectedTypeValidator.cs

http://odata.codeplex.com · C# · 239 lines · 147 code · 25 blank · 67 comment · 37 complexity · f87162a715576acc4fe58a0c1569af33 MD5 · raw file

  1. // Copyright 2011 Microsoft Corporation
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. namespace Microsoft.Data.OData
  15. {
  16. #region Namespaces
  17. using System.Diagnostics;
  18. using Microsoft.Data.Edm;
  19. using Microsoft.Data.Edm.Library;
  20. using Microsoft.Data.OData.Metadata;
  21. #endregion Namespaces
  22. /// <summary>
  23. /// Helper class to verify that all items of a collection are of the same kind and type.
  24. /// </summary>
  25. /// <remarks>This class is only used if no expected item type is specified for the collection;
  26. /// otherwise all items are already validated against the expected item type.</remarks>
  27. internal sealed class CollectionWithoutExpectedTypeValidator
  28. {
  29. /// <summary>true if the item type was derived from the collection value; otherwise false.</summary>
  30. private readonly bool itemTypeDerivedFromCollectionValue;
  31. /// <summary>The item type name extracted from the first non-null item.</summary>
  32. private string itemTypeName;
  33. /// <summary>
  34. /// The primitive type denoted by the item type name or null if the type name is not a valid primitive type name.
  35. /// </summary>
  36. private IEdmPrimitiveType primitiveItemType;
  37. /// <summary>The item type kind from the first non-null item.</summary>
  38. private EdmTypeKind itemTypeKind;
  39. /// <summary>
  40. /// Constructor.
  41. /// </summary>
  42. /// <param name="itemTypeNameFromCollection">The item type name extracted from the collection type name.</param>
  43. internal CollectionWithoutExpectedTypeValidator(string itemTypeNameFromCollection)
  44. {
  45. DebugUtils.CheckNoExternalCallers();
  46. if (itemTypeNameFromCollection != null)
  47. {
  48. this.itemTypeName = itemTypeNameFromCollection;
  49. this.itemTypeKind = ComputeExpectedTypeKind(this.itemTypeName, out this.primitiveItemType);
  50. this.itemTypeDerivedFromCollectionValue = true;
  51. }
  52. }
  53. /// <summary>
  54. /// If specified on a collection, returns the item type name that all items are expected to be compatible with; otherwise null.
  55. /// </summary>
  56. internal string ItemTypeNameFromCollection
  57. {
  58. get
  59. {
  60. DebugUtils.CheckNoExternalCallers();
  61. return this.itemTypeDerivedFromCollectionValue ? this.itemTypeName : null;
  62. }
  63. }
  64. /// <summary>
  65. /// If specified on a collection, returns the item type kind that all items are expected to be compatible with; otherwise EdmTypeKind.None.
  66. /// </summary>
  67. internal EdmTypeKind ItemTypeKindFromCollection
  68. {
  69. get
  70. {
  71. DebugUtils.CheckNoExternalCallers();
  72. return this.itemTypeDerivedFromCollectionValue ? this.itemTypeKind : EdmTypeKind.None;
  73. }
  74. }
  75. /// <summary>
  76. /// Validates a collection item that was read to make sure it is valid (i.e., has the correct
  77. /// type name and type kind) with respect to the other items in the collection.
  78. /// </summary>
  79. /// <param name="collectionItemTypeName">The type name of the item from the payload.</param>
  80. /// <param name="collectionItemTypeKind">The type kind of the item from the payload.</param>
  81. internal void ValidateCollectionItem(string collectionItemTypeName, EdmTypeKind collectionItemTypeKind)
  82. {
  83. DebugUtils.CheckNoExternalCallers();
  84. // Only primitive and complex values are allowed in collections
  85. if (collectionItemTypeKind != EdmTypeKind.Primitive && collectionItemTypeKind != EdmTypeKind.Complex)
  86. {
  87. throw new ODataException(Strings.CollectionWithoutExpectedTypeValidator_InvalidItemTypeKind(collectionItemTypeKind));
  88. }
  89. if (this.itemTypeDerivedFromCollectionValue)
  90. {
  91. Debug.Assert(this.itemTypeName != null, "this.itemType != null");
  92. // If the collection has a type name assign missing item type names from it.
  93. collectionItemTypeName = collectionItemTypeName ?? this.itemTypeName;
  94. // If we have a type name from the collection, make sure the type names of all items match
  95. this.ValidateCollectionItemTypeNameAndKind(collectionItemTypeName, collectionItemTypeKind);
  96. }
  97. else
  98. {
  99. // If we don't have a type name from the collection, store the type name and type kind of the first non-null item.
  100. if (this.itemTypeKind == EdmTypeKind.None)
  101. {
  102. // Compute the kind from the specified type name if available.
  103. this.itemTypeKind = collectionItemTypeName == null
  104. ? collectionItemTypeKind
  105. : ComputeExpectedTypeKind(collectionItemTypeName, out this.primitiveItemType);
  106. // If no payload type name is specified either default to Edm.String (for primitive type kinds) or leave the type name
  107. // null (for complex items without type name)
  108. if (collectionItemTypeName == null)
  109. {
  110. this.itemTypeKind = collectionItemTypeKind;
  111. if (this.itemTypeKind == EdmTypeKind.Primitive)
  112. {
  113. this.itemTypeName = Metadata.EdmConstants.EdmStringTypeName;
  114. this.primitiveItemType = EdmCoreModel.Instance.GetString(/*isNullable*/ false).PrimitiveDefinition();
  115. }
  116. else
  117. {
  118. this.itemTypeName = null;
  119. this.primitiveItemType = null;
  120. }
  121. }
  122. else
  123. {
  124. this.itemTypeKind = ComputeExpectedTypeKind(collectionItemTypeName, out this.primitiveItemType);
  125. this.itemTypeName = collectionItemTypeName;
  126. }
  127. }
  128. if (collectionItemTypeName == null && collectionItemTypeKind == EdmTypeKind.Primitive)
  129. {
  130. // Default to Edm.String if no payload type is specified and the type kind is 'Primitive'
  131. collectionItemTypeName = Metadata.EdmConstants.EdmStringTypeName;
  132. }
  133. // Validate the expected and actual type names and type kinds.
  134. // Note that we compute the expected type kind from the expected type name and thus the payload
  135. // type kind (passed to this method) might be different from the computed expected type kind.
  136. this.ValidateCollectionItemTypeNameAndKind(collectionItemTypeName, collectionItemTypeKind);
  137. }
  138. }
  139. /// <summary>
  140. /// Computes the expected type kind of an item from the type name read from the payload.
  141. /// </summary>
  142. /// <param name="typeName">The type name to compute the type kind from.</param>
  143. /// <param name="primitiveItemType">The primitive type for the specified type name or null if the type name is not a valid primitve type.</param>
  144. /// <returns>The <see cref="EdmTypeKind"/> of the type with the specified <paramref name="typeName"/>.</returns>
  145. private static EdmTypeKind ComputeExpectedTypeKind(string typeName, out IEdmPrimitiveType primitiveItemType)
  146. {
  147. IEdmSchemaType knownType = EdmCoreModel.Instance.FindDeclaredType(typeName);
  148. if (knownType != null)
  149. {
  150. Debug.Assert(knownType.TypeKind == EdmTypeKind.Primitive, "Only primitive types should be resolved by the core model.");
  151. primitiveItemType = (IEdmPrimitiveType)knownType;
  152. return EdmTypeKind.Primitive;
  153. }
  154. primitiveItemType = null;
  155. return EdmTypeKind.Complex;
  156. }
  157. /// <summary>
  158. /// Validate that the expected and actual type names and type kinds are compatible.
  159. /// </summary>
  160. /// <param name="collectionItemTypeName">The actual type name.</param>
  161. /// <param name="collectionItemTypeKind">The actual type kind.</param>
  162. private void ValidateCollectionItemTypeNameAndKind(string collectionItemTypeName, EdmTypeKind collectionItemTypeKind)
  163. {
  164. // Compare the item type kinds.
  165. if (this.itemTypeKind != collectionItemTypeKind)
  166. {
  167. throw new ODataException(Strings.CollectionWithoutExpectedTypeValidator_IncompatibleItemTypeKind(collectionItemTypeKind, this.itemTypeKind));
  168. }
  169. if (this.itemTypeKind == EdmTypeKind.Primitive)
  170. {
  171. Debug.Assert(this.primitiveItemType != null, "this.primitiveItemType != null");
  172. Debug.Assert(collectionItemTypeName != null, "collectionItemTypeName != null");
  173. // NOTE: we do support type inheritance for spatial primitive types; otherwise the type names have to match.
  174. if (string.CompareOrdinal(this.itemTypeName, collectionItemTypeName) == 0)
  175. {
  176. return;
  177. }
  178. if (this.primitiveItemType.IsSpatial())
  179. {
  180. EdmPrimitiveTypeKind collectionItemPrimitiveKind = EdmCoreModel.Instance.GetPrimitiveTypeKind(collectionItemTypeName);
  181. IEdmPrimitiveType collectionItemPrimitiveType = EdmCoreModel.Instance.GetPrimitiveType(collectionItemPrimitiveKind);
  182. if (this.itemTypeDerivedFromCollectionValue)
  183. {
  184. // If the collection defines an item type, the collection item type has to be assignable to it.
  185. if (this.primitiveItemType.IsAssignableFrom(collectionItemPrimitiveType))
  186. {
  187. return;
  188. }
  189. }
  190. else
  191. {
  192. // If the collection does not define an item type, the collection items must have a common base type.
  193. IEdmPrimitiveType commonBaseType = EdmLibraryExtensions.GetCommonBaseType(this.primitiveItemType, collectionItemPrimitiveType);
  194. if (commonBaseType != null)
  195. {
  196. this.primitiveItemType = commonBaseType;
  197. this.itemTypeName = commonBaseType.ODataFullName();
  198. return;
  199. }
  200. }
  201. }
  202. throw new ODataException(Strings.CollectionWithoutExpectedTypeValidator_IncompatibleItemTypeName(collectionItemTypeName, this.itemTypeName));
  203. }
  204. else
  205. {
  206. // Since we do not support type inheritance for complex types, comparison of the type names is sufficient
  207. if (string.CompareOrdinal(this.itemTypeName, collectionItemTypeName) != 0)
  208. {
  209. throw new ODataException(Strings.CollectionWithoutExpectedTypeValidator_IncompatibleItemTypeName(collectionItemTypeName, this.itemTypeName));
  210. }
  211. }
  212. }
  213. }
  214. }