2010-04-07 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.XML / System.Xml / DTDObjectModel.cs
blobbbe073f6f13c6461611e3d586245fff56805aa98
1 //
2 // Mono.Xml.DTDObjectModel
3 //
4 // Author:
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
6 //
7 // (C)2003 Atsushi Enomoto
8 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System;
31 using System.Collections;
32 using System.Globalization;
33 using System.IO;
34 using System.Text;
35 using System.Xml;
36 using System.Xml.Schema;
37 #if NET_2_0
38 using System.Collections.Generic;
39 #endif
40 #if NET_2_1
41 using XmlSchemaException = System.Xml.XmlException;
42 #else
43 using Mono.Xml.Schema;
44 #endif
46 #if NET_2_0
47 using XmlTextReaderImpl = Mono.Xml2.XmlTextReader;
48 #else
49 using XmlTextReaderImpl = System.Xml.XmlTextReader;
50 #endif
52 namespace Mono.Xml
54 internal class DTDObjectModel
56 // This specifies the max number of dependent external entities
57 // per a DTD can consume. A malicious external document server
58 // might send users' document processing server a large number
59 // of external entities.
60 public const int AllowedExternalEntitiesMax = 256;
62 DTDAutomataFactory factory;
63 DTDElementAutomata rootAutomata;
64 DTDEmptyAutomata emptyAutomata;
65 DTDAnyAutomata anyAutomata;
66 DTDInvalidAutomata invalidAutomata;
68 DTDElementDeclarationCollection elementDecls;
69 DTDAttListDeclarationCollection attListDecls;
70 DTDParameterEntityDeclarationCollection peDecls;
71 DTDEntityDeclarationCollection entityDecls;
72 DTDNotationDeclarationCollection notationDecls;
73 ArrayList validationErrors;
74 XmlResolver resolver;
75 XmlNameTable nameTable;
77 Hashtable externalResources;
79 string baseURI;
80 string name;
81 string publicId;
82 string systemId;
83 string intSubset;
84 bool intSubsetHasPERef;
85 bool isStandalone;
86 int lineNumber;
87 int linePosition;
89 public DTDObjectModel (XmlNameTable nameTable)
91 this.nameTable = nameTable;
92 elementDecls = new DTDElementDeclarationCollection (this);
93 attListDecls = new DTDAttListDeclarationCollection (this);
94 entityDecls = new DTDEntityDeclarationCollection (this);
95 peDecls = new DTDParameterEntityDeclarationCollection (this);
96 notationDecls = new DTDNotationDeclarationCollection (this);
97 factory = new DTDAutomataFactory (this);
98 validationErrors = new ArrayList ();
99 externalResources = new Hashtable ();
102 public string BaseURI {
103 get { return baseURI; }
104 set { baseURI = value; }
107 public bool IsStandalone {
108 get { return isStandalone; }
109 set { isStandalone = value; }
112 public string Name {
113 get { return name; }
114 set { name = value; }
117 public XmlNameTable NameTable {
118 get { return nameTable; }
121 public string PublicId {
122 get { return publicId; }
123 set { publicId = value; }
126 public string SystemId {
127 get { return systemId; }
128 set { systemId = value; }
131 public string InternalSubset {
132 get { return intSubset; }
133 set { intSubset = value; }
136 public bool InternalSubsetHasPEReference {
137 get { return intSubsetHasPERef; }
138 set { intSubsetHasPERef = value; }
141 public int LineNumber {
142 get { return lineNumber; }
143 set { lineNumber = value; }
146 public int LinePosition {
147 get { return linePosition; }
148 set { linePosition = value; }
151 #if !NET_2_1
152 internal XmlSchema CreateXsdSchema ()
154 XmlSchema s = new XmlSchema ();
155 s.SourceUri = BaseURI;
156 s.LineNumber = LineNumber;
157 s.LinePosition = LinePosition;
158 foreach (DTDElementDeclaration el in ElementDecls.Values)
159 s.Items.Add (el.CreateXsdElement ());
160 return s;
162 #endif
164 public string ResolveEntity (string name)
166 DTDEntityDeclaration decl = EntityDecls [name]
167 as DTDEntityDeclaration;
168 if (decl == null) {
169 #if NET_2_1
170 AddError (new XmlSchemaException (String.Format ("Required entity was not found: {0}", name), null, this.LineNumber, this.LinePosition));
171 #else
172 AddError (new XmlSchemaException ("Required entity was not found.",
173 this.LineNumber, this.LinePosition, null, this.BaseURI, null));
174 #endif
175 return " ";
177 else
178 return decl.EntityValue;
181 internal XmlResolver Resolver {
182 get { return resolver; }
185 public XmlResolver XmlResolver {
186 set { resolver = value; }
189 internal Hashtable ExternalResources {
190 get { return externalResources; }
193 public DTDAutomataFactory Factory {
194 get { return factory; }
197 public DTDElementDeclaration RootElement {
198 get { return ElementDecls [Name]; }
201 public DTDElementDeclarationCollection ElementDecls {
202 get { return elementDecls; }
205 public DTDAttListDeclarationCollection AttListDecls {
206 get { return attListDecls; }
209 public DTDEntityDeclarationCollection EntityDecls {
210 get { return entityDecls; }
213 public DTDParameterEntityDeclarationCollection PEDecls {
214 get { return peDecls; }
217 public DTDNotationDeclarationCollection NotationDecls {
218 get { return notationDecls; }
221 public DTDAutomata RootAutomata {
222 get {
223 if (rootAutomata == null)
224 rootAutomata = new DTDElementAutomata (this, this.Name);
225 return rootAutomata;
229 public DTDEmptyAutomata Empty {
230 get {
231 if (emptyAutomata == null)
232 emptyAutomata = new DTDEmptyAutomata (this);
233 return emptyAutomata;
237 public DTDAnyAutomata Any {
238 get {
239 if (anyAutomata == null)
240 anyAutomata = new DTDAnyAutomata (this);
241 return anyAutomata;
245 public DTDInvalidAutomata Invalid {
246 get {
247 if (invalidAutomata == null)
248 invalidAutomata = new DTDInvalidAutomata (this);
249 return invalidAutomata;
253 public XmlSchemaException [] Errors {
254 get { return validationErrors.ToArray (typeof (XmlSchemaException)) as XmlSchemaException []; }
257 public void AddError (XmlSchemaException ex)
259 validationErrors.Add (ex);
262 internal string GenerateEntityAttributeText (string entityName)
264 DTDEntityDeclaration entity = EntityDecls [entityName] as DTDEntityDeclaration;
265 if (entity == null)
266 return null;
267 return entity.EntityValue;
270 internal XmlTextReaderImpl GenerateEntityContentReader (string entityName, XmlParserContext context)
272 DTDEntityDeclaration entity = EntityDecls [entityName] as DTDEntityDeclaration;
273 if (entity == null)
274 return null;
276 if (entity.SystemId != null) {
277 Uri baseUri = entity.BaseURI == String.Empty ? null : new Uri (entity.BaseURI);
278 Stream stream = resolver.GetEntity (resolver.ResolveUri (baseUri, entity.SystemId), null, typeof (Stream)) as Stream;
279 return new XmlTextReaderImpl (stream, XmlNodeType.Element, context);
281 else
282 return new XmlTextReaderImpl (entity.EntityValue, XmlNodeType.Element, context);
286 #if NET_2_0
287 class DictionaryBase : List<KeyValuePair<string,DTDNode>>
289 public IEnumerable<DTDNode> Values {
290 get {
291 foreach (KeyValuePair<string,DTDNode> p in this)
292 yield return p.Value;
296 #endif
297 internal class DTDCollectionBase : DictionaryBase
299 DTDObjectModel root;
301 protected DTDCollectionBase (DTDObjectModel root)
303 this.root = root;
306 protected DTDObjectModel Root {
307 get { return root; }
310 #if NET_2_0
311 public DictionaryBase InnerHashtable {
312 get { return this; }
315 protected void BaseAdd (string name, DTDNode value)
317 base.Add (new KeyValuePair<string,DTDNode> (name, value));
320 public bool Contains (string key)
322 foreach (KeyValuePair<string,DTDNode> p in this)
323 if (p.Key == key)
324 return true;
325 return false;
328 protected object BaseGet (string name)
330 foreach (KeyValuePair<string,DTDNode> p in this)
331 if (p.Key == name)
332 return p.Value;
333 return null;
335 #else
336 public ICollection Keys {
337 get { return InnerHashtable.Keys; }
340 public ICollection Values {
341 get { return InnerHashtable.Values; }
344 protected void BaseAdd (string name, object value)
346 InnerHashtable.Add (name, value);
349 public bool Contains (string key)
351 return InnerHashtable.Contains (key);
354 protected object BaseGet (string name)
356 return InnerHashtable [name];
358 #endif
361 internal class DTDElementDeclarationCollection : DTDCollectionBase
364 public DTDElementDeclarationCollection (DTDObjectModel root) : base (root) {}
366 public DTDElementDeclaration this [string name] {
367 get { return Get (name); }
370 public DTDElementDeclaration Get (string name)
372 return BaseGet (name) as DTDElementDeclaration;
375 public void Add (string name, DTDElementDeclaration decl)
377 if (Contains (name)) {
378 Root.AddError (new XmlSchemaException (String.Format (
379 "Element declaration for {0} was already added.",
380 name), null));
381 return;
383 decl.SetRoot (Root);
384 BaseAdd (name, decl);
388 internal class DTDAttListDeclarationCollection : DTDCollectionBase
390 public DTDAttListDeclarationCollection (DTDObjectModel root) : base (root) {}
392 public DTDAttListDeclaration this [string name] {
393 get { return BaseGet (name) as DTDAttListDeclaration; }
396 public void Add (string name, DTDAttListDeclaration decl)
398 DTDAttListDeclaration existing = this [name];
399 if (existing != null) {
400 // It is valid, that is additive declaration.
401 foreach (DTDAttributeDefinition def in decl.Definitions)
402 if (decl.Get (def.Name) == null)
403 existing.Add (def);
404 } else {
405 decl.SetRoot (Root);
406 BaseAdd (name, decl);
411 internal class DTDEntityDeclarationCollection : DTDCollectionBase
413 public DTDEntityDeclarationCollection (DTDObjectModel root) : base (root) {}
415 public DTDEntityDeclaration this [string name] {
416 get { return BaseGet (name) as DTDEntityDeclaration; }
419 public void Add (string name, DTDEntityDeclaration decl)
421 if (Contains (name))
422 throw new InvalidOperationException (String.Format (
423 "Entity declaration for {0} was already added.",
424 name));
425 decl.SetRoot (Root);
426 BaseAdd (name, decl);
430 internal class DTDNotationDeclarationCollection : DTDCollectionBase
432 public DTDNotationDeclarationCollection (DTDObjectModel root) : base (root) {}
434 public DTDNotationDeclaration this [string name] {
435 get { return BaseGet (name) as DTDNotationDeclaration; }
438 public void Add (string name, DTDNotationDeclaration decl)
440 if (Contains (name))
441 throw new InvalidOperationException (String.Format (
442 "Notation declaration for {0} was already added.",
443 name));
444 decl.SetRoot (Root);
445 BaseAdd (name, decl);
449 // This class contains either ElementName or ChildModels.
450 internal class DTDContentModel : DTDNode
452 DTDObjectModel root;
453 DTDAutomata compiledAutomata;
455 string ownerElementName;
456 string elementName;
457 DTDContentOrderType orderType = DTDContentOrderType.None;
458 DTDContentModelCollection childModels = new DTDContentModelCollection ();
459 DTDOccurence occurence = DTDOccurence.One;
461 internal DTDContentModel (DTDObjectModel root, string ownerElementName)
463 this.root = root;
464 this.ownerElementName = ownerElementName;
467 public DTDContentModelCollection ChildModels {
468 get { return childModels; }
469 set { childModels = value; }
472 public DTDElementDeclaration ElementDecl {
473 get { return root.ElementDecls [ownerElementName]; }
476 public string ElementName {
477 get { return elementName; }
478 set { elementName = value; }
481 public DTDOccurence Occurence {
482 get { return occurence; }
483 set { occurence = value; }
486 public DTDContentOrderType OrderType {
487 get { return orderType; }
488 set { orderType = value; }
491 public DTDAutomata GetAutomata ()
493 if (compiledAutomata == null)
494 Compile ();
495 return compiledAutomata;
498 public DTDAutomata Compile ()
500 compiledAutomata = CompileInternal ();
501 return compiledAutomata;
504 #if !NET_2_1
505 internal XmlSchemaParticle CreateXsdParticle ()
507 XmlSchemaParticle p = CreateXsdParticleCore ();
508 if (p == null)
509 return null;
511 switch (Occurence) {
512 case DTDOccurence.Optional:
513 p.MinOccurs = 0;
514 break;
515 case DTDOccurence.OneOrMore:
516 p.MaxOccursString = "unbounded";
517 break;
518 case DTDOccurence.ZeroOrMore:
519 p.MinOccurs = 0;
520 p.MaxOccursString = "unbounded";
521 break;
523 return p;
526 XmlSchemaParticle CreateXsdParticleCore ()
528 XmlSchemaParticle p = null;
529 if (ElementName != null) {
530 XmlSchemaElement el = new XmlSchemaElement ();
531 SetLineInfo (el);
532 el.RefName = new XmlQualifiedName (ElementName);
533 return el;
535 else if (ChildModels.Count == 0)
536 return null;
537 else {
538 XmlSchemaGroupBase gb =
539 (OrderType == DTDContentOrderType.Seq) ?
540 (XmlSchemaGroupBase)
541 new XmlSchemaSequence () :
542 new XmlSchemaChoice ();
543 SetLineInfo (gb);
544 foreach (DTDContentModel cm in ChildModels.Items) {
545 XmlSchemaParticle c = cm.CreateXsdParticle ();
546 if (c != null)
547 gb.Items.Add (c);
549 p = gb;
551 return p;
553 #endif
555 private DTDAutomata CompileInternal ()
557 if (ElementDecl.IsAny)
558 return root.Any;
559 if (ElementDecl.IsEmpty)
560 return root.Empty;
562 DTDAutomata basis = GetBasicContentAutomata ();
563 switch (Occurence) {
564 case DTDOccurence.One:
565 return basis;
566 case DTDOccurence.Optional:
567 return Choice (root.Empty, basis);
568 case DTDOccurence.OneOrMore:
569 return new DTDOneOrMoreAutomata (root, basis);
570 case DTDOccurence.ZeroOrMore:
571 return Choice (root.Empty, new DTDOneOrMoreAutomata (root, basis));
573 throw new InvalidOperationException ();
576 private DTDAutomata GetBasicContentAutomata ()
578 if (ElementName != null)
579 return new DTDElementAutomata (root, ElementName);
580 switch (ChildModels.Count) {
581 case 0:
582 return root.Empty;
583 case 1:
584 return ChildModels [0].GetAutomata ();
587 DTDAutomata current = null;
588 int childCount = ChildModels.Count;
589 switch (OrderType) {
590 case DTDContentOrderType.Seq:
591 current = Sequence (
592 ChildModels [childCount - 2].GetAutomata (),
593 ChildModels [childCount - 1].GetAutomata ());
594 for (int i = childCount - 2; i > 0; i--)
595 current = Sequence (
596 ChildModels [i - 1].GetAutomata (), current);
597 return current;
598 case DTDContentOrderType.Or:
599 current = Choice (
600 ChildModels [childCount - 2].GetAutomata (),
601 ChildModels [childCount - 1].GetAutomata ());
602 for (int i = childCount - 2; i > 0; i--)
603 current = Choice (
604 ChildModels [i - 1].GetAutomata (), current);
605 return current;
606 default:
607 throw new InvalidOperationException ("Invalid pattern specification");
611 private DTDAutomata Sequence (DTDAutomata l, DTDAutomata r)
613 return root.Factory.Sequence (l, r);
616 private DTDAutomata Choice (DTDAutomata l, DTDAutomata r)
618 return l.MakeChoice (r);
622 internal class DTDContentModelCollection
624 ArrayList contentModel = new ArrayList ();
626 public DTDContentModelCollection ()
630 public IList Items {
631 get { return contentModel; }
634 public DTDContentModel this [int i] {
635 get { return contentModel [i] as DTDContentModel; }
638 public int Count {
639 get { return contentModel.Count; }
642 public void Add (DTDContentModel model)
644 contentModel.Add (model);
648 internal abstract class DTDNode : IXmlLineInfo
650 DTDObjectModel root;
651 bool isInternalSubset;
652 string baseURI;
653 int lineNumber;
654 int linePosition;
656 public virtual string BaseURI {
657 get { return baseURI; }
658 set { baseURI = value; }
661 public bool IsInternalSubset {
662 get { return isInternalSubset; }
663 set { isInternalSubset = value; }
666 public int LineNumber {
667 get { return lineNumber; }
668 set { lineNumber = value; }
671 public int LinePosition {
672 get { return linePosition; }
673 set { linePosition = value; }
676 public bool HasLineInfo ()
678 return lineNumber != 0;
681 internal void SetRoot (DTDObjectModel root)
683 this.root = root;
684 if (baseURI == null)
685 this.BaseURI = root.BaseURI;
688 protected DTDObjectModel Root {
689 get { return root; }
692 internal XmlException NotWFError (string message)
694 return new XmlException (this as IXmlLineInfo, BaseURI, message);
697 #if !NET_2_1
698 public void SetLineInfo (XmlSchemaObject obj)
700 obj.SourceUri = BaseURI;
701 obj.LineNumber = LineNumber;
702 obj.LinePosition = LinePosition;
704 #endif
707 internal class DTDElementDeclaration : DTDNode
709 DTDObjectModel root;
710 DTDContentModel contentModel;
711 string name;
712 bool isEmpty;
713 bool isAny;
714 bool isMixedContent;
716 internal DTDElementDeclaration (DTDObjectModel root)
718 this.root = root;
721 public string Name {
722 get { return name; }
723 set { name = value; }
725 public bool IsEmpty {
726 get { return isEmpty; }
727 set { isEmpty = value; }
730 public bool IsAny {
731 get { return isAny; }
732 set { isAny = value; }
735 public bool IsMixedContent {
736 get { return isMixedContent; }
737 set { isMixedContent = value; }
740 public DTDContentModel ContentModel {
741 get {
742 if (contentModel == null)
743 contentModel = new DTDContentModel (root, Name);
744 return contentModel;
748 public DTDAttListDeclaration Attributes {
749 get {
750 return Root.AttListDecls [Name];
754 #if !NET_2_1
755 internal XmlSchemaElement CreateXsdElement ()
757 XmlSchemaElement el = new XmlSchemaElement ();
758 SetLineInfo (el);
759 el.Name = Name;
761 XmlSchemaComplexType ct = new XmlSchemaComplexType ();
762 el.SchemaType = ct;
763 if (Attributes != null) {
764 SetLineInfo (ct);
765 foreach (DTDAttributeDefinition a in
766 Attributes.Definitions)
767 ct.Attributes.Add (a.CreateXsdAttribute ());
769 if (IsEmpty)
770 ; // nothing to do
771 else if (IsAny) {
772 XmlSchemaAny any = new XmlSchemaAny ();
773 any.MinOccurs = 0;
774 any.MaxOccursString = "unbounded";
775 ct.Particle = any;
777 else {
778 if (IsMixedContent)
779 ct.IsMixed = true;
780 ct.Particle = ContentModel.CreateXsdParticle ();
784 if (IsEmpty) {
785 el.SchemaType = new XmlSchemaComplexType ();
786 SetLineInfo (el.SchemaType);
788 else if (IsAny)
789 el.SchemaTypeName = new XmlQualifiedName (
790 "anyType", XmlSchema.Namespace);
791 else {
792 XmlSchemaComplexType ct = new XmlSchemaComplexType ();
793 SetLineInfo (ct);
794 if (Attributes != null)
795 foreach (DTDAttributeDefinition a in
796 Attributes.Definitions)
797 ct.Attributes.Add (a.CreateXsdAttribute ());
798 if (IsMixedContent)
799 ct.IsMixed = true;
800 ct.Particle = ContentModel.CreateXsdParticle ();
801 el.SchemaType = ct;
804 return el;
806 #endif
809 internal class DTDAttributeDefinition : DTDNode
811 string name;
812 XmlSchemaDatatype datatype;
813 ArrayList enumeratedLiterals;
814 string unresolvedDefault;
815 ArrayList enumeratedNotations;
816 DTDAttributeOccurenceType occurenceType = DTDAttributeOccurenceType.None;
817 string resolvedDefaultValue;
818 string resolvedNormalizedDefaultValue;
820 internal DTDAttributeDefinition (DTDObjectModel root)
822 this.SetRoot (root);
825 public string Name {
826 get { return name; }
827 set { name =value; }
830 public XmlSchemaDatatype Datatype {
831 get { return datatype; }
832 set { datatype = value; }
835 public DTDAttributeOccurenceType OccurenceType {
836 get { return this.occurenceType; }
837 set { this.occurenceType = value; }
840 // entity reference inside enumerated values are not allowed,
841 // but on the other hand, they are allowed inside default value.
842 // Then I decided to use string ArrayList for enumerated values,
843 // and unresolved string value for DefaultValue.
844 public ArrayList EnumeratedAttributeDeclaration {
845 get {
846 if (enumeratedLiterals == null)
847 enumeratedLiterals = new ArrayList ();
848 return this.enumeratedLiterals;
852 public ArrayList EnumeratedNotations {
853 get {
854 if (enumeratedNotations == null)
855 enumeratedNotations = new ArrayList ();
856 return this.enumeratedNotations;
860 public string DefaultValue {
861 get {
862 if (resolvedDefaultValue == null)
863 resolvedDefaultValue = ComputeDefaultValue ();
864 return resolvedDefaultValue;
868 public string NormalizedDefaultValue {
869 get {
870 if (resolvedNormalizedDefaultValue == null) {
871 string s = ComputeDefaultValue ();
872 try {
873 object o = Datatype.ParseValue (s, null, null);
874 resolvedNormalizedDefaultValue =
875 (o is string []) ?
876 String.Join (" ", (string []) o) :
877 o is IFormattable ? ((IFormattable) o).ToString (null, CultureInfo.InvariantCulture) : o.ToString ();
878 } catch (Exception) {
879 // This is for non-error-reporting reader
880 resolvedNormalizedDefaultValue = Datatype.Normalize (s);
883 return resolvedNormalizedDefaultValue;
887 public string UnresolvedDefaultValue {
888 get { return this.unresolvedDefault; }
889 set { this.unresolvedDefault = value; }
892 public char QuoteChar {
893 get {
894 return UnresolvedDefaultValue.Length > 0 ?
895 this.UnresolvedDefaultValue [0] :
896 '"';
900 #if !NET_2_1
901 internal XmlSchemaAttribute CreateXsdAttribute ()
903 XmlSchemaAttribute a = new XmlSchemaAttribute ();
904 SetLineInfo (a);
905 a.Name = Name;
906 a.DefaultValue = resolvedNormalizedDefaultValue;
907 if (OccurenceType != DTDAttributeOccurenceType.Required)
908 a.Use = XmlSchemaUse.Optional;
910 XmlQualifiedName qname = XmlQualifiedName.Empty;
911 ArrayList enumeration = null;
912 if (enumeratedNotations != null && enumeratedNotations.Count > 0) {
913 qname = new XmlQualifiedName ("NOTATION", XmlSchema.Namespace);
914 enumeration = enumeratedNotations;
916 else if (enumeratedLiterals != null)
917 enumeration = enumeratedLiterals;
918 else {
919 switch (Datatype.TokenizedType) {
920 case XmlTokenizedType.ID:
921 qname = new XmlQualifiedName ("ID", XmlSchema.Namespace); break;
922 case XmlTokenizedType.IDREF:
923 qname = new XmlQualifiedName ("IDREF", XmlSchema.Namespace); break;
924 case XmlTokenizedType.IDREFS:
925 qname = new XmlQualifiedName ("IDREFS", XmlSchema.Namespace); break;
926 case XmlTokenizedType.ENTITY:
927 qname = new XmlQualifiedName ("ENTITY", XmlSchema.Namespace); break;
928 case XmlTokenizedType.ENTITIES:
929 qname = new XmlQualifiedName ("ENTITIES", XmlSchema.Namespace); break;
930 case XmlTokenizedType.NMTOKEN:
931 qname = new XmlQualifiedName ("NMTOKEN", XmlSchema.Namespace); break;
932 case XmlTokenizedType.NMTOKENS:
933 qname = new XmlQualifiedName ("NMTOKENS", XmlSchema.Namespace); break;
934 case XmlTokenizedType.NOTATION:
935 qname = new XmlQualifiedName ("NOTATION", XmlSchema.Namespace); break;
939 if (enumeration != null) {
940 XmlSchemaSimpleType st = new XmlSchemaSimpleType ();
941 SetLineInfo (st);
942 XmlSchemaSimpleTypeRestriction r =
943 new XmlSchemaSimpleTypeRestriction ();
944 SetLineInfo (r);
945 r.BaseTypeName = qname;
946 if (enumeratedNotations != null) {
947 foreach (string name in enumeratedNotations) {
948 XmlSchemaEnumerationFacet f =
949 new XmlSchemaEnumerationFacet ();
950 SetLineInfo (f);
951 r.Facets.Add (f);
952 f.Value = name;
955 st.Content = r;
957 else if (qname != XmlQualifiedName.Empty)
958 a.SchemaTypeName = qname;
959 return a;
961 #endif
963 internal string ComputeDefaultValue ()
965 if (UnresolvedDefaultValue == null)
966 return null;
968 StringBuilder sb = new StringBuilder ();
969 int pos = 0;
970 int next = 0;
971 string value = this.UnresolvedDefaultValue;
972 while ((next = value.IndexOf ('&', pos)) >= 0) {
973 int semicolon = value.IndexOf (';', next);
974 if (value [next + 1] == '#') {
975 // character reference.
976 char c = value [next + 2];
977 NumberStyles style = NumberStyles.Integer;
978 string spec;
979 if (c == 'x' || c == 'X') {
980 spec = value.Substring (next + 3, semicolon - next - 3);
981 style |= NumberStyles.HexNumber;
983 else
984 spec = value.Substring (next + 2, semicolon - next - 2);
985 sb.Append ((char) int.Parse (spec, style, CultureInfo.InvariantCulture));
986 } else {
987 sb.Append (value.Substring (pos, next - 1));
988 string name = value.Substring (next + 1, semicolon - 2);
989 int predefined = XmlChar.GetPredefinedEntity (name);
990 if (predefined >= 0)
991 sb.Append (predefined);
992 else
993 sb.Append (Root.ResolveEntity (name));
995 pos = semicolon + 1;
997 sb.Append (value.Substring (pos));
998 // strip quote chars
999 string ret = sb.ToString (1, sb.Length - 2);
1000 sb.Length = 0;
1001 return ret;
1006 internal class DTDAttListDeclaration : DTDNode
1008 string name;
1009 Hashtable attributeOrders = new Hashtable ();
1010 ArrayList attributes = new ArrayList ();
1012 internal DTDAttListDeclaration (DTDObjectModel root)
1014 SetRoot (root);
1017 public string Name {
1018 get { return name; }
1019 set { name = value; }
1022 public DTDAttributeDefinition this [int i] {
1023 get { return Get (i); }
1026 public DTDAttributeDefinition this [string name] {
1027 get { return Get (name); }
1030 public DTDAttributeDefinition Get (int i)
1032 return attributes [i] as DTDAttributeDefinition;
1035 public DTDAttributeDefinition Get (string name)
1037 object o = attributeOrders [name];
1038 if (o != null)
1039 return attributes [(int) o] as DTDAttributeDefinition;
1040 else
1041 return null;
1044 public IList Definitions {
1045 get { return attributes; }
1048 public void Add (DTDAttributeDefinition def)
1050 if (attributeOrders [def.Name] != null)
1051 throw new InvalidOperationException (String.Format (
1052 "Attribute definition for {0} was already added at element {1}.",
1053 def.Name, this.Name));
1054 def.SetRoot (Root);
1055 attributeOrders.Add (def.Name, attributes.Count);
1056 attributes.Add (def);
1059 public int Count {
1060 get { return attributeOrders.Count; }
1064 internal class DTDEntityBase : DTDNode
1066 string name;
1067 string publicId;
1068 string systemId;
1069 string literalValue;
1070 string replacementText;
1071 string uriString;
1072 Uri absUri;
1073 bool isInvalid;
1074 // Exception loadException;
1075 bool loadFailed;
1076 XmlResolver resolver;
1078 protected DTDEntityBase (DTDObjectModel root)
1080 SetRoot (root);
1083 internal bool IsInvalid {
1084 get { return isInvalid; }
1085 set { isInvalid = value; }
1088 public bool LoadFailed {
1089 get { return loadFailed; }
1090 set { loadFailed = value; }
1093 public string Name {
1094 get { return name; }
1095 set { name = value; }
1098 public string PublicId {
1099 get { return publicId; }
1100 set { publicId = value; }
1103 public string SystemId {
1104 get { return systemId; }
1105 set { systemId = value; }
1108 public string LiteralEntityValue {
1109 get { return literalValue; }
1110 set { literalValue = value; }
1113 public string ReplacementText {
1114 get { return replacementText; }
1115 set { replacementText = value; }
1118 public XmlResolver XmlResolver {
1119 set { resolver = value; }
1122 public string ActualUri {
1123 get {
1124 if (uriString == null) {
1125 if (resolver == null || SystemId == null || SystemId.Length == 0)
1126 uriString = BaseURI;
1127 else {
1128 Uri baseUri = null;
1129 try {
1130 if (BaseURI != null && BaseURI.Length > 0)
1131 baseUri = new Uri (BaseURI);
1132 } catch (UriFormatException) {
1135 absUri = resolver.ResolveUri (baseUri, SystemId);
1136 uriString = absUri != null ? absUri.ToString () : String.Empty;
1139 return uriString;
1143 public void Resolve ()
1145 if (ActualUri == String.Empty) {
1146 LoadFailed = true;
1147 LiteralEntityValue = String.Empty;
1148 return;
1151 if (Root.ExternalResources.ContainsKey (ActualUri))
1152 LiteralEntityValue = (string) Root.ExternalResources [ActualUri];
1153 Stream s = null;
1154 try {
1155 s = resolver.GetEntity (absUri, null, typeof (Stream)) as Stream;
1156 XmlTextReaderImpl xtr = new XmlTextReaderImpl (ActualUri, s, Root.NameTable);
1157 // Don't skip Text declaration here. LiteralEntityValue contains it. See spec 4.5
1158 LiteralEntityValue = xtr.GetRemainder ().ReadToEnd ();
1160 Root.ExternalResources.Add (ActualUri, LiteralEntityValue);
1161 if (Root.ExternalResources.Count > DTDObjectModel.AllowedExternalEntitiesMax)
1162 throw new InvalidOperationException ("The total amount of external entities exceeded the allowed number.");
1164 } catch (Exception) {
1165 // loadException = ex;
1166 LiteralEntityValue = String.Empty;
1167 LoadFailed = true;
1168 // throw NotWFError ("Cannot resolve external entity. URI is " + ActualUri + " .");
1169 } finally {
1170 if (s != null)
1171 s.Close ();
1176 internal class DTDEntityDeclaration : DTDEntityBase
1178 string entityValue;
1179 string notationName;
1181 ArrayList ReferencingEntities = new ArrayList ();
1183 bool scanned;
1184 bool recursed;
1185 bool hasExternalReference;
1187 internal DTDEntityDeclaration (DTDObjectModel root) : base (root)
1191 public string NotationName {
1192 get { return notationName; }
1193 set { notationName = value; }
1196 public bool HasExternalReference {
1197 get {
1198 if (!scanned)
1199 ScanEntityValue (new ArrayList ());
1200 return hasExternalReference;
1204 public string EntityValue {
1205 get {
1206 if (this.IsInvalid)
1207 return String.Empty;
1209 if (PublicId == null && SystemId == null && LiteralEntityValue == null)
1210 return String.Empty;
1212 if (entityValue == null) {
1213 if (NotationName != null)
1214 entityValue = "";
1215 else if (SystemId == null || SystemId == String.Empty) {
1216 entityValue = ReplacementText;
1217 if (entityValue == null)
1218 entityValue = String.Empty;
1219 } else {
1220 entityValue = ReplacementText;
1222 // Check illegal recursion.
1223 ScanEntityValue (new ArrayList ());
1225 return entityValue;
1229 // It returns whether the entity contains references to external entities.
1230 public void ScanEntityValue (ArrayList refs)
1232 // To modify this code, beware nesting between this and EntityValue.
1233 string value = EntityValue;
1234 if (this.SystemId != null)
1235 hasExternalReference = true;
1237 if (recursed)
1238 throw NotWFError ("Entity recursion was found.");
1239 recursed = true;
1241 if (scanned) {
1242 foreach (string referenced in refs)
1243 if (this.ReferencingEntities.Contains (referenced))
1244 throw NotWFError (String.Format (
1245 "Nested entity was found between {0} and {1}",
1246 referenced, Name));
1247 recursed = false;
1248 return;
1251 int len = value.Length;
1252 int start = 0;
1253 for (int i=0; i<len; i++) {
1254 switch (value [i]) {
1255 case '&':
1256 start = i+1;
1257 break;
1258 case ';':
1259 if (start == 0)
1260 break;
1261 string name = value.Substring (start, i - start);
1262 if (name.Length == 0)
1263 throw NotWFError ("Entity reference name is missing.");
1264 if (name [0] == '#')
1265 break; // character reference
1266 if (XmlChar.GetPredefinedEntity (name) >= 0)
1267 break; // predefined reference
1269 this.ReferencingEntities.Add (name);
1270 DTDEntityDeclaration decl = Root.EntityDecls [name];
1271 if (decl != null) {
1272 if (decl.SystemId != null)
1273 hasExternalReference = true;
1274 refs.Add (Name);
1275 decl.ScanEntityValue (refs);
1276 foreach (string str in decl.ReferencingEntities)
1277 ReferencingEntities.Add (str);
1278 refs.Remove (Name);
1279 value = value.Remove (start - 1, name.Length + 2);
1280 value = value.Insert (start - 1, decl.EntityValue);
1281 i -= name.Length + 1; // not +2, because of immediate i++ .
1282 len = value.Length;
1284 start = 0;
1285 break;
1288 if (start != 0)
1289 #if NET_2_1
1290 Root.AddError (new XmlSchemaException (this, this.BaseURI, "Invalid reference character '&' is specified."));
1291 #else
1292 Root.AddError (new XmlSchemaException ("Invalid reference character '&' is specified.",
1293 this.LineNumber, this.LinePosition, null, this.BaseURI, null));
1294 #endif
1295 scanned = true;
1296 recursed = false;
1300 internal class DTDNotationDeclaration : DTDNode
1302 string name;
1303 string localName;
1304 string prefix;
1305 string publicId;
1306 string systemId;
1308 public string Name {
1309 get { return name; }
1310 set { name = value; }
1313 public string PublicId {
1314 get { return publicId; }
1315 set { publicId = value; }
1318 public string SystemId {
1319 get { return systemId; }
1320 set { systemId = value; }
1323 public string LocalName {
1324 get { return localName; }
1325 set { localName = value; }
1328 public string Prefix {
1329 get { return prefix; }
1330 set { prefix = value; }
1333 internal DTDNotationDeclaration (DTDObjectModel root)
1335 SetRoot (root);
1339 internal class DTDParameterEntityDeclarationCollection
1341 Hashtable peDecls = new Hashtable ();
1342 DTDObjectModel root;
1344 public DTDParameterEntityDeclarationCollection (DTDObjectModel root)
1346 this.root = root;
1349 public DTDParameterEntityDeclaration this [string name] {
1350 get { return peDecls [name] as DTDParameterEntityDeclaration; }
1353 public void Add (string name, DTDParameterEntityDeclaration decl)
1355 // PEDecl can be overriden.
1356 if (peDecls [name] != null)
1357 return;
1358 decl.SetRoot (root);
1359 peDecls.Add (name, decl);
1362 public ICollection Keys {
1363 get { return peDecls.Keys; }
1366 public ICollection Values {
1367 get { return peDecls.Values; }
1371 internal class DTDParameterEntityDeclaration : DTDEntityBase
1373 internal DTDParameterEntityDeclaration (DTDObjectModel root) : base (root)
1378 internal enum DTDContentOrderType
1380 None,
1381 Seq,
1385 internal enum DTDAttributeOccurenceType
1387 None,
1388 Required,
1389 Optional,
1390 Fixed
1393 internal enum DTDOccurence
1395 One,
1396 Optional,
1397 ZeroOrMore,
1398 OneOrMore