887d82668913515c2cc19054d4cea6c7a1ed538a
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / EntityModel / SchemaObjectModel / StructuredType.cs
1 //---------------------------------------------------------------------
2 // <copyright file="StructuredType.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 namespace System.Data.EntityModel.SchemaObjectModel
11 {
12     using System;
13     using System.Data.Entity;
14     using System.Data.Metadata.Edm;
15     using System.Diagnostics;
16     using System.Globalization;
17     using System.Xml;
18
19     /// <summary>
20     /// Summary description for StructuredType.
21     /// </summary>
22     internal abstract class StructuredType : SchemaType
23     {
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;
31         #endregion
32
33         #region Static Fields
34         private static readonly char[] NameSeparators = new char[] { '.' };
35         #endregion
36
37         #region Public Properties
38         /// <summary>
39         /// 
40         /// </summary>
41         public StructuredType BaseType
42         {
43             get
44             {
45                 return _baseType;
46             }
47             private set
48             {
49                 _baseType = value;
50             }
51         }
52
53         /// <summary>
54         /// 
55         /// 
56         /// </summary>
57         public ISchemaElementLookUpTable<StructuredProperty> Properties
58         {
59             get
60             {
61                 if (_properties == null)
62                 {
63                     _properties = new FilteredSchemaElementLookUpTable<StructuredProperty, SchemaElement>(NamedMembers);
64                 }
65                 return _properties;
66             }
67         }
68
69         /// <summary>
70         /// 
71         /// 
72         /// </summary>
73         protected SchemaElementLookUpTable<SchemaElement> NamedMembers
74         {
75             get
76             {
77                 if (_namedMembers == null)
78                 {
79                     _namedMembers = new SchemaElementLookUpTable<SchemaElement>();
80                 }
81                 return _namedMembers;
82             }
83         }
84
85         /// <summary>
86         /// 
87         /// </summary>
88         public virtual bool IsTypeHierarchyRoot
89         {
90             get
91             {
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");
94
95                 // any type without a base is a base type
96                 return BaseType == null;
97             }
98         }
99
100
101         /// <summary>
102         /// 
103         /// </summary>
104         public bool IsAbstract
105         {
106             get
107             {
108                 return _isAbstract;
109             }
110         }
111
112
113         #endregion
114
115         #region More Public Methods
116         /// <summary>
117         /// Find a property by name in the type hierarchy
118         /// </summary>
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)
122         {
123             StructuredProperty property = Properties.LookUpEquivalentKey(name);
124             if (property != null)
125                 return property;
126
127             if (IsTypeHierarchyRoot)
128                 return null;
129
130             return BaseType.FindProperty(name);
131         }
132
133
134         /// <summary>
135         /// Determines whether this type is of the same type as baseType, 
136         /// or is derived from baseType.
137         /// </summary>
138         /// <param name="baseType"></param>
139         /// <returns>true if this type is of the baseType, false otherwise</returns>
140         public bool IsOfType(StructuredType baseType)
141         {
142             StructuredType type = this;
143
144             while (type != null && type != baseType)
145             {
146                 type = type.BaseType;
147             }
148
149             return (type == baseType);
150         }
151         #endregion
152
153         #region Protected Methods
154         /// <summary>
155         /// 
156         /// </summary>
157         internal override void ResolveTopLevelNames()
158         {
159             base.ResolveTopLevelNames();
160
161             TryResolveBaseType();
162
163             foreach (SchemaElement member in NamedMembers)
164                 member.ResolveTopLevelNames();
165
166         }
167
168         /// <summary>
169         /// 
170         /// </summary>
171         /// <returns></returns>
172         internal override void Validate()
173         {
174             base.Validate();
175
176             foreach (SchemaElement member in NamedMembers)
177             {
178                 if (BaseType != null)
179                 {
180                     StructuredType definingType;
181                     SchemaElement definingMember;
182                     string errorMessage = null;
183                     if(HowDefined.AsMember == BaseType.DefinesMemberName(member.Name, out definingType, out definingMember))
184                     {
185                         errorMessage = System.Data.Entity.Strings.DuplicateMemberName(member.Name, FQName, definingType.FQName);
186                     }
187                     if (errorMessage != null)
188                         member.AddError(ErrorCode.AlreadyDefined, EdmSchemaErrorSeverity.Error, errorMessage);
189                 }
190
191                 member.Validate();
192             }
193         }
194
195         /// <summary>
196         /// 
197         /// </summary>
198         /// <param name="parentElement"></param>
199         protected StructuredType(Schema parentElement)
200             : base(parentElement)
201         {
202         }
203
204         /// <summary>
205         /// Add a member to the type
206         /// </summary>
207         /// <param name="newMember">the member being added</param>
208         protected void AddMember(SchemaElement newMember)
209         {
210             Debug.Assert(newMember != null, "newMember parameter is null");
211
212             if (string.IsNullOrEmpty(newMember.Name))
213             {
214                 // this is an error condition that has already been reported.
215                 return;
216             }
217
218             if (this.Schema.DataModel != SchemaDataModelOption.ProviderDataModel &&
219                  Utils.CompareNames(newMember.Name, Name) == 0)
220             {
221                 newMember.AddError(ErrorCode.BadProperty, EdmSchemaErrorSeverity.Error,
222                     System.Data.Entity.Strings.InvalidMemberNameMatchesTypeName(newMember.Name, FQName));
223             }
224
225             NamedMembers.Add(newMember, true, Strings.PropertyNameAlreadyDefinedDuplicate);
226         }
227
228
229         /// <summary>
230         /// See if a name is a member in a type or any of its base types
231         /// </summary>
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)
237         {
238             if (NamedMembers.ContainsKey(name))
239             {
240                 definingType = this;
241                 definingMember = NamedMembers[name];
242                 return HowDefined.AsMember;
243             }
244
245             definingMember = NamedMembers.LookUpEquivalentKey(name);
246             Debug.Assert(definingMember == null, "we allow the scenario that members can have same name but different cases");
247
248             if (IsTypeHierarchyRoot)
249             {
250                 definingType = null;
251                 definingMember = null;
252                 return HowDefined.NotDefined;
253             }
254
255             return BaseType.DefinesMemberName(name, out definingType, out definingMember);
256         }
257         #endregion
258
259         #region Protected Properties
260         /// <summary>
261         /// 
262         /// </summary>
263         protected string UnresolvedBaseType
264         {
265             get
266             {
267                 return _unresolvedBaseType;
268             }
269             set
270             {
271                 _unresolvedBaseType = value;
272             }
273         }
274
275         protected override bool HandleElement(XmlReader reader)
276         {
277             if (base.HandleElement(reader))
278             {
279                 return true;
280             }
281             else if (CanHandleElement(reader, XmlConstants.Property))
282             {
283                 HandlePropertyElement(reader);
284                 return true;
285             }
286             return false;
287         }
288
289         protected override bool HandleAttribute(XmlReader reader)
290         {
291             if (base.HandleAttribute(reader))
292             {
293                 return true;
294             }
295             else if (CanHandleAttribute(reader, XmlConstants.BaseType))
296             {
297                 HandleBaseTypeAttribute(reader);
298                 return true;
299             }
300             else if (CanHandleAttribute(reader, XmlConstants.Abstract))
301             {
302                 HandleAbstractAttribute(reader);
303                 return true;
304             }
305
306             return false;
307         }
308         #endregion
309
310         #region Private Methods
311         /// <summary>
312         /// 
313         /// </summary>
314         private bool TryResolveBaseType()
315         {
316             if (_baseTypeResolveResult.HasValue)
317             {
318                 return _baseTypeResolveResult.Value;
319             }
320
321             if (BaseType != null)
322             {
323                 _baseTypeResolveResult = true;
324                 return _baseTypeResolveResult.Value;
325             }
326
327             if (UnresolvedBaseType == null)
328             {
329                 _baseTypeResolveResult = true;
330                 return _baseTypeResolveResult.Value;
331             }
332
333             SchemaType element;
334             if (!Schema.ResolveTypeName(this, UnresolvedBaseType, out element))
335             {
336                 _baseTypeResolveResult = false;
337                 return _baseTypeResolveResult.Value;
338             }
339
340             BaseType = element as StructuredType;
341             if (BaseType == null)
342             {
343                 AddError(ErrorCode.InvalidBaseType, EdmSchemaErrorSeverity.Error,
344                     System.Data.Entity.Strings.InvalidBaseTypeForStructuredType(UnresolvedBaseType, FQName));
345                 _baseTypeResolveResult = false;
346                 return _baseTypeResolveResult.Value;
347             }
348
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())
352             {
353                 BaseType = null;
354
355                 AddError(ErrorCode.CycleInTypeHierarchy, EdmSchemaErrorSeverity.Error,
356                     System.Data.Entity.Strings.CycleInTypeHierarchy(FQName));
357                 _baseTypeResolveResult = false;
358                 return _baseTypeResolveResult.Value;
359             }
360
361             _baseTypeResolveResult = true;
362             return true;
363         }
364
365         /// <summary>
366         /// 
367         /// </summary>
368         /// <param name="reader"></param>
369         private void HandleBaseTypeAttribute(XmlReader reader)
370         {
371             Debug.Assert(UnresolvedBaseType == null, string.Format(CultureInfo.CurrentCulture, "{0} is already defined", reader.Name));
372
373             string baseType;
374             if (!Utils.GetDottedName(this.Schema, reader, out baseType))
375                 return;
376
377             UnresolvedBaseType = baseType;
378         }
379
380         /// <summary>
381         /// 
382         /// </summary>
383         /// <param name="reader"></param>
384         private void HandleAbstractAttribute(XmlReader reader)
385         {
386             HandleBoolAttribute(reader, ref _isAbstract);
387         }
388
389         /// <summary>
390         /// 
391         /// </summary>
392         /// <param name="reader"></param>
393         private void HandlePropertyElement(XmlReader reader)
394         {
395             StructuredProperty property = new StructuredProperty(this);
396
397             property.Parse(reader);
398
399             AddMember(property);
400         }
401
402         /// <summary>
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.
405         /// </summary>
406         /// <returns>true if a cycle exists in the type hierarchy, false otherwise</returns>
407         private bool CheckForInheritanceCycle()
408         {
409             StructuredType baseType = BaseType;
410             Debug.Assert(baseType != null);
411
412             StructuredType ref1 = baseType;
413             StructuredType ref2 = baseType;
414
415             do
416             {
417                 ref2 = ref2.BaseType;
418
419                 if (Object.ReferenceEquals(ref1, ref2))
420                     return true;
421
422                 if (ref1 == null)
423                     return false;
424
425                 ref1 = ref1.BaseType;
426
427                 if (ref2 != null)
428                     ref2 = ref2.BaseType;
429             }
430             while (ref2 != null);
431
432             return false;
433         }
434
435         #endregion
436
437         #region Private Properties
438         #endregion
439
440         private enum HowDefined
441         {
442             NotDefined,
443             AsMember,
444         }
445     }
446 }