1 //---------------------------------------------------------------------
2 // <copyright file="StructuredType.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.EntityModel.SchemaObjectModel
13 using System.Data.Entity;
14 using System.Data.Metadata.Edm;
15 using System.Diagnostics;
16 using System.Globalization;
20 /// Summary description for StructuredType.
22 internal abstract class StructuredType : SchemaType
24 #region Instance Fields
25 private bool? _baseTypeResolveResult;
26 private string _unresolvedBaseType = null;
27 private StructuredType _baseType = null;
28 private bool _isAbstract = false;
29 private SchemaElementLookUpTable<SchemaElement> _namedMembers = null;
30 private ISchemaElementLookUpTable<StructuredProperty> _properties = null;
34 private static readonly char[] NameSeparators = new char[] { '.' };
37 #region Public Properties
41 public StructuredType BaseType
57 public ISchemaElementLookUpTable<StructuredProperty> Properties
61 if (_properties == null)
63 _properties = new FilteredSchemaElementLookUpTable<StructuredProperty, SchemaElement>(NamedMembers);
73 protected SchemaElementLookUpTable<SchemaElement> NamedMembers
77 if (_namedMembers == null)
79 _namedMembers = new SchemaElementLookUpTable<SchemaElement>();
88 public virtual bool IsTypeHierarchyRoot
92 Debug.Assert((BaseType == null && _unresolvedBaseType == null) ||
93 (BaseType != null && _unresolvedBaseType != null), "you are checking for the hierarchy root before the basetype has been set");
95 // any type without a base is a base type
96 return BaseType == null;
104 public bool IsAbstract
115 #region More Public Methods
117 /// Find a property by name in the type hierarchy
119 /// <param name="name">simple property name</param>
120 /// <returns>the StructuredProperty object if name exists, null otherwise</returns>
121 public StructuredProperty FindProperty(string name)
123 StructuredProperty property = Properties.LookUpEquivalentKey(name);
124 if (property != null)
127 if (IsTypeHierarchyRoot)
130 return BaseType.FindProperty(name);
135 /// Determines whether this type is of the same type as baseType,
136 /// or is derived from baseType.
138 /// <param name="baseType"></param>
139 /// <returns>true if this type is of the baseType, false otherwise</returns>
140 public bool IsOfType(StructuredType baseType)
142 StructuredType type = this;
144 while (type != null && type != baseType)
146 type = type.BaseType;
149 return (type == baseType);
153 #region Protected Methods
157 internal override void ResolveTopLevelNames()
159 base.ResolveTopLevelNames();
161 TryResolveBaseType();
163 foreach (SchemaElement member in NamedMembers)
164 member.ResolveTopLevelNames();
171 /// <returns></returns>
172 internal override void Validate()
176 foreach (SchemaElement member in NamedMembers)
178 if (BaseType != null)
180 StructuredType definingType;
181 SchemaElement definingMember;
182 string errorMessage = null;
183 if(HowDefined.AsMember == BaseType.DefinesMemberName(member.Name, out definingType, out definingMember))
185 errorMessage = System.Data.Entity.Strings.DuplicateMemberName(member.Name, FQName, definingType.FQName);
187 if (errorMessage != null)
188 member.AddError(ErrorCode.AlreadyDefined, EdmSchemaErrorSeverity.Error, errorMessage);
198 /// <param name="parentElement"></param>
199 protected StructuredType(Schema parentElement)
200 : base(parentElement)
205 /// Add a member to the type
207 /// <param name="newMember">the member being added</param>
208 protected void AddMember(SchemaElement newMember)
210 Debug.Assert(newMember != null, "newMember parameter is null");
212 if (string.IsNullOrEmpty(newMember.Name))
214 // this is an error condition that has already been reported.
218 if (this.Schema.DataModel != SchemaDataModelOption.ProviderDataModel &&
219 Utils.CompareNames(newMember.Name, Name) == 0)
221 newMember.AddError(ErrorCode.BadProperty, EdmSchemaErrorSeverity.Error,
222 System.Data.Entity.Strings.InvalidMemberNameMatchesTypeName(newMember.Name, FQName));
225 NamedMembers.Add(newMember, true, Strings.PropertyNameAlreadyDefinedDuplicate);
230 /// See if a name is a member in a type or any of its base types
232 /// <param name="name">name to look for</param>
233 /// <param name="definingType">if defined, the type that defines it</param>
234 /// <param name="definingMember">if defined, the member that defines it</param>
235 /// <returns>how name was defined</returns>
236 private HowDefined DefinesMemberName(string name, out StructuredType definingType, out SchemaElement definingMember)
238 if (NamedMembers.ContainsKey(name))
241 definingMember = NamedMembers[name];
242 return HowDefined.AsMember;
245 definingMember = NamedMembers.LookUpEquivalentKey(name);
246 Debug.Assert(definingMember == null, "we allow the scenario that members can have same name but different cases");
248 if (IsTypeHierarchyRoot)
251 definingMember = null;
252 return HowDefined.NotDefined;
255 return BaseType.DefinesMemberName(name, out definingType, out definingMember);
259 #region Protected Properties
263 protected string UnresolvedBaseType
267 return _unresolvedBaseType;
271 _unresolvedBaseType = value;
275 protected override bool HandleElement(XmlReader reader)
277 if (base.HandleElement(reader))
281 else if (CanHandleElement(reader, XmlConstants.Property))
283 HandlePropertyElement(reader);
289 protected override bool HandleAttribute(XmlReader reader)
291 if (base.HandleAttribute(reader))
295 else if (CanHandleAttribute(reader, XmlConstants.BaseType))
297 HandleBaseTypeAttribute(reader);
300 else if (CanHandleAttribute(reader, XmlConstants.Abstract))
302 HandleAbstractAttribute(reader);
310 #region Private Methods
314 private bool TryResolveBaseType()
316 if (_baseTypeResolveResult.HasValue)
318 return _baseTypeResolveResult.Value;
321 if (BaseType != null)
323 _baseTypeResolveResult = true;
324 return _baseTypeResolveResult.Value;
327 if (UnresolvedBaseType == null)
329 _baseTypeResolveResult = true;
330 return _baseTypeResolveResult.Value;
334 if (!Schema.ResolveTypeName(this, UnresolvedBaseType, out element))
336 _baseTypeResolveResult = false;
337 return _baseTypeResolveResult.Value;
340 BaseType = element as StructuredType;
341 if (BaseType == null)
343 AddError(ErrorCode.InvalidBaseType, EdmSchemaErrorSeverity.Error,
344 System.Data.Entity.Strings.InvalidBaseTypeForStructuredType(UnresolvedBaseType, FQName));
345 _baseTypeResolveResult = false;
346 return _baseTypeResolveResult.Value;
349 // verify that creating this link to the base type will not introduce a cycle;
350 // if so, break the link and add an error
351 if (CheckForInheritanceCycle())
355 AddError(ErrorCode.CycleInTypeHierarchy, EdmSchemaErrorSeverity.Error,
356 System.Data.Entity.Strings.CycleInTypeHierarchy(FQName));
357 _baseTypeResolveResult = false;
358 return _baseTypeResolveResult.Value;
361 _baseTypeResolveResult = true;
368 /// <param name="reader"></param>
369 private void HandleBaseTypeAttribute(XmlReader reader)
371 Debug.Assert(UnresolvedBaseType == null, string.Format(CultureInfo.CurrentCulture, "{0} is already defined", reader.Name));
374 if (!Utils.GetDottedName(this.Schema, reader, out baseType))
377 UnresolvedBaseType = baseType;
383 /// <param name="reader"></param>
384 private void HandleAbstractAttribute(XmlReader reader)
386 HandleBoolAttribute(reader, ref _isAbstract);
392 /// <param name="reader"></param>
393 private void HandlePropertyElement(XmlReader reader)
395 StructuredProperty property = new StructuredProperty(this);
397 property.Parse(reader);
403 /// Determine if a cycle exists in the type hierarchy: use two pointers to
404 /// walk the chain, if one catches up with the other, we have a cycle.
406 /// <returns>true if a cycle exists in the type hierarchy, false otherwise</returns>
407 private bool CheckForInheritanceCycle()
409 StructuredType baseType = BaseType;
410 Debug.Assert(baseType != null);
412 StructuredType ref1 = baseType;
413 StructuredType ref2 = baseType;
417 ref2 = ref2.BaseType;
419 if (Object.ReferenceEquals(ref1, ref2))
425 ref1 = ref1.BaseType;
428 ref2 = ref2.BaseType;
430 while (ref2 != null);
437 #region Private Properties
440 private enum HowDefined