1 //---------------------------------------------------------------------
2 // <copyright file="TypeSemantics.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 using System.Collections.Generic;
11 using System.Data.Common;
13 using System.Diagnostics;
14 using objectModel = System.Collections.ObjectModel;
16 namespace System.Data.Metadata.Edm
19 /// Provides type semantics service, type operations and type predicates for the EDM type system.
22 /// For detailed functional specification, see "The EDP Type System.docx" and "edm.spec.doc".
24 /// 1) The notion of 'type' for the sake of type operation semantics is based on TypeUsage, i.e., EdmType *plus* facets.
26 /// 2) EDM built-in primitive types are defined by the EDM Provider Manifest.
28 /// 3) SubType and Promotable are similar notions however subtyping is stricter than promotability. Subtyping is used for mapping
29 /// validation while Promotability is used in query, update expression static type validation.
31 internal static class TypeSemantics
35 // cache commom super type closure
37 static private objectModel.ReadOnlyCollection<PrimitiveType>[,] _commonTypeClosure;
43 #region 'Public' Interface
46 /// Determines whether two types are exactly equal.
47 /// For row types, this INCLUDES property names as well as property types.
49 /// <param name="type1">The first type to compare.</param>
50 /// <param name="type2">The second type to compare.</param>
51 /// <returns>If the two types are structurally equal, <c>true</c>; otherwise <c>false</c>.</returns>
52 internal static bool IsEqual(TypeUsage type1, TypeUsage type2)
54 return CompareTypes(type1, type2, false /*equivalenceOnly*/);
58 /// Determines if the two types are structurally equivalent.
60 /// <param name="fromType"></param>
61 /// <param name="toType"></param>
63 /// Equivalence for nomimal types is based on lexical identity and structural equivalence for structural types.
64 /// Structural equivalence for row types is based only on equivalence of property types, property names are ignored.
66 /// <returns>true if equivalent, false otherwise</returns>
67 internal static bool IsStructurallyEqual(TypeUsage fromType, TypeUsage toType)
69 return CompareTypes(fromType, toType, true /*equivalenceOnly*/);
73 /// determines if two types are equivalent or if fromType is promotable to toType
75 /// <param name="fromType"></param>
76 /// <param name="toType"></param>
77 /// <returns>true if fromType equivalent or promotable to toType, false otherwise</returns>
78 internal static bool IsStructurallyEqualOrPromotableTo(TypeUsage fromType, TypeUsage toType)
80 return IsStructurallyEqual(fromType, toType) ||
81 IsPromotableTo(fromType, toType);
85 /// determines if two types are equivalent or if fromType is promotable to toType
87 /// <param name="fromType"></param>
88 /// <param name="toType"></param>
89 /// <returns>true if fromType equivalent or promotable to toType, false otherwise</returns>
90 internal static bool IsStructurallyEqualOrPromotableTo(EdmType fromType, EdmType toType)
92 return IsStructurallyEqualOrPromotableTo(TypeUsage.Create(fromType), TypeUsage.Create(toType));
96 /// determines if subType is equal to or a sub-type of superType.
98 /// <param name="subType"></param>
99 /// <param name="superType"></param>
100 /// <returns>true if subType is equal to or a sub-type of superType, false otherwise</returns>
101 internal static bool IsSubTypeOf(TypeUsage subType, TypeUsage superType)
103 Debug.Assert(subType != null, "subType must not be NULL");
104 Debug.Assert(superType != null, "superType must not be NULL");
106 if (subType.EdmEquals(superType))
111 if (Helper.IsPrimitiveType(subType.EdmType) && Helper.IsPrimitiveType(superType.EdmType))
113 return IsPrimitiveTypeSubTypeOf(subType, superType);
116 return subType.IsSubtypeOf(superType);
120 /// determines if subType EdmType is a sub-type of superType EdmType.
122 /// <param name="subEdmType"></param>
123 /// <param name="superEdmType"></param>
124 /// <returns>true if subType is a sub-type of superType, false otherwise</returns>
125 internal static bool IsSubTypeOf(EdmType subEdmType, EdmType superEdmType)
127 return subEdmType.IsSubtypeOf(superEdmType);
131 /// Determines if fromType is promotable to toType.
133 /// <param name="fromType"></param>
134 /// <param name="toType"></param>
135 /// <returns>true if fromType is promotable to toType, false otherwise</returns>
136 internal static bool IsPromotableTo(TypeUsage fromType, TypeUsage toType)
138 Debug.Assert(fromType != null, "fromType must not be NULL");
139 Debug.Assert(toType != null, "toType must not be NULL");
141 if (toType.EdmType.EdmEquals(fromType.EdmType))
146 if (Helper.IsPrimitiveType(fromType.EdmType) && Helper.IsPrimitiveType(toType.EdmType))
148 return IsPrimitiveTypePromotableTo(fromType,
151 else if (Helper.IsCollectionType(fromType.EdmType) && Helper.IsCollectionType(toType.EdmType))
153 return IsPromotableTo(TypeHelpers.GetElementTypeUsage(fromType),
154 TypeHelpers.GetElementTypeUsage(toType));
156 else if (Helper.IsEntityTypeBase(fromType.EdmType) && Helper.IsEntityTypeBase(toType.EdmType))
158 return fromType.EdmType.IsSubtypeOf(toType.EdmType);
160 else if (Helper.IsRefType(fromType.EdmType) && Helper.IsRefType(toType.EdmType))
162 return IsPromotableTo(TypeHelpers.GetElementTypeUsage(fromType),
163 TypeHelpers.GetElementTypeUsage(toType));
165 else if (Helper.IsRowType(fromType.EdmType) && Helper.IsRowType(toType.EdmType))
167 return IsPromotableTo((RowType)fromType.EdmType,
168 (RowType)toType.EdmType);
175 /// Flattens composite transient type down to nominal type leafs.
177 internal static IEnumerable<TypeUsage> FlattenType(TypeUsage type)
179 Func<TypeUsage, bool> isLeaf = t => !Helper.IsTransientType(t.EdmType);
181 Func<TypeUsage, IEnumerable<TypeUsage>> getImmediateSubNodes =
184 if (Helper.IsCollectionType(t.EdmType) || Helper.IsRefType(t.EdmType))
186 return new[] { TypeHelpers.GetElementTypeUsage(t) };
188 else if (Helper.IsRowType(t.EdmType))
190 return ((RowType)t.EdmType).Properties.Select(p => p.TypeUsage);
194 Debug.Fail("cannot enumerate subnodes of a leaf node");
195 return new TypeUsage[] { };
199 return Common.Utils.Helpers.GetLeafNodes<TypeUsage>(type, isLeaf, getImmediateSubNodes);
203 /// determines if fromType can be casted to toType.
205 /// <param name="fromType">Type to cast from.</param>
206 /// <param name="toType">Type to cast to.</param>
207 /// <returns><c>true</c> if <paramref name="fromType"/> can be casted to <paramref name="toType" />; <c>false</c> otherwise.</returns>
210 /// - primitive types can be casted to other primitive types
211 /// - primitive types can be casted to enum types
212 /// - enum types can be casted to primitive types
213 /// - enum types cannot be casted to other enum types except for casting to the same type
215 internal static bool IsCastAllowed(TypeUsage fromType, TypeUsage toType)
217 Debug.Assert(fromType != null, "fromType != null");
218 Debug.Assert(toType != null, "toType != null");
221 (Helper.IsPrimitiveType(fromType.EdmType) && Helper.IsPrimitiveType(toType.EdmType)) ||
222 (Helper.IsPrimitiveType(fromType.EdmType) && Helper.IsEnumType(toType.EdmType)) ||
223 (Helper.IsEnumType(fromType.EdmType) && Helper.IsPrimitiveType(toType.EdmType)) ||
224 (Helper.IsEnumType(fromType.EdmType) && Helper.IsEnumType(toType.EdmType) && fromType.EdmType.Equals(toType.EdmType));
228 /// Determines if a common super type (LUB) exists between type1 and type2.
230 /// <param name="type1"></param>
231 /// <param name="type2"></param>
232 /// <param name="commonType"></param>
234 /// true if a common super type between type1 and type2 exists and out commonType represents the common super type.
235 /// false otherwise along with commonType as null
237 internal static bool TryGetCommonType(TypeUsage type1, TypeUsage type2, out TypeUsage commonType)
239 Debug.Assert(type1 != null, "type1 must not be NULL");
240 Debug.Assert(type2 != null, "type2 must not be NULL");
244 if (type1.EdmEquals(type2))
246 commonType = ForgetConstraints(type2);
250 if (Helper.IsPrimitiveType(type1.EdmType) && Helper.IsPrimitiveType(type2.EdmType))
252 return TryGetCommonPrimitiveType(type1, type2, out commonType);
255 EdmType commonEdmType;
256 if (TryGetCommonType(type1.EdmType, type2.EdmType, out commonEdmType))
258 commonType = ForgetConstraints(TypeUsage.Create(commonEdmType));
267 /// Gets a Common super-type of type1 and type2 if one exists. null otherwise.
269 /// <param name="type1"></param>
270 /// <param name="type2"></param>
271 /// <returns></returns>
272 internal static TypeUsage GetCommonType(TypeUsage type1, TypeUsage type2)
274 TypeUsage commonType = null;
275 if (TypeSemantics.TryGetCommonType(type1, type2, out commonType))
283 /// determines if an EdmFunction is an aggregate function
285 /// <param name="function"></param>
286 /// <returns></returns>
287 internal static bool IsAggregateFunction(EdmFunction function)
289 return function.AggregateAttribute;
293 /// determines if fromType can be cast to toType. this operation is valid only
294 /// if fromtype and totype are polimorphic types.
296 /// <param name="fromType"></param>
297 /// <param name="toType"></param>
298 /// <returns></returns>
299 internal static bool IsValidPolymorphicCast(TypeUsage fromType, TypeUsage toType)
301 if (!IsPolymorphicType(fromType) || !IsPolymorphicType(toType))
305 return (IsStructurallyEqual(fromType, toType) || IsSubTypeOf(fromType, toType) || IsSubTypeOf(toType, fromType));
309 /// determines if fromEdmType can be cast to toEdmType. this operation is valid only
310 /// if fromtype and totype are polimorphic types.
312 /// <param name="fromType"></param>
313 /// <param name="toType"></param>
314 /// <returns></returns>
315 internal static bool IsValidPolymorphicCast(EdmType fromEdmType, EdmType toEdmType)
317 return IsValidPolymorphicCast(TypeUsage.Create(fromEdmType), TypeUsage.Create(toEdmType));
321 /// Determines if the <param ref="type" /> is a structural nominal type, i.e., EntityType or ComplexType
323 /// <param name="type">Type to be checked.</param>
324 /// <returns><c>true</c> if the <param ref="type" /> is a nominal type. <c>false</c> otherwise.</returns>
325 static internal bool IsNominalType(TypeUsage type)
327 Debug.Assert(!IsEnumerationType(type), "Implicit cast/Softcast is not allowed for enums so we should never see enum type here.");
329 return IsEntityType(type) || IsComplexType(type);
333 /// determines if type is a collection type.
335 /// <param name="type"></param>
336 /// <returns></returns>
337 internal static bool IsCollectionType(TypeUsage type)
339 return Helper.IsCollectionType(type.EdmType);
343 /// determines if type is a complex type.
345 /// <param name="type"></param>
346 /// <returns></returns>
347 internal static bool IsComplexType(TypeUsage type)
349 return (BuiltInTypeKind.ComplexType == type.EdmType.BuiltInTypeKind);
353 /// determines if type is an EntityType
355 /// <param name="type"></param>
356 /// <returns></returns>
357 internal static bool IsEntityType(TypeUsage type)
359 return Helper.IsEntityType(type.EdmType);
363 /// determines if type is a Relationship Type.
365 /// <param name="type"></param>
366 /// <returns></returns>
367 internal static bool IsRelationshipType(TypeUsage type)
369 return (BuiltInTypeKind.AssociationType == type.EdmType.BuiltInTypeKind);
373 /// determines if type is of EnumerationType.
375 /// <param name="type"></param>
376 /// <returns></returns>
377 internal static bool IsEnumerationType(TypeUsage type)
379 Debug.Assert(type != null, "type != null");
381 return Helper.IsEnumType(type.EdmType);
385 /// determines if <paramref name="type"/> is primitive or enumeration type
387 /// <param name="type">Type to verify.</param>
388 /// <returns><c>true</c> if <paramref name="type"/> is primitive or enumeration type. <c>false</c> otherwise.</returns>
389 internal static bool IsScalarType(TypeUsage type)
391 return IsScalarType(type.EdmType);
395 /// determines if <paramref name="type"/> is primitive or enumeration type
397 /// <param name="type">Type to verify.</param>
398 /// <returns><c>true</c> if <paramref name="type"/> is primitive or enumeration type. <c>false</c> otherwise.</returns>
399 internal static bool IsScalarType(EdmType type)
401 Debug.Assert(type != null, "type != null");
403 return Helper.IsPrimitiveType(type) || Helper.IsEnumType(type);
407 /// Determines if type is a numeric type, i.e., is one of:
408 /// Byte, Int16, Int32, Int64, Decimal, Single or Double
410 /// <param name="type"></param>
411 /// <returns></returns>
412 internal static bool IsNumericType(TypeUsage type)
414 return (IsIntegerNumericType(type) || IsFixedPointNumericType(type) || IsFloatPointNumericType(type));
418 /// Determines if type is an integer numeric type, i.e., is one of: Byte, Int16, Int32, Int64
420 /// <param name="type"></param>
421 /// <returns></returns>
422 internal static bool IsIntegerNumericType(TypeUsage type)
424 PrimitiveTypeKind typeKind;
425 if (TypeHelpers.TryGetPrimitiveTypeKind(type, out typeKind))
429 case PrimitiveTypeKind.Byte:
430 case PrimitiveTypeKind.Int16:
431 case PrimitiveTypeKind.Int32:
432 case PrimitiveTypeKind.Int64:
433 case PrimitiveTypeKind.SByte:
444 /// Determines if type is an fixed point numeric type, i.e., is one of: Decimal
446 /// <param name="type"></param>
447 /// <returns></returns>
448 internal static bool IsFixedPointNumericType(TypeUsage type)
450 PrimitiveTypeKind typeKind;
451 if (TypeHelpers.TryGetPrimitiveTypeKind(type, out typeKind))
453 return (typeKind == PrimitiveTypeKind.Decimal);
460 /// Determines if type is an float point numeric type, i.e., is one of: Single or Double.
462 /// <param name="type"></param>
463 /// <returns></returns>
464 internal static bool IsFloatPointNumericType(TypeUsage type)
466 PrimitiveTypeKind typeKind;
467 if (TypeHelpers.TryGetPrimitiveTypeKind(type, out typeKind))
469 return (typeKind == PrimitiveTypeKind.Double || typeKind == PrimitiveTypeKind.Single);
475 /// Determines if type is an unsigned integer numeric type, i.e., is Byte
477 /// <param name="type"></param>
478 /// <returns></returns>
479 internal static bool IsUnsignedNumericType(TypeUsage type)
481 PrimitiveTypeKind typeKind;
482 if (TypeHelpers.TryGetPrimitiveTypeKind(type, out typeKind))
486 case PrimitiveTypeKind.Byte:
497 /// determines if type is a polimorphic type, ie, EntityType or ComplexType.
499 /// <param name="type"></param>
500 /// <returns></returns>
501 internal static bool IsPolymorphicType(TypeUsage type)
503 return (IsEntityType(type) || IsComplexType(type));
507 /// determines if type is of Boolean Kind
509 /// <param name="type"></param>
510 /// <returns></returns>
511 internal static bool IsBooleanType(TypeUsage type)
513 return TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.Boolean);
517 /// determines if type is a primitive/scalar type.
519 /// <param name="type"></param>
520 /// <returns></returns>
521 internal static bool IsPrimitiveType(TypeUsage type)
523 return Helper.IsPrimitiveType(type.EdmType);
527 /// determines if type is a primitive type of given primitiveTypeKind
529 /// <param name="type"></param>
530 /// <param name="primitiveType"></param>
531 /// <returns></returns>
532 internal static bool IsPrimitiveType(TypeUsage type, PrimitiveTypeKind primitiveTypeKind)
534 PrimitiveTypeKind typeKind;
535 if (TypeHelpers.TryGetPrimitiveTypeKind(type, out typeKind))
537 return (typeKind == primitiveTypeKind);
543 /// determines if type is a RowType
545 /// <param name="type"></param>
546 /// <returns></returns>
547 internal static bool IsRowType(TypeUsage type)
549 return Helper.IsRowType(type.EdmType);
553 /// determines if type is a ReferenceType
555 /// <param name="type"></param>
556 /// <returns></returns>
557 internal static bool IsReferenceType(TypeUsage type)
559 return Helper.IsRefType(type.EdmType);
563 /// determines if type is a spatial type
565 /// <param name="type"></param>
566 /// <returns></returns>
567 internal static bool IsSpatialType(TypeUsage type)
569 return Helper.IsSpatialType(type);
573 /// determines if type is a strong spatial type (i.e., a spatial type, but not one of the two spatial union types)
575 /// <param name="type"></param>
576 /// <returns></returns>
577 internal static bool IsStrongSpatialType(TypeUsage type)
579 return IsPrimitiveType(type) && Helper.IsStrongSpatialTypeKind(((PrimitiveType)type.EdmType).PrimitiveTypeKind);
583 /// determines if type is a structural type, ie, EntityType, ComplexType, RowType or ReferenceType.
585 /// <param name="type"></param>
586 /// <returns></returns>
587 internal static bool IsStructuralType(TypeUsage type)
589 return Helper.IsStructuralType(type.EdmType);
593 /// determines if edmMember is part of the key of it's defining type.
595 /// <param name="member"></param>
596 /// <returns></returns>
597 internal static bool IsPartOfKey(EdmMember edmMember)
599 if (Helper.IsRelationshipEndMember(edmMember))
601 return ((RelationshipType)edmMember.DeclaringType).KeyMembers.Contains(edmMember);
604 if (!Helper.IsEdmProperty(edmMember))
609 if (Helper.IsEntityTypeBase(edmMember.DeclaringType))
611 return ((EntityTypeBase)edmMember.DeclaringType).KeyMembers.Contains(edmMember);
618 /// determines if type is Nullable.
620 /// <param name="type"></param>
621 /// <returns></returns>
622 internal static bool IsNullable(TypeUsage type)
625 if (type.Facets.TryGetValue(EdmProviderManifest.NullableFacetName, false, out nullableFacet))
627 return (bool)nullableFacet.Value;
633 /// determines if edmMember is Nullable.
635 /// <param name="edmMember"></param>
636 /// <returns></returns>
637 internal static bool IsNullable(EdmMember edmMember)
639 return IsNullable(edmMember.TypeUsage);
643 /// determines if given type is equal-comparable.
645 /// <param name="type"></param>
646 /// <returns>true if equal-comparable, false otherwise</returns>
647 internal static bool IsEqualComparable(TypeUsage type)
649 return IsEqualComparable(type.EdmType);
653 /// Determines if type1 is equal-comparable to type2.
654 /// in order for type1 and type2 to be equal-comparable, they must be
655 /// individualy equal-comparable and have a common super-type.
657 /// <param name="type1">an instance of a TypeUsage</param>
658 /// <param name="type2">an instance of a TypeUsage</param>
659 /// <returns><c>true</c> if type1 and type2 are equal-comparable, <c>false</c> otherwise</returns>
660 internal static bool IsEqualComparableTo(TypeUsage type1, TypeUsage type2)
662 if (IsEqualComparable(type1) && IsEqualComparable(type2))
664 return HasCommonType(type1, type2);
670 /// Determines if given type is order-comparable
672 /// <param name="typeUsage"></param>
673 /// <returns></returns>
674 internal static bool IsOrderComparable(TypeUsage type)
676 Debug.Assert(null != type, "type must not be null");
677 return IsOrderComparable(type.EdmType);
680 /// Determines if type1 is order-comparable to type2.
681 /// in order for type1 and type2 to be order-comparable, they must be
682 /// individualy order-comparable and have a common super-type.
684 /// <param name="type1">an instance of a TypeUsage</param>
685 /// <param name="type2">an instance of a TypeUsage</param>
686 /// <returns><c>true</c> if type1 and type2 are order-comparable, <c>false</c> otherwise</returns>
687 internal static bool IsOrderComparableTo(TypeUsage type1, TypeUsage type2)
689 if (IsOrderComparable(type1) && IsOrderComparable(type2))
691 return HasCommonType(type1, type2);
697 /// Removes facets that are not type constraints.
699 /// <param name="type"></param>
700 /// <returns></returns>
701 internal static TypeUsage ForgetConstraints(TypeUsage type)
703 if (Helper.IsPrimitiveType(type.EdmType))
705 return EdmProviderManifest.Instance.ForgetScalarConstraints(type);
710 [System.Diagnostics.Conditional("DEBUG")]
711 static internal void AssertTypeInvariant(string message, Func<bool> assertPredicate)
713 System.Diagnostics.Debug.Assert(assertPredicate(),
714 "Type invariant check FAILED\n" + message);
716 #endregion // Internal interface
721 #region Private Interface
724 private static bool IsPrimitiveTypeSubTypeOf(TypeUsage fromType, TypeUsage toType)
726 Debug.Assert(fromType != null, "fromType must not be null");
727 Debug.Assert(Helper.IsPrimitiveType(fromType.EdmType), "fromType must be primitive type");
728 Debug.Assert(toType != null, "toType must not be null");
729 Debug.Assert(Helper.IsPrimitiveType(toType.EdmType), "toType must be primitive type");
731 if (!IsSubTypeOf((PrimitiveType)fromType.EdmType, (PrimitiveType)toType.EdmType))
739 private static bool IsSubTypeOf(PrimitiveType subPrimitiveType, PrimitiveType superPrimitiveType)
741 if (object.ReferenceEquals(subPrimitiveType, superPrimitiveType))
746 if (Helper.AreSameSpatialUnionType(subPrimitiveType, superPrimitiveType))
751 objectModel.ReadOnlyCollection<PrimitiveType> superTypes = EdmProviderManifest.Instance.GetPromotionTypes(subPrimitiveType);
753 return (-1 != superTypes.IndexOf(superPrimitiveType));
756 #endregion // Subtyping
758 #region Promotability
759 private static bool IsPromotableTo(RowType fromRowType, RowType toRowType)
761 Debug.Assert(fromRowType != null && toRowType != null);
763 if (fromRowType.Properties.Count != toRowType.Properties.Count)
768 for (int i = 0; i < fromRowType.Properties.Count; i++)
770 if (!IsPromotableTo(fromRowType.Properties[i].TypeUsage, toRowType.Properties[i].TypeUsage))
779 private static bool IsPrimitiveTypePromotableTo(TypeUsage fromType, TypeUsage toType)
781 Debug.Assert(fromType != null, "fromType must not be null");
782 Debug.Assert(Helper.IsPrimitiveType(fromType.EdmType), "fromType must be primitive type");
783 Debug.Assert(toType != null, "toType must not be null");
784 Debug.Assert(Helper.IsPrimitiveType(toType.EdmType), "toType must be primitive type");
786 if (!IsSubTypeOf((PrimitiveType)fromType.EdmType, (PrimitiveType)toType.EdmType))
794 #endregion // promotability
796 #region Common Super-Type
797 private static bool TryGetCommonType(EdmType edmType1, EdmType edmType2, out EdmType commonEdmType)
799 Debug.Assert(edmType1 != null && edmType2 != null);
801 if (edmType2 == edmType1)
803 commonEdmType = edmType1;
807 if (Helper.IsPrimitiveType(edmType1) && Helper.IsPrimitiveType(edmType2))
809 return TryGetCommonType((PrimitiveType)edmType1,
810 (PrimitiveType)edmType2,
814 else if (Helper.IsCollectionType(edmType1) && Helper.IsCollectionType(edmType2))
816 return TryGetCommonType((CollectionType)edmType1,
817 (CollectionType)edmType2,
821 else if (Helper.IsEntityTypeBase(edmType1) && Helper.IsEntityTypeBase(edmType2))
823 return TryGetCommonBaseType(edmType1,
828 else if (Helper.IsRefType(edmType1) && Helper.IsRefType(edmType2))
830 return TryGetCommonType((RefType)edmType1,
835 else if (Helper.IsRowType(edmType1) && Helper.IsRowType(edmType2))
837 return TryGetCommonType((RowType)edmType1,
843 commonEdmType = null;
848 private static bool TryGetCommonPrimitiveType(TypeUsage type1, TypeUsage type2, out TypeUsage commonType)
850 Debug.Assert(type1 != null, "type1 must not be null");
851 Debug.Assert(Helper.IsPrimitiveType(type1.EdmType), "type1 must be primitive type");
852 Debug.Assert(type2 != null, "type2 must not be null");
853 Debug.Assert(Helper.IsPrimitiveType(type2.EdmType), "type2 must be primitive type");
857 if (IsPromotableTo(type1, type2))
859 commonType = ForgetConstraints(type2);
863 if (IsPromotableTo(type2, type1))
865 commonType = ForgetConstraints(type1);
869 objectModel.ReadOnlyCollection<PrimitiveType> superTypes = GetPrimitiveCommonSuperTypes((PrimitiveType)type1.EdmType,
870 (PrimitiveType)type2.EdmType);
871 if (superTypes.Count == 0)
876 commonType = TypeUsage.CreateDefaultTypeUsage(superTypes[0]);
877 return null != commonType;
880 private static bool TryGetCommonType(PrimitiveType primitiveType1, PrimitiveType primitiveType2, out EdmType commonType)
884 if (IsSubTypeOf(primitiveType1, primitiveType2))
886 commonType = primitiveType2;
890 if (IsSubTypeOf(primitiveType2, primitiveType1))
892 commonType = primitiveType1;
896 objectModel.ReadOnlyCollection<PrimitiveType> superTypes = GetPrimitiveCommonSuperTypes(primitiveType1, primitiveType2);
897 if (superTypes.Count > 0)
899 commonType = superTypes[0];
906 private static bool TryGetCommonType(CollectionType collectionType1, CollectionType collectionType2, out EdmType commonType)
908 TypeUsage commonTypeUsage = null;
909 if (!TryGetCommonType(collectionType1.TypeUsage, collectionType2.TypeUsage, out commonTypeUsage))
915 commonType = new CollectionType(commonTypeUsage);
919 private static bool TryGetCommonType(RefType refType1, RefType reftype2, out EdmType commonType)
921 Debug.Assert(refType1.ElementType != null && reftype2.ElementType != null);
923 if (!TryGetCommonType(refType1.ElementType, reftype2.ElementType, out commonType))
928 commonType = new RefType((EntityType)commonType);
932 private static bool TryGetCommonType(RowType rowType1, RowType rowType2, out EdmType commonRowType)
934 if (rowType1.Properties.Count != rowType2.Properties.Count ||
935 rowType1.InitializerMetadata != rowType2.InitializerMetadata)
937 commonRowType = null;
941 // find a common type for every property
942 List<EdmProperty> commonProperties = new List<EdmProperty>();
943 for (int i = 0; i < rowType1.Properties.Count; i++)
945 TypeUsage columnCommonTypeUsage;
946 if (!TryGetCommonType(rowType1.Properties[i].TypeUsage, rowType2.Properties[i].TypeUsage, out columnCommonTypeUsage))
948 commonRowType = null;
952 commonProperties.Add(new EdmProperty(rowType1.Properties[i].Name, columnCommonTypeUsage));
955 commonRowType = new RowType(commonProperties, rowType1.InitializerMetadata);
959 private static bool TryGetCommonBaseType(EdmType type1, EdmType type2, out EdmType commonBaseType)
961 // put all the other base types in a dictionary
962 Dictionary<EdmType, byte> otherBaseTypes = new Dictionary<EdmType, byte>();
963 for (EdmType ancestor = type2; ancestor != null; ancestor = ancestor.BaseType)
965 otherBaseTypes.Add(ancestor, 0);
968 // walk up the ancestor chain, and see if any of them are
969 // common to the otherTypes ancestors
970 for (EdmType ancestor = type1; ancestor != null; ancestor = ancestor.BaseType)
973 if (otherBaseTypes.ContainsKey(ancestor))
975 commonBaseType = ancestor;
980 commonBaseType = null;
984 private static bool HasCommonType(TypeUsage type1, TypeUsage type2)
986 return (null != TypeHelpers.GetCommonTypeUsage(type1, type2));
988 #endregion // common super-type helpers
990 #region Comparability
992 /// Determines if the given edmType is equal comparable. Consult "EntitySql Language Specification",
993 /// section 7 - Comparison and Dependent Operations for details.
995 /// <param name="edmType">an instance of an EdmType</param>
996 /// <returns>true if edmType is equal-comparable, false otherwise</returns>
997 private static bool IsEqualComparable(EdmType edmType)
999 if (Helper.IsPrimitiveType(edmType) || Helper.IsRefType(edmType) || Helper.IsEntityType(edmType) || Helper.IsEnumType(edmType))
1003 else if (Helper.IsRowType(edmType))
1005 RowType rowType = (RowType)edmType;
1006 foreach (EdmProperty rowProperty in rowType.Properties)
1008 if (!IsEqualComparable(rowProperty.TypeUsage))
1018 /// Determines if the given edmType is order comparable. Consult "EntitySql Language Specification",
1019 /// section 7 - Comparison and Dependent Operations for details.
1021 /// <param name="edmType">an instance of an EdmType</param>
1022 /// <returns>true if edmType is order-comparable, false otherwise</returns>
1023 private static bool IsOrderComparable(EdmType edmType)
1025 // only primitive and enum types are assumed to be order-comparable though they
1026 // may still fail during runtime depending on the provider specific behavior
1027 return Helper.IsScalarType(edmType);
1031 #region Private Helpers
1033 private static bool CompareTypes(TypeUsage fromType, TypeUsage toType, bool equivalenceOnly)
1035 Debug.Assert(fromType != null && toType != null);
1037 // If the type usages are the same reference, they are equal.
1038 if (object.ReferenceEquals(fromType, toType))
1043 if (fromType.EdmType.BuiltInTypeKind != toType.EdmType.BuiltInTypeKind)
1049 // Ensure structural evaluation for Collection, Ref and Row types
1051 if (fromType.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType)
1053 // Collection Type: Just compare the Element types
1054 return CompareTypes(((CollectionType)fromType.EdmType).TypeUsage,
1055 ((CollectionType)toType.EdmType).TypeUsage,
1058 else if (fromType.EdmType.BuiltInTypeKind == BuiltInTypeKind.RefType)
1060 // Both are Reference Types, so compare the referenced Entity types
1061 return ((RefType)fromType.EdmType).ElementType.EdmEquals(((RefType)toType.EdmType).ElementType);
1063 else if (fromType.EdmType.BuiltInTypeKind == BuiltInTypeKind.RowType)
1066 RowType fromRow = (RowType)fromType.EdmType;
1067 RowType toRow = (RowType)toType.EdmType;
1068 // Both are RowTypes, so compare the structure.
1069 // The number of properties must be the same.
1070 if (fromRow.Properties.Count != toRow.Properties.Count)
1075 // Compare properties. For an equivalence comparison, only
1076 // property types must match, otherwise names and types must match.
1077 for (int idx = 0; idx < fromRow.Properties.Count; idx++)
1079 EdmProperty fromProp = fromRow.Properties[idx];
1080 EdmProperty toProp = toRow.Properties[idx];
1082 if (!equivalenceOnly && (fromProp.Name != toProp.Name))
1087 if (!CompareTypes(fromProp.TypeUsage, toProp.TypeUsage, equivalenceOnly))
1097 // compare non-transient type usages - simply compare the edm types instead
1099 return fromType.EdmType.EdmEquals(toType.EdmType);
1103 /// Computes the closure of common super types of the set of predefined edm primitive types
1104 /// This is done only once and cached as opposed to previous implementation that was computing
1105 /// this for every new pair of types.
1107 private static void ComputeCommonTypeClosure()
1109 if (null != _commonTypeClosure)
1114 objectModel.ReadOnlyCollection<PrimitiveType>[,] commonTypeClosure = new objectModel.ReadOnlyCollection<PrimitiveType>[EdmConstants.NumPrimitiveTypes, EdmConstants.NumPrimitiveTypes];
1115 for (int i = 0; i < EdmConstants.NumPrimitiveTypes; i++)
1117 commonTypeClosure[i, i] = Helper.EmptyPrimitiveTypeReadOnlyCollection;
1120 objectModel.ReadOnlyCollection<PrimitiveType> primitiveTypes = EdmProviderManifest.Instance.GetStoreTypes();
1122 for (int i = 0; i < EdmConstants.NumPrimitiveTypes; i++)
1124 for (int j = 0; j < i; j++)
1126 commonTypeClosure[i, j] = Intersect(EdmProviderManifest.Instance.GetPromotionTypes(primitiveTypes[i]),
1127 EdmProviderManifest.Instance.GetPromotionTypes(primitiveTypes[j]));
1129 commonTypeClosure[j, i] = commonTypeClosure[i, j];
1133 TypeSemantics.AssertTypeInvariant("Common Type closure is incorrect",
1136 for (int i = 0; i < EdmConstants.NumPrimitiveTypes; i++)
1138 for (int j = 0; j < EdmConstants.NumPrimitiveTypes; j++)
1140 if (commonTypeClosure[i, j] != commonTypeClosure[j, i])
1142 if (i == j && commonTypeClosure[i, j].Count != 0)
1149 System.Threading.Interlocked.CompareExchange<objectModel.ReadOnlyCollection<PrimitiveType>[,]>(ref _commonTypeClosure, commonTypeClosure, null);
1153 /// returns the intersection of types.
1155 /// <param name="types1"></param>
1156 /// <param name="types2"></param>
1157 /// <returns></returns>
1158 private static objectModel.ReadOnlyCollection<PrimitiveType> Intersect(IList<PrimitiveType> types1, IList<PrimitiveType> types2)
1160 List<PrimitiveType> commonTypes = new List<PrimitiveType>();
1161 for (int i = 0; i < types1.Count; i++)
1163 if (types2.Contains(types1[i]))
1165 commonTypes.Add(types1[i]);
1169 if (0 == commonTypes.Count)
1171 return Helper.EmptyPrimitiveTypeReadOnlyCollection;
1174 return new objectModel.ReadOnlyCollection<PrimitiveType>(commonTypes);
1178 /// Returns the list of common super types of two primitive types.
1180 /// <param name="primitiveType1"></param>
1181 /// <param name="primitiveType2"></param>
1182 /// <returns></returns>
1183 private static objectModel.ReadOnlyCollection<PrimitiveType> GetPrimitiveCommonSuperTypes(PrimitiveType primitiveType1, PrimitiveType primitiveType2)
1185 ComputeCommonTypeClosure();
1186 return _commonTypeClosure[(int)primitiveType1.PrimitiveTypeKind, (int)primitiveType2.PrimitiveTypeKind];
1188 #endregion // Private Helpers
1190 #endregion // Private interface