Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.Entity / System / Data / EntityModel / SchemaObjectModel / SchemaElement.cs
blob79be13fdd4ff42e914865cf90fb6b737b13da0f5
1 //---------------------------------------------------------------------
2 // <copyright file="SchemaElement.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.EntityModel.SchemaObjectModel
12 using System;
13 using System.Collections.Generic;
14 using System.Data;
15 using System.Data.Entity;
16 using System.Data.Metadata.Edm;
17 using System.Diagnostics;
18 using System.Globalization;
19 using System.IO;
20 using System.Xml;
21 using System.Xml.Linq;
23 /// <summary>
24 /// Summary description for SchemaElement.
25 /// </summary>
26 [DebuggerDisplay("Name={Name}")]
27 internal abstract class SchemaElement
29 // see http://www.w3.org/TR/2006/REC-xml-names-20060816/
30 internal const string XmlNamespaceNamespace = "http://www.w3.org/2000/xmlns/";
33 #region Instance Fields
34 private SchemaElement _parentElement = null;
35 private Schema _schema = null;
36 private int _lineNumber = 0;
37 private int _linePosition = 0;
38 private string _name = null;
39 private DocumentationElement _documentation = null;
41 private List<MetadataProperty> _otherContent;
43 #endregion
45 #region Static Fields
46 /// <summary></summary>
47 protected const int MaxValueVersionComponent = short.MaxValue;
48 #endregion
50 #region Public Properties
51 /// <summary>
52 ///
53 /// </summary>
54 internal int LineNumber
56 get
58 return _lineNumber;
62 /// <summary>
63 ///
64 /// </summary>
65 internal int LinePosition
67 get
69 return _linePosition;
73 /// <summary>
74 ///
75 /// </summary>
76 public virtual string Name
78 get
80 return _name;
82 set
84 _name = value;
88 /// <summary>
89 ///
90 /// </summary>
91 internal DocumentationElement Documentation
93 get
95 return _documentation;
97 set
99 _documentation = value;
103 /// <summary>
104 ///
105 /// </summary>
106 internal SchemaElement ParentElement
110 return _parentElement;
112 private set
114 _parentElement = value;
118 /// <summary>
119 ///
120 /// </summary>
121 internal Schema Schema
125 return _schema;
129 _schema = value;
133 /// <summary>
134 ///
135 /// </summary>
136 public virtual string FQName
140 return Name;
144 /// <summary>
145 ///
146 /// </summary>
147 public virtual string Identity
151 return Name;
156 public List<MetadataProperty> OtherContent
158 get
160 if (_otherContent == null)
162 _otherContent = new List<MetadataProperty>();
165 return _otherContent;
168 #endregion
170 #region Internal Methods
171 /// <summary>
172 /// Validates this element and its children
173 /// </summary>
175 internal virtual void Validate()
179 /// <summary>
180 ///
181 /// </summary>
182 /// <param name="errorCode"></param>
183 /// <param name="severity"></param>
184 /// <param name="lineNumber"></param>
185 /// <param name="linePosition"></param>
186 /// <param name="message"></param>
187 internal void AddError( ErrorCode errorCode, EdmSchemaErrorSeverity severity, int lineNumber, int linePosition, object message )
189 AddError(errorCode,severity,SchemaLocation,lineNumber,linePosition,message);
192 /// <summary>
193 ///
194 /// </summary>
195 /// <param name="errorCode"></param>
196 /// <param name="severity"></param>
197 /// <param name="reader"></param>
198 /// <param name="message"></param>
199 internal void AddError( ErrorCode errorCode, EdmSchemaErrorSeverity severity, XmlReader reader, object message )
201 int lineNumber;
202 int linePosition;
203 GetPositionInfo(reader, out lineNumber, out linePosition);
204 AddError(errorCode,severity,SchemaLocation,lineNumber,linePosition,message);
207 /// <summary>
208 ///
209 /// </summary>
210 /// <param name="errorCode"></param>
211 /// <param name="severity"></param>
212 /// <param name="message"></param>
213 internal void AddError( ErrorCode errorCode, EdmSchemaErrorSeverity severity, object message )
215 AddError(errorCode,severity,SchemaLocation,LineNumber,LinePosition,message);
218 /// <summary>
219 ///
220 /// </summary>
221 /// <param name="errorCode"></param>
222 /// <param name="severity"></param>
223 /// <param name="element"></param>
224 /// <param name="message"></param>
225 internal void AddError( ErrorCode errorCode, EdmSchemaErrorSeverity severity, SchemaElement element, object message )
227 AddError(errorCode,severity,element.Schema.Location,element.LineNumber,element.LinePosition,message);
230 /// <summary>
231 ///
232 /// </summary>
233 /// <param name="reader"></param>
234 /// <returns></returns>
235 internal void Parse(XmlReader reader)
237 GetPositionInfo(reader);
239 bool hasEndElement = !reader.IsEmptyElement;
241 Debug.Assert(reader.NodeType == XmlNodeType.Element);
242 for ( bool more = reader.MoveToFirstAttribute(); more; more = reader.MoveToNextAttribute() )
244 ParseAttribute(reader);
246 HandleAttributesComplete();
248 bool done = !hasEndElement;
249 bool skipToNextElement = false;
250 while ( !done )
252 if ( skipToNextElement )
254 skipToNextElement = false;
255 reader.Skip();
256 if ( reader.EOF )
257 break;
259 else
261 if ( !reader.Read() )
262 break;
264 switch ( reader.NodeType )
266 case XmlNodeType.Element:
267 skipToNextElement = ParseElement(reader);
268 break;
270 case XmlNodeType.EndElement:
272 done = true;
273 break;
276 case XmlNodeType.CDATA:
277 case XmlNodeType.Text:
278 case XmlNodeType.SignificantWhitespace:
279 ParseText(reader);
280 break;
282 // we ignore these childless elements
283 case XmlNodeType.Whitespace:
284 case XmlNodeType.XmlDeclaration:
285 case XmlNodeType.Comment:
286 case XmlNodeType.Notation:
287 case XmlNodeType.ProcessingInstruction:
289 break;
292 // we ignore these elements that can have children
293 case XmlNodeType.DocumentType:
294 case XmlNodeType.EntityReference:
296 skipToNextElement = true;
297 break;
300 default:
302 AddError( ErrorCode.UnexpectedXmlNodeType, EdmSchemaErrorSeverity.Error, reader,
303 System.Data.Entity.Strings.UnexpectedXmlNodeType(reader.NodeType));
304 skipToNextElement = true;
305 break;
309 HandleChildElementsComplete();
310 if ( reader.EOF && reader.Depth > 0 )
312 AddError( ErrorCode.MalformedXml, EdmSchemaErrorSeverity.Error, 0, 0,
313 System.Data.Entity.Strings.MalformedXml(LineNumber,LinePosition));
317 /// <summary>
318 /// Set the current line number and position for an XmlReader
319 /// </summary>
320 /// <param name="reader">the reader whose position is desired</param>
321 internal void GetPositionInfo(XmlReader reader)
323 GetPositionInfo(reader,out _lineNumber,out _linePosition);
326 /// <summary>
327 /// Get the current line number and position for an XmlReader
328 /// </summary>
329 /// <param name="reader">the reader whose position is desired</param>
330 /// <param name="lineNumber">the line number</param>
331 /// <param name="linePosition">the line position</param>
332 internal static void GetPositionInfo(XmlReader reader, out int lineNumber, out int linePosition)
334 IXmlLineInfo xmlLineInfo = reader as IXmlLineInfo;
335 if ( xmlLineInfo != null && xmlLineInfo.HasLineInfo() )
337 lineNumber = xmlLineInfo.LineNumber;
338 linePosition = xmlLineInfo.LinePosition;
340 else
342 lineNumber = 0;
343 linePosition = 0;
347 /// <summary>
348 ///
349 /// </summary>
350 internal virtual void ResolveTopLevelNames()
353 internal virtual void ResolveSecondLevelNames()
356 #endregion
358 #region Protected Methods
360 /// <summary>
361 ///
362 /// </summary>
363 /// <param name="parentElement"></param>
364 internal SchemaElement(SchemaElement parentElement)
366 if ( parentElement != null )
368 ParentElement = parentElement;
369 for ( SchemaElement element = parentElement; element != null; element = element.ParentElement )
371 Schema schema = element as Schema;
372 if ( schema != null )
374 Schema = schema;
375 break;
379 if (Schema == null)
381 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.AllElementsMustBeInSchema);
386 internal SchemaElement(SchemaElement parentElement, string name)
387 : this(parentElement)
389 _name = name;
392 /// <summary>
393 ///
394 /// </summary>
395 protected virtual void HandleAttributesComplete()
399 /// <summary>
400 ///
401 /// </summary>
402 protected virtual void HandleChildElementsComplete()
406 /// <summary>
407 ///
408 /// </summary>
409 /// <param name="reader"></param>
410 /// <param name="field"></param>
411 /// <returns></returns>
412 protected string HandleUndottedNameAttribute(XmlReader reader, string field)
414 string name = field;
415 Debug.Assert(string.IsNullOrEmpty(field), string.Format(CultureInfo.CurrentCulture, "{0} is already defined", reader.Name));
417 bool success = Utils.GetUndottedName(Schema, reader, out name);
418 if ( !success )
419 return name;
421 return name;
424 /// <summary>
425 ///
426 /// </summary>
427 /// <param name="reader"></param>
428 /// <param name="field"></param>
429 /// <param name="errorMessageId"></param>
430 /// <returns></returns>
431 protected ReturnValue<string> HandleDottedNameAttribute(XmlReader reader, string field, Func<object, string> errorFormat)
433 ReturnValue<string> returnValue = new ReturnValue<string>();
434 Debug.Assert(string.IsNullOrEmpty(field), string.Format(CultureInfo.CurrentCulture, "{0} is already defined", reader.Name));
436 string value;
437 if ( !Utils.GetDottedName(Schema,reader,out value) )
438 return returnValue;
440 returnValue.Value = value;
441 return returnValue;
444 /// <summary>
445 /// Use to handle an attribute with an int data type
446 /// </summary>
447 /// <param name="reader">the reader positioned at the int attribute</param>
448 /// <param name="field">The int field to be given the value found</param>
449 /// <returns>true if an int value was successfuly extracted from the attribute, false otherwise.</returns>
450 internal bool HandleIntAttribute(XmlReader reader, ref int field)
452 int value;
453 if ( !Utils.GetInt(Schema, reader, out value) )
454 return false;
456 field = value;
457 return true;
460 /// <summary>
461 /// Use to handle an attribute with an int data type
462 /// </summary>
463 /// <param name="reader">the reader positioned at the int attribute</param>
464 /// <param name="field">The int field to be given the value found</param>
465 /// <returns>true if an int value was successfuly extracted from the attribute, false otherwise.</returns>
466 internal bool HandleByteAttribute(XmlReader reader, ref byte field)
468 byte value;
469 if ( !Utils.GetByte(Schema, reader, out value) )
470 return false;
472 field = value;
473 return true;
475 /// <summary>
476 ///
477 /// </summary>
478 /// <param name="reader"></param>
479 /// <param name="field"></param>
480 /// <returns></returns>
481 internal bool HandleBoolAttribute(XmlReader reader, ref bool field)
483 bool value;
484 if ( !Utils.GetBool(Schema,reader,out value) )
485 return false;
487 field = value;
488 return true;
491 /// <summary>
492 /// Use this to jump through an element that doesn't need any processing
493 /// </summary>
494 /// <param name="reader">xml reader currently positioned at an element</param>
495 protected virtual void SkipThroughElement(XmlReader reader)
497 Debug.Assert(reader != null);
498 Parse(reader);
501 protected void SkipElement(XmlReader reader)
503 using (XmlReader subtree = reader.ReadSubtree())
505 while (subtree.Read()) ;
509 #endregion
511 #region Protected Properties
512 /// <summary>
513 ///
514 /// </summary>
515 protected string SchemaLocation
519 if ( Schema != null )
520 return Schema.Location;
521 return null;
525 protected virtual bool HandleText(XmlReader reader)
527 return false;
530 internal virtual SchemaElement Clone(SchemaElement parentElement)
532 throw Error.NotImplemented();
534 #endregion
536 #region Private Methods
537 /// <summary>
538 ///
539 /// </summary>
540 /// <param name="reader"></param>
541 /// <returns></returns>
542 private void HandleDocumentationElement(XmlReader reader)
544 Documentation = new DocumentationElement(this);
545 Documentation.Parse(reader);
548 /// <summary>
549 ///
550 /// </summary>
551 /// <param name="reader"></param>
552 protected virtual void HandleNameAttribute(XmlReader reader)
554 Name = HandleUndottedNameAttribute(reader, Name);
557 /// <summary>
558 ///
559 /// </summary>
560 /// <param name="errorCode"></param>
561 /// <param name="severity"></param>
562 /// <param name="source"></param>
563 /// <param name="lineNumber"></param>
564 /// <param name="linePosition"></param>
565 /// <param name="message"></param>
566 private void AddError( ErrorCode errorCode, EdmSchemaErrorSeverity severity, string sourceLocation, int lineNumber, int linePosition, object message )
568 EdmSchemaError error = null;
569 string messageString = message as string;
570 if ( messageString != null )
571 error = new EdmSchemaError( messageString, (int)errorCode, severity, sourceLocation, lineNumber, linePosition );
572 else
574 Exception ex = message as Exception;
575 if ( ex != null )
576 error = new EdmSchemaError( ex.Message, (int)errorCode, severity, sourceLocation, lineNumber, linePosition, ex );
577 else
578 error = new EdmSchemaError( message.ToString(), (int)errorCode, severity, sourceLocation, lineNumber, linePosition );
580 Schema.AddError(error);
583 /// <summary>
584 /// Call handler for the current attribute
585 /// </summary>
586 /// <param name="reader">XmlReader positioned at the attribute</param>
587 private void ParseAttribute(XmlReader reader)
589 #if false
590 // the attribute value is schema invalid, just skip it; this avoids some duplicate errors at the expense of better error messages...
591 if ( reader.SchemaInfo != null && reader.SchemaInfo.Validity == System.Xml.Schema.XmlSchemaValidity.Invalid )
592 continue;
593 #endif
594 string attributeNamespace = reader.NamespaceURI;
595 if (attributeNamespace == XmlConstants.AnnotationNamespace
596 && reader.LocalName == XmlConstants.UseStrongSpatialTypes
597 && !ProhibitAttribute(attributeNamespace, reader.LocalName)
598 && HandleAttribute(reader))
600 return;
602 else if (!Schema.IsParseableXmlNamespace(attributeNamespace, true))
604 AddOtherContent(reader);
606 else if (!ProhibitAttribute(attributeNamespace, reader.LocalName)&&
607 HandleAttribute(reader))
609 return;
611 else if (reader.SchemaInfo == null || reader.SchemaInfo.Validity != System.Xml.Schema.XmlSchemaValidity.Invalid)
613 // there's no handler for (namespace,name) and there wasn't a validation error.
614 // Report an error of our own if the node is in no namespace or if it is in one of our xml schemas tartget namespace.
615 if (string.IsNullOrEmpty(attributeNamespace) || Schema.IsParseableXmlNamespace(attributeNamespace, true))
617 AddError(ErrorCode.UnexpectedXmlAttribute, EdmSchemaErrorSeverity.Error, reader, System.Data.Entity.Strings.UnexpectedXmlAttribute(reader.Name));
622 protected virtual bool ProhibitAttribute(string namespaceUri, string localName)
624 return false;
627 /// <summary>
628 /// This overload assumes the default namespace
629 /// </summary>
630 /// <param name="reader"></param>
631 /// <param name="localName"></param>
632 /// <returns></returns>
633 internal static bool CanHandleAttribute(XmlReader reader, string localName)
635 Debug.Assert(reader.NamespaceURI != null);
636 return reader.NamespaceURI.Length == 0 && reader.LocalName == localName;
639 protected virtual bool HandleAttribute(XmlReader reader)
641 if(CanHandleAttribute(reader, XmlConstants.Name))
643 HandleNameAttribute(reader);
644 return true;
647 return false;
650 private bool AddOtherContent(XmlReader reader)
652 int lineNumber;
653 int linePosition;
654 GetPositionInfo(reader, out lineNumber, out linePosition);
656 MetadataProperty property;
657 if (reader.NodeType == XmlNodeType.Element)
660 if (this._schema.SchemaVersion == XmlConstants.EdmVersionForV1 ||
661 this._schema.SchemaVersion == XmlConstants.EdmVersionForV1_1)
663 // skip this element
664 // we don't support element annotations in v1 and v1.1
665 return true;
668 // in V1 and V1.1 the codegen can only appear as the attribute annotation and we want to maintain
669 // the same behavior for V2, thus we throw if we encounter CodeGen namespace
670 // in structural annotation in V2 and furthur version
671 if (this._schema.SchemaVersion >= XmlConstants.EdmVersionForV2
672 && reader.NamespaceURI == XmlConstants.CodeGenerationSchemaNamespace)
674 Debug.Assert(
675 XmlConstants.SchemaVersionLatest == XmlConstants.EdmVersionForV3,
676 "Please add checking for the latest namespace");
678 AddError(ErrorCode.NoCodeGenNamespaceInStructuralAnnotation, EdmSchemaErrorSeverity.Error, lineNumber, linePosition, Strings.NoCodeGenNamespaceInStructuralAnnotation(XmlConstants.CodeGenerationSchemaNamespace));
679 return true;
682 Debug.Assert(
683 !Schema.IsParseableXmlNamespace(reader.NamespaceURI, false),
684 "Structural annotation cannot use any edm reserved namespaces");
686 // using this subtree aproach because when I call
687 // reader.ReadOuterXml() it positions me at the Node beyond
688 // the end of the node I am starting on
689 // which doesn't work with the parsing logic
690 using (XmlReader subtree = reader.ReadSubtree())
692 subtree.Read();
693 XElement element = XElement.Load(new StringReader(subtree.ReadOuterXml()));
695 property = CreateMetadataPropertyFromOtherNamespaceXmlArtifact(element.Name.NamespaceName, element.Name.LocalName, element);
698 else
700 if (reader.NamespaceURI == XmlNamespaceNamespace)
702 // we don't bring in namespace definitions
703 return true;
706 Debug.Assert(reader.NodeType == XmlNodeType.Attribute, "called an attribute function when not on an attribute");
707 property = CreateMetadataPropertyFromOtherNamespaceXmlArtifact(reader.NamespaceURI, reader.LocalName, reader.Value);
710 if (!OtherContent.Exists(mp => mp.Identity == property.Identity))
712 OtherContent.Add(property);
714 else
716 AddError(ErrorCode.AlreadyDefined, EdmSchemaErrorSeverity.Error, lineNumber, linePosition, Strings.DuplicateAnnotation(property.Identity, this.FQName));
718 return false;
721 internal static MetadataProperty CreateMetadataPropertyFromOtherNamespaceXmlArtifact(string xmlNamespaceUri, string artifactName, object value)
723 MetadataProperty property;
724 property = new MetadataProperty(xmlNamespaceUri + ":" + artifactName,
725 TypeUsage.Create(EdmProviderManifest.Instance.GetPrimitiveType(PrimitiveTypeKind.String)),
726 value);
727 return property;
730 /// <summary>
731 /// Call handler for the current element
732 /// </summary>
733 /// <param name="reader">XmlReader positioned at the element</param>
734 /// <returns>true if element content should be skipped</returns>
735 private bool ParseElement(XmlReader reader)
737 string elementNamespace = reader.NamespaceURI;
738 // for schema element that right under the schema, we just ignore them, since schema does not
739 // have metadataproperties
740 if (!Schema.IsParseableXmlNamespace(elementNamespace, true) && this.ParentElement != null)
742 return AddOtherContent(reader);
744 if (HandleElement(reader))
746 return false;
748 else
751 // we need to report an error if the namespace for this element is a target namespace for the xml schemas we are parsing against.
752 // otherwise we assume that this is either a valid 'any' element or that the xsd validator has generated an error
753 if (string.IsNullOrEmpty(elementNamespace) || Schema.IsParseableXmlNamespace(reader.NamespaceURI, false))
755 AddError(ErrorCode.UnexpectedXmlElement, EdmSchemaErrorSeverity.Error, reader, System.Data.Entity.Strings.UnexpectedXmlElement(reader.Name));
757 return true;
761 protected bool CanHandleElement(XmlReader reader, string localName)
763 return reader.NamespaceURI == Schema.SchemaXmlNamespace && reader.LocalName == localName;
766 protected virtual bool HandleElement(XmlReader reader)
768 if (CanHandleElement(reader, XmlConstants.Documentation))
770 HandleDocumentationElement(reader);
771 return true;
774 return false;
777 /// <summary>
778 /// Handle text data.
779 /// </summary>
780 /// <param name="reader">XmlReader positioned at Text, CData, or SignificantWhitespace </param>
781 private void ParseText(XmlReader reader)
783 if (HandleText(reader))
785 return;
787 else if (reader.Value != null && reader.Value.Trim().Length == 0)
789 // just ignore this text. Don't add an error, since the value is just whitespace.
791 else
793 AddError( ErrorCode.TextNotAllowed, EdmSchemaErrorSeverity.Error, reader, System.Data.Entity.Strings.TextNotAllowed(reader.Value ) );
796 #endregion
798 [Conditional("DEBUG")]
799 internal static void AssertReaderConsidersSchemaInvalid(XmlReader reader)
801 Debug.Assert(reader.SchemaInfo == null ||
802 reader.SchemaInfo.Validity != System.Xml.Schema.XmlSchemaValidity.Valid, "The xsd should see this as not acceptable");