* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / System.XML / System.Xml / DTDObjectModel.cs
1 //
2 // Mono.Xml.DTDObjectModel
3 //
4 // Author:
5 //      Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
6 //
7 //      (C)2003 Atsushi Enomoto
8 //
9
10 //
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.
29 //
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 using Mono.Xml.Schema;
38
39 #if NET_2_0
40 using XmlTextReaderImpl = Mono.Xml2.XmlTextReader;
41 #else
42 using XmlTextReaderImpl = System.Xml.XmlTextReader;
43 #endif
44
45 namespace Mono.Xml
46 {
47         internal class DTDObjectModel
48         {
49                 // This specifies the max number of dependent external entities
50                 // per a DTD can consume. A malicious external document server
51                 // might send users' document processing server a large number
52                 // of external entities.
53                 public const int AllowedExternalEntitiesMax = 256;
54
55                 DTDAutomataFactory factory;
56                 DTDElementAutomata rootAutomata;
57                 DTDEmptyAutomata emptyAutomata;
58                 DTDAnyAutomata anyAutomata;
59                 DTDInvalidAutomata invalidAutomata;
60
61                 DTDElementDeclarationCollection elementDecls;
62                 DTDAttListDeclarationCollection attListDecls;
63                 DTDParameterEntityDeclarationCollection peDecls;
64                 DTDEntityDeclarationCollection entityDecls;
65                 DTDNotationDeclarationCollection notationDecls;
66                 ArrayList validationErrors;
67                 XmlResolver resolver;
68                 XmlNameTable nameTable;
69
70                 Hashtable externalResources;
71
72                 string baseURI;
73                 string name;
74                 string publicId;
75                 string systemId;
76                 string intSubset;
77                 bool intSubsetHasPERef;
78                 bool isStandalone;
79                 int lineNumber;
80                 int linePosition;
81
82                 public DTDObjectModel (XmlNameTable nameTable)
83                 {
84                         this.nameTable = nameTable;
85                         elementDecls = new DTDElementDeclarationCollection (this);
86                         attListDecls = new DTDAttListDeclarationCollection (this);
87                         entityDecls = new DTDEntityDeclarationCollection (this);
88                         peDecls = new DTDParameterEntityDeclarationCollection (this);
89                         notationDecls = new DTDNotationDeclarationCollection (this);
90                         factory = new DTDAutomataFactory (this);
91                         validationErrors = new ArrayList ();
92                         externalResources = new Hashtable ();
93                 }
94
95                 public string BaseURI {
96                         get { return baseURI; }
97                         set { baseURI = value; }
98                 }
99
100                 public bool IsStandalone {
101                         get { return isStandalone; }
102                         set { isStandalone = value; }
103                 }
104
105                 public string Name {
106                         get { return name; }
107                         set { name = value; }
108                 }
109
110                 public XmlNameTable NameTable {
111                         get { return nameTable; }
112                 }
113                 
114                 public string PublicId {
115                         get { return publicId; }
116                         set { publicId = value; }
117                 }
118
119                 public string SystemId {
120                         get { return systemId; }
121                         set { systemId = value; }
122                 }
123                 
124                 public string InternalSubset {
125                         get { return intSubset; }
126                         set { intSubset = value; }
127                 }
128
129                 public bool InternalSubsetHasPEReference {
130                         get { return intSubsetHasPERef; }
131                         set { intSubsetHasPERef = value; }
132                 }
133
134                 public int LineNumber {
135                         get { return lineNumber; }
136                         set { lineNumber = value; }
137                 }
138
139                 public int LinePosition {
140                         get { return linePosition; }
141                         set { linePosition = value; }
142                 }
143
144                 internal XmlSchema CreateXsdSchema ()
145                 {
146                         XmlSchema s = new XmlSchema ();
147                         s.SourceUri = BaseURI;
148                         s.LineNumber = LineNumber;
149                         s.LinePosition = LinePosition;
150                         foreach (DTDElementDeclaration el in ElementDecls.Values)
151                                 s.Items.Add (el.CreateXsdElement ());
152                         return s;
153                 }
154
155                 public string ResolveEntity (string name)
156                 {
157                         DTDEntityDeclaration decl = EntityDecls [name] 
158                                 as DTDEntityDeclaration;
159                         if (decl == null) {
160                                 AddError (new XmlSchemaException ("Required entity was not found.",
161                                         this.LineNumber, this.LinePosition, null, this.BaseURI, null));
162                                 return " ";
163                         }
164                         else
165                                 return decl.EntityValue;
166                 }
167
168                 internal XmlResolver Resolver {
169                         get { return resolver; }
170                 }
171
172                 public XmlResolver XmlResolver {
173                         set { resolver = value; }
174                 }
175
176                 internal Hashtable ExternalResources {
177                         get { return externalResources; }
178                 }
179
180                 public DTDAutomataFactory Factory {
181                         get { return factory; }
182                 }
183
184                 public DTDElementDeclaration RootElement {
185                         get { return ElementDecls [Name]; }
186                 }
187
188                 public DTDElementDeclarationCollection ElementDecls {
189                         get { return elementDecls; }
190                 }
191
192                 public DTDAttListDeclarationCollection AttListDecls {
193                         get { return attListDecls; }
194                 }
195
196                 public DTDEntityDeclarationCollection EntityDecls {
197                         get { return entityDecls; }
198                 }
199
200                 public DTDParameterEntityDeclarationCollection PEDecls {
201                         get { return peDecls; }
202                 }
203
204                 public DTDNotationDeclarationCollection NotationDecls {
205                         get { return notationDecls; }
206                 }
207
208                 public DTDAutomata RootAutomata {
209                         get {
210                                 if (rootAutomata == null)
211                                         rootAutomata = new DTDElementAutomata (this, this.Name);
212                                 return rootAutomata;
213                         }
214                 }
215
216                 public DTDEmptyAutomata Empty {
217                         get {
218                                 if (emptyAutomata == null)
219                                         emptyAutomata = new DTDEmptyAutomata (this);
220                                 return emptyAutomata;
221                         }
222                 }
223
224                 public DTDAnyAutomata Any {
225                         get {
226                                 if (anyAutomata == null)
227                                         anyAutomata = new DTDAnyAutomata (this);
228                                 return anyAutomata;
229                         }
230                 }
231
232                 public DTDInvalidAutomata Invalid {
233                         get {
234                                 if (invalidAutomata == null)
235                                         invalidAutomata = new DTDInvalidAutomata (this);
236                                 return invalidAutomata;
237                         }
238                 }
239
240                 public XmlSchemaException [] Errors {
241                         get { return validationErrors.ToArray (typeof (XmlSchemaException)) as XmlSchemaException []; }
242                 }
243
244                 public void AddError (XmlSchemaException ex)
245                 {
246                         validationErrors.Add (ex);
247                 }
248
249                 internal string GenerateEntityAttributeText (string entityName)
250                 {
251                         DTDEntityDeclaration entity = EntityDecls [entityName] as DTDEntityDeclaration;
252                         if (entity == null)
253                                 return null;
254                         return entity.EntityValue;
255                 }
256
257                 internal XmlTextReaderImpl GenerateEntityContentReader (string entityName, XmlParserContext context)
258                 {
259                         DTDEntityDeclaration entity = EntityDecls [entityName] as DTDEntityDeclaration;
260                         if (entity == null)
261                                 return null;
262
263                         if (entity.SystemId != null) {
264                                 Uri baseUri = entity.BaseURI == String.Empty ? null : new Uri (entity.BaseURI);
265                                 Stream stream = resolver.GetEntity (resolver.ResolveUri (baseUri, entity.SystemId), null, typeof (Stream)) as Stream;
266                                 return new XmlTextReaderImpl (stream, XmlNodeType.Element, context);
267                         }
268                         else
269                                 return new XmlTextReaderImpl (entity.EntityValue, XmlNodeType.Element, context);
270                 }
271         }
272
273         internal class DTDCollectionBase : DictionaryBase
274         {
275                 DTDObjectModel root;
276
277                 protected DTDCollectionBase (DTDObjectModel root)
278                 {
279                         this.root = root;
280                 }
281
282                 protected DTDObjectModel Root {
283                         get { return root; }
284                 }
285
286                 public ICollection Keys {
287                         get { return InnerHashtable.Keys; }
288                 }
289
290                 public ICollection Values {
291                         get { return InnerHashtable.Values; }
292                 }
293         }
294
295         internal class DTDElementDeclarationCollection : DTDCollectionBase
296         {
297
298                 public DTDElementDeclarationCollection (DTDObjectModel root) : base (root) {}
299
300                 public DTDElementDeclaration this [string name] {
301                         get { return Get (name); }
302                 }
303
304                 public DTDElementDeclaration Get (string name)
305                 {
306                         return InnerHashtable [name] as DTDElementDeclaration;
307                 }
308
309                 public void Add (string name, DTDElementDeclaration decl)
310                 {
311                         if (InnerHashtable.Contains (name)) {
312                                 Root.AddError (new XmlSchemaException (String.Format (
313                                         "Element declaration for {0} was already added.",
314                                         name), null));
315                                 return;
316                         }
317                         decl.SetRoot (Root);
318                         InnerHashtable.Add (name, decl);
319                 }
320         }
321
322         internal class DTDAttListDeclarationCollection : DTDCollectionBase
323         {
324                 public DTDAttListDeclarationCollection (DTDObjectModel root) : base (root) {}
325
326                 public DTDAttListDeclaration this [string name] {
327                         get { return InnerHashtable [name] as DTDAttListDeclaration; }
328                 }
329
330                 public void Add (string name, DTDAttListDeclaration decl)
331                 {
332                         DTDAttListDeclaration existing = this [name];
333                         if (existing != null) {
334                                 // It is valid, that is additive declaration.
335                                 foreach (DTDAttributeDefinition def in decl.Definitions)
336                                         if (decl.Get (def.Name) == null)
337                                                 existing.Add (def);
338                         } else {
339                                 decl.SetRoot (Root);
340                                 InnerHashtable.Add (name, decl);
341                         }
342                 }
343         }
344
345         internal class DTDEntityDeclarationCollection : DTDCollectionBase
346         {
347                 public DTDEntityDeclarationCollection (DTDObjectModel root) : base (root) {}
348
349                 public DTDEntityDeclaration this [string name] {
350                         get { return InnerHashtable [name] as DTDEntityDeclaration; }
351                 }
352
353                 public void Add (string name, DTDEntityDeclaration decl)
354                 {
355                         if (InnerHashtable [name] != null)
356                                 throw new InvalidOperationException (String.Format (
357                                         "Entity declaration for {0} was already added.",
358                                         name));
359                         decl.SetRoot (Root);
360                         InnerHashtable.Add (name, decl);
361                 }
362         }
363
364         internal class DTDNotationDeclarationCollection : DTDCollectionBase
365         {
366                 public DTDNotationDeclarationCollection (DTDObjectModel root) : base (root) {}
367
368                 public DTDNotationDeclaration this [string name] {
369                         get { return InnerHashtable [name] as DTDNotationDeclaration; }
370                 }
371
372                 public void Add (string name, DTDNotationDeclaration decl)
373                 {
374                         if (InnerHashtable [name] != null)
375                                 throw new InvalidOperationException (String.Format (
376                                         "Notation declaration for {0} was already added.",
377                                         name));
378                         decl.SetRoot (Root);
379                         InnerHashtable.Add (name, decl);
380                 }
381         }
382
383         // This class contains either ElementName or ChildModels.
384         internal class DTDContentModel : DTDNode
385         {
386                 DTDObjectModel root;
387                 DTDAutomata compiledAutomata;
388
389                 string ownerElementName;
390                 string elementName;
391                 DTDContentOrderType orderType = DTDContentOrderType.None;
392                 DTDContentModelCollection childModels = new DTDContentModelCollection ();
393                 DTDOccurence occurence = DTDOccurence.One;
394
395                 internal DTDContentModel (DTDObjectModel root, string ownerElementName)
396                 {
397                         this.root = root;
398                         this.ownerElementName = ownerElementName;
399                 }
400
401                 public DTDContentModelCollection ChildModels {
402                         get { return childModels; }
403                         set { childModels = value; }
404                 }
405
406                 public DTDElementDeclaration ElementDecl {
407                         get { return root.ElementDecls [ownerElementName]; }
408                 }
409
410                 public string ElementName {
411                         get { return elementName; }
412                         set { elementName = value; }
413                 }
414
415                 public DTDOccurence Occurence {
416                         get { return occurence; }
417                         set { occurence = value; }
418                 }
419
420                 public DTDContentOrderType OrderType {
421                         get { return orderType; }
422                         set { orderType = value; }
423                 }
424
425                 public DTDAutomata GetAutomata ()
426                 {
427                         if (compiledAutomata == null)
428                                 Compile ();
429                         return compiledAutomata;
430                 }
431
432                 public DTDAutomata Compile ()
433                 {
434                         compiledAutomata = CompileInternal ();
435                         return compiledAutomata;
436                 }
437
438                 internal XmlSchemaParticle CreateXsdParticle ()
439                 {
440                         XmlSchemaParticle p = CreateXsdParticleCore ();
441                         if (p == null)
442                                 return null;
443
444                         switch (Occurence) {
445                         case DTDOccurence.Optional:
446                                 p.MinOccurs = 0;
447                                 break;
448                         case DTDOccurence.OneOrMore:
449                                 p.MaxOccursString = "unbounded";
450                                 break;
451                         case DTDOccurence.ZeroOrMore:
452                                 p.MinOccurs = 0;
453                                 p.MaxOccursString = "unbounded";
454                                 break;
455                         }
456                         return p;
457                 }
458
459                 XmlSchemaParticle CreateXsdParticleCore ()
460                 {
461                         XmlSchemaParticle p = null;
462                         if (ElementName != null) {
463                                 XmlSchemaElement el = new XmlSchemaElement ();
464                                 SetLineInfo (el);
465                                 el.RefName = new XmlQualifiedName (ElementName);
466                                 return el;
467                         }
468                         else if (ChildModels.Count == 0)
469                                 return null;
470                         else {
471                                 XmlSchemaGroupBase gb = 
472                                         (OrderType == DTDContentOrderType.Seq) ?
473                                                 (XmlSchemaGroupBase)
474                                                 new XmlSchemaSequence () :
475                                                 new XmlSchemaChoice ();
476                                 SetLineInfo (gb);
477                                 foreach (DTDContentModel cm in ChildModels.Items) {
478                                         XmlSchemaParticle c = cm.CreateXsdParticle ();
479                                         if (c != null)
480                                                 gb.Items.Add (c);
481                                 }
482                                 p = gb;
483                         }
484                         return p;
485                 }
486
487                 private DTDAutomata CompileInternal ()
488                 {
489                         if (ElementDecl.IsAny)
490                                 return root.Any;
491                         if (ElementDecl.IsEmpty)
492                                 return root.Empty;
493
494                         DTDAutomata basis = GetBasicContentAutomata ();
495                         switch (Occurence) {
496                         case DTDOccurence.One:
497                                 return basis;
498                         case DTDOccurence.Optional:
499                                 return Choice (root.Empty, basis);
500                         case DTDOccurence.OneOrMore:
501                                 return new DTDOneOrMoreAutomata (root, basis);
502                         case DTDOccurence.ZeroOrMore:
503                                 return Choice (root.Empty, new DTDOneOrMoreAutomata (root, basis));
504                         }
505                         throw new InvalidOperationException ();
506                 }
507
508                 private DTDAutomata GetBasicContentAutomata ()
509                 {
510                         if (ElementName != null)
511                                 return new DTDElementAutomata (root, ElementName);
512                         switch (ChildModels.Count) {
513                         case 0:
514                                 return root.Empty;
515                         case 1:
516                                 return ChildModels [0].GetAutomata ();
517                         }
518
519                         DTDAutomata current = null;
520                         int childCount = ChildModels.Count;
521                         switch (OrderType) {
522                         case DTDContentOrderType.Seq:
523                                 current = Sequence (
524                                         ChildModels [childCount - 2].GetAutomata (),
525                                         ChildModels [childCount - 1].GetAutomata ());
526                                 for (int i = childCount - 2; i > 0; i--)
527                                         current = Sequence (
528                                                 ChildModels [i - 1].GetAutomata (), current);
529                                 return current;
530                         case DTDContentOrderType.Or:
531                                 current = Choice (
532                                         ChildModels [childCount - 2].GetAutomata (),
533                                         ChildModels [childCount - 1].GetAutomata ());
534                                 for (int i = childCount - 2; i > 0; i--)
535                                         current = Choice (
536                                                 ChildModels [i - 1].GetAutomata (), current);
537                                 return current;
538                         default:
539                                 throw new InvalidOperationException ("Invalid pattern specification");
540                         }
541                 }
542
543                 private DTDAutomata Sequence (DTDAutomata l, DTDAutomata r)
544                 {
545                         return root.Factory.Sequence (l, r);
546                 }
547
548                 private DTDAutomata Choice (DTDAutomata l, DTDAutomata r)
549                 {
550                         return l.MakeChoice (r);
551                 }
552         }
553
554         internal class DTDContentModelCollection
555         {
556                 ArrayList contentModel = new ArrayList ();
557
558                 public DTDContentModelCollection ()
559                 {
560                 }
561
562                 public IList Items {
563                         get { return contentModel; }
564                 }
565
566                 public DTDContentModel this [int i] {
567                         get { return contentModel [i] as DTDContentModel; }
568                 }
569
570                 public int Count {
571                         get { return contentModel.Count; }
572                 }
573
574                 public void Add (DTDContentModel model)
575                 {
576                         contentModel.Add (model);
577                 }
578         }
579
580         internal abstract class DTDNode : IXmlLineInfo
581         {
582                 DTDObjectModel root;
583                 bool isInternalSubset;
584                 string baseURI;
585                 int lineNumber;
586                 int linePosition;
587
588                 public virtual string BaseURI {
589                         get { return baseURI; }
590                         set { baseURI = value; }
591                 }
592
593                 public bool IsInternalSubset {
594                         get { return isInternalSubset; }
595                         set { isInternalSubset = value; }
596                 }
597
598                 public int LineNumber {
599                         get { return lineNumber; }
600                         set { lineNumber = value; }
601                 }
602
603                 public int LinePosition {
604                         get { return linePosition; }
605                         set { linePosition = value; }
606                 }
607
608                 public bool HasLineInfo ()
609                 {
610                         return lineNumber != 0;
611                 }
612
613                 internal void SetRoot (DTDObjectModel root)
614                 {
615                         this.root = root;
616                         if (baseURI == null)
617                                 this.BaseURI = root.BaseURI;
618                 }
619
620                 protected DTDObjectModel Root {
621                         get { return root; }
622                 }
623
624                 internal XmlException NotWFError (string message)
625                 {
626                         return new XmlException (this as IXmlLineInfo, BaseURI, message);
627                 }
628
629                 public void SetLineInfo (XmlSchemaObject obj)
630                 {
631                         obj.SourceUri = BaseURI;
632                         obj.LineNumber = LineNumber;
633                         obj.LinePosition = LinePosition;
634                 }
635         }
636
637         internal class DTDElementDeclaration : DTDNode
638         {
639                 DTDObjectModel root;
640                 DTDContentModel contentModel;
641                 string name;
642                 bool isEmpty;
643                 bool isAny;
644                 bool isMixedContent;
645
646                 internal DTDElementDeclaration (DTDObjectModel root)
647                 {
648                         this.root = root;
649                 }
650
651                 public string Name {
652                         get { return name; }
653                         set { name = value; }
654                 }
655                 public bool IsEmpty {
656                         get { return isEmpty; }
657                         set { isEmpty = value; }
658                 }
659
660                 public bool IsAny {
661                         get { return isAny; }
662                         set { isAny = value; }
663                 }
664
665                 public bool IsMixedContent {
666                         get { return isMixedContent; }
667                         set { isMixedContent = value; }
668                 }
669
670                 public DTDContentModel ContentModel {
671                         get {
672                                 if (contentModel == null)
673                                         contentModel = new DTDContentModel (root, Name);
674                                 return contentModel;
675                         }
676                 }
677
678                 public DTDAttListDeclaration Attributes {
679                         get {
680                                 return Root.AttListDecls [Name];
681                         }
682                 }
683
684                 internal XmlSchemaElement CreateXsdElement ()
685                 {
686                         XmlSchemaElement el = new XmlSchemaElement ();
687                         SetLineInfo (el);
688                         el.Name = Name;
689
690                         XmlSchemaComplexType ct = new XmlSchemaComplexType ();
691                         el.SchemaType = ct;
692                         if (Attributes != null) {
693                                 SetLineInfo (ct);
694                                 foreach (DTDAttributeDefinition a in
695                                         Attributes.Definitions)
696                                         ct.Attributes.Add (a.CreateXsdAttribute ());
697                         }
698                         if (IsEmpty)
699                                 ; // nothing to do
700                         else if (IsAny) {
701                                 XmlSchemaAny any = new XmlSchemaAny ();
702                                 any.MinOccurs = 0;
703                                 any.MaxOccursString = "unbounded";
704                                 ct.Particle = any;
705                         }
706                         else {
707                                 if (IsMixedContent)
708                                         ct.IsMixed = true;
709                                 ct.Particle = ContentModel.CreateXsdParticle ();
710                         }
711
712                         /*
713                         if (IsEmpty) {
714                                 el.SchemaType = new XmlSchemaComplexType ();
715                                 SetLineInfo (el.SchemaType);
716                         }
717                         else if (IsAny)
718                                 el.SchemaTypeName = new XmlQualifiedName (
719                                         "anyType", XmlSchema.Namespace);
720                         else {
721                                 XmlSchemaComplexType ct = new XmlSchemaComplexType ();
722                                 SetLineInfo (ct);
723                                 if (Attributes != null)
724                                         foreach (DTDAttributeDefinition a in
725                                                 Attributes.Definitions)
726                                                 ct.Attributes.Add (a.CreateXsdAttribute ());
727                                 if (IsMixedContent)
728                                         ct.IsMixed = true;
729                                 ct.Particle = ContentModel.CreateXsdParticle ();
730                                 el.SchemaType = ct;
731                         }
732                         */
733                         return el;
734                 }
735         }
736
737         internal class DTDAttributeDefinition : DTDNode
738         {
739                 string name;
740                 XmlSchemaDatatype datatype;
741                 ArrayList enumeratedLiterals;
742                 string unresolvedDefault;
743                 ArrayList enumeratedNotations;
744                 DTDAttributeOccurenceType occurenceType = DTDAttributeOccurenceType.None;
745                 string resolvedDefaultValue;
746                 string resolvedNormalizedDefaultValue;
747
748                 internal DTDAttributeDefinition (DTDObjectModel root)
749                 {
750                         this.SetRoot (root);
751                 }
752
753                 public string Name {
754                         get { return name; }
755                         set { name =value; }
756                 }
757
758                 public XmlSchemaDatatype Datatype {
759                         get { return datatype; }
760                         set { datatype = value; }
761                 }
762
763                 public DTDAttributeOccurenceType OccurenceType {
764                         get { return this.occurenceType; }
765                         set { this.occurenceType = value; }
766                 }
767
768                 // entity reference inside enumerated values are not allowed,
769                 // but on the other hand, they are allowed inside default value.
770                 // Then I decided to use string ArrayList for enumerated values,
771                 // and unresolved string value for DefaultValue.
772                 public ArrayList EnumeratedAttributeDeclaration {
773                         get {
774                                 if (enumeratedLiterals == null)
775                                         enumeratedLiterals = new ArrayList ();
776                                 return this.enumeratedLiterals;
777                         }
778                 }
779
780                 public ArrayList EnumeratedNotations {
781                         get {
782                                 if (enumeratedNotations == null)
783                                         enumeratedNotations = new ArrayList ();
784                                 return this.enumeratedNotations;
785                         }
786                 }
787
788                 public string DefaultValue {
789                         get {
790                                 if (resolvedDefaultValue == null)
791                                         resolvedDefaultValue = ComputeDefaultValue ();
792                                 return resolvedDefaultValue;
793                         }
794                 }
795
796                 public string NormalizedDefaultValue {
797                         get {
798                                 if (resolvedNormalizedDefaultValue == null) {
799                                         string s = ComputeDefaultValue ();
800                                         try {
801                                                 object o = Datatype.ParseValue (s, null, null);
802                                                 resolvedNormalizedDefaultValue = 
803                                                         (o is string []) ? 
804                                                         String.Join (" ", (string []) o) :
805                                                         o is IFormattable ? ((IFormattable) o).ToString (null, CultureInfo.InvariantCulture) : o.ToString ();
806                                         } catch (Exception) {
807                                                 // This is for non-error-reporting reader
808                                                 resolvedNormalizedDefaultValue = Datatype.Normalize (s);
809                                         }
810                                 }
811                                 return resolvedNormalizedDefaultValue;
812                         }
813                 }
814
815                 public string UnresolvedDefaultValue {
816                         get { return this.unresolvedDefault; }
817                         set { this.unresolvedDefault = value; }
818                 }
819
820                 public char QuoteChar {
821                         get {
822                                 return UnresolvedDefaultValue.Length > 0 ?
823                                         this.UnresolvedDefaultValue [0] :
824                                         '"';
825                         }
826                 }
827
828                 internal XmlSchemaAttribute CreateXsdAttribute ()
829                 {
830                         XmlSchemaAttribute a = new XmlSchemaAttribute ();
831                         SetLineInfo (a);
832                         a.Name = Name;
833                         a.DefaultValue = resolvedNormalizedDefaultValue;
834                         if (OccurenceType != DTDAttributeOccurenceType.Required)
835                                 a.Use = XmlSchemaUse.Optional;
836
837                         XmlQualifiedName qname = XmlQualifiedName.Empty;
838                         ArrayList enumeration = null;
839                         if (enumeratedNotations != null && enumeratedNotations.Count > 0) {
840                                 qname = new XmlQualifiedName ("NOTATION", XmlSchema.Namespace);
841                                 enumeration = enumeratedNotations;
842                         }
843                         else if (enumeratedLiterals != null)
844                                 enumeration = enumeratedLiterals;
845                         else {
846                                 switch (Datatype.TokenizedType) {
847                                 case XmlTokenizedType.ID:
848                                         qname = new XmlQualifiedName ("ID", XmlSchema.Namespace); break;
849                                 case XmlTokenizedType.IDREF:
850                                         qname = new XmlQualifiedName ("IDREF", XmlSchema.Namespace); break;
851                                 case XmlTokenizedType.IDREFS:
852                                         qname = new XmlQualifiedName ("IDREFS", XmlSchema.Namespace); break;
853                                 case XmlTokenizedType.ENTITY:
854                                         qname = new XmlQualifiedName ("ENTITY", XmlSchema.Namespace); break;
855                                 case XmlTokenizedType.ENTITIES:
856                                         qname = new XmlQualifiedName ("ENTITIES", XmlSchema.Namespace); break;
857                                 case XmlTokenizedType.NMTOKEN:
858                                         qname = new XmlQualifiedName ("NMTOKEN", XmlSchema.Namespace); break;
859                                 case XmlTokenizedType.NMTOKENS:
860                                         qname = new XmlQualifiedName ("NMTOKENS", XmlSchema.Namespace); break;
861                                 case XmlTokenizedType.NOTATION:
862                                         qname = new XmlQualifiedName ("NOTATION", XmlSchema.Namespace); break;
863                                 }
864                         }
865
866                         if (enumeration != null) {
867                                 XmlSchemaSimpleType st = new XmlSchemaSimpleType ();
868                                 SetLineInfo (st);
869                                 XmlSchemaSimpleTypeRestriction r =
870                                         new XmlSchemaSimpleTypeRestriction ();
871                                 SetLineInfo (r);
872                                 r.BaseTypeName = qname;
873                                 if (enumeratedNotations != null) {
874                                         foreach (string name in enumeratedNotations) {
875                                                 XmlSchemaEnumerationFacet f =
876                                                         new XmlSchemaEnumerationFacet ();
877                                                 SetLineInfo (f);
878                                                 r.Facets.Add (f);
879                                                 f.Value = name;
880                                         }
881                                 }
882                                 st.Content = r;
883                         }
884                         else if (qname != XmlQualifiedName.Empty)
885                                 a.SchemaTypeName = qname;
886                         return a;
887                 }
888
889                 internal string ComputeDefaultValue ()
890                 {
891                         if (UnresolvedDefaultValue == null)
892                                 return null;
893
894                         StringBuilder sb = new StringBuilder ();
895                         int pos = 0;
896                         int next = 0;
897                         string value = this.UnresolvedDefaultValue;
898                         while ((next = value.IndexOf ('&', pos)) >= 0) {
899                                 int semicolon = value.IndexOf (';', next);
900                                 if (value [next + 1] == '#') {
901                                         // character reference.
902                                         char c = value [next + 2];
903                                         NumberStyles style = NumberStyles.Integer;
904                                         string spec;
905                                         if (c == 'x' || c == 'X') {
906                                                 spec = value.Substring (next + 3, semicolon - next - 3);
907                                                 style |= NumberStyles.HexNumber;
908                                         }
909                                         else
910                                                 spec = value.Substring (next + 2, semicolon - next - 2);
911                                         sb.Append ((char) int.Parse (spec, style, CultureInfo.InvariantCulture));
912                                 } else {
913                                         sb.Append (value.Substring (pos, next - 1));
914                                         string name = value.Substring (next + 1, semicolon - 2);
915                                         int predefined = XmlChar.GetPredefinedEntity (name);
916                                         if (predefined >= 0)
917                                                 sb.Append (predefined);
918                                         else
919                                                 sb.Append (Root.ResolveEntity (name));
920                                 }
921                                 pos = semicolon + 1;
922                         }
923                         sb.Append (value.Substring (pos));
924                         // strip quote chars
925                         string ret = sb.ToString (1, sb.Length - 2);
926                         sb.Length = 0;
927                         return ret;
928                 }
929
930         }
931
932         internal class DTDAttListDeclaration : DTDNode
933         {
934                 string name;
935                 Hashtable attributeOrders = new Hashtable ();
936                 ArrayList attributes = new ArrayList ();
937
938                 internal DTDAttListDeclaration (DTDObjectModel root)
939                 {
940                         SetRoot (root);
941                 }
942
943                 public string Name {
944                         get { return name; }
945                         set { name = value; }
946                 }
947
948                 public DTDAttributeDefinition this [int i] {
949                         get { return Get (i); }
950                 }
951
952                 public DTDAttributeDefinition this [string name] {
953                         get { return Get (name); }
954                 }
955
956                 public DTDAttributeDefinition Get (int i)
957                 {
958                         return attributes [i] as DTDAttributeDefinition;
959                 }
960
961                 public DTDAttributeDefinition Get (string name)
962                 {
963                         object o = attributeOrders [name];
964                         if (o != null)
965                                 return attributes [(int) o] as DTDAttributeDefinition;
966                         else
967                                 return null;
968                 }
969
970                 public IList Definitions {
971                         get { return attributes; }
972                 }
973
974                 public void Add (DTDAttributeDefinition def)
975                 {
976                         if (attributeOrders [def.Name] != null)
977                                 throw new InvalidOperationException (String.Format (
978                                         "Attribute definition for {0} was already added at element {1}.",
979                                         def.Name, this.Name));
980                         def.SetRoot (Root);
981                         attributeOrders.Add (def.Name, attributes.Count);
982                         attributes.Add (def);
983                 }
984
985                 public int Count {
986                         get { return attributeOrders.Count; }
987                 }
988         }
989
990         internal class DTDEntityBase : DTDNode
991         {
992                 string name;
993                 string publicId;
994                 string systemId;
995                 string literalValue;
996                 string replacementText;
997                 string uriString;
998                 Uri absUri;
999                 bool isInvalid;
1000 //              Exception loadException;
1001                 bool loadFailed;
1002                 XmlResolver resolver;
1003
1004                 protected DTDEntityBase (DTDObjectModel root)
1005                 {
1006                         SetRoot (root);
1007                 }
1008
1009                 internal bool IsInvalid {
1010                         get { return isInvalid; }
1011                         set { isInvalid = value; }
1012                 }
1013
1014                 public bool LoadFailed {
1015                         get { return loadFailed; }
1016                         set { loadFailed = value; }
1017                 }
1018
1019                 public string Name {
1020                         get { return name; }
1021                         set { name = value; }
1022                 }
1023
1024                 public string PublicId {
1025                         get { return publicId; }
1026                         set { publicId = value; }
1027                 }
1028
1029                 public string SystemId {
1030                         get { return systemId; }
1031                         set { systemId = value; }
1032                 }
1033
1034                 public string LiteralEntityValue {
1035                         get { return literalValue; }
1036                         set { literalValue = value; }
1037                 }
1038
1039                 public string ReplacementText {
1040                         get { return replacementText; }
1041                         set { replacementText = value; }
1042                 }
1043
1044                 public XmlResolver XmlResolver {
1045                         set { resolver = value; }
1046                 }
1047
1048                 public string ActualUri {
1049                         get {
1050                                 if (uriString == null) {
1051                                         if (resolver == null || SystemId == null || SystemId.Length == 0)
1052                                                 uriString = BaseURI;
1053                                         else {
1054                                                 Uri baseUri = null;
1055                                                 try {
1056                                                         if (BaseURI != null && BaseURI.Length > 0)
1057                                                                 baseUri = new Uri (BaseURI);
1058                                                 } catch (UriFormatException) {
1059                                                 }
1060
1061                                                 absUri = resolver.ResolveUri (baseUri, SystemId);
1062                                                 uriString = absUri != null ? absUri.ToString () : String.Empty;
1063                                         }
1064                                 }
1065                                 return uriString;
1066                         }
1067                 }
1068
1069                 public void Resolve ()
1070                 {
1071                         if (ActualUri == String.Empty) {
1072                                 LoadFailed = true;
1073                                 LiteralEntityValue = String.Empty;
1074                                 return;
1075                         }
1076
1077                         if (Root.ExternalResources.ContainsKey (ActualUri))
1078                                 LiteralEntityValue = (string) Root.ExternalResources [ActualUri];
1079                         Stream s = null;
1080                         try {
1081                                 s = resolver.GetEntity (absUri, null, typeof (Stream)) as Stream;
1082                                 XmlTextReaderImpl xtr = new XmlTextReaderImpl (ActualUri, s, Root.NameTable);
1083                                 // Don't skip Text declaration here. LiteralEntityValue contains it. See spec 4.5
1084                                 LiteralEntityValue = xtr.GetRemainder ().ReadToEnd ();
1085
1086                                 Root.ExternalResources.Add (ActualUri, LiteralEntityValue);
1087                                 if (Root.ExternalResources.Count > DTDObjectModel.AllowedExternalEntitiesMax)
1088                                         throw new InvalidOperationException ("The total amount of external entities exceeded the allowed number.");
1089
1090                         } catch (Exception ex) {
1091 //                              loadException = ex;
1092                                 LiteralEntityValue = String.Empty;
1093                                 LoadFailed = true;
1094 //                              throw NotWFError ("Cannot resolve external entity. URI is " + ActualUri + " .");
1095                         } finally {
1096                                 if (s != null)
1097                                         s.Close ();
1098                         }
1099                 }
1100         }
1101
1102         internal class DTDEntityDeclaration : DTDEntityBase
1103         {
1104                 string entityValue;
1105                 string notationName;
1106
1107                 ArrayList ReferencingEntities = new ArrayList ();
1108
1109                 bool scanned;
1110                 bool recursed;
1111                 bool hasExternalReference;
1112
1113                 internal DTDEntityDeclaration (DTDObjectModel root) : base (root)
1114                 {
1115                 }
1116
1117                 public string NotationName {
1118                         get { return notationName; }
1119                         set { notationName = value; }
1120                 }
1121
1122                 public bool HasExternalReference {
1123                         get {
1124                                 if (!scanned)
1125                                         ScanEntityValue (new ArrayList ());
1126                                 return hasExternalReference;
1127                         }
1128                 }
1129
1130                 public string EntityValue {
1131                         get {
1132                                 if (this.IsInvalid)
1133                                         return String.Empty;
1134
1135                                 if (PublicId == null && SystemId == null && LiteralEntityValue == null)
1136                                         return String.Empty;
1137
1138                                 if (entityValue == null) {
1139                                         if (NotationName != null)
1140                                                 entityValue = "";
1141                                         else if (SystemId == null || SystemId == String.Empty) {
1142                                                 entityValue = ReplacementText;
1143                                                 if (entityValue == null)
1144                                                         entityValue = String.Empty;
1145                                         } else {
1146                                                 entityValue = ReplacementText;
1147                                         }
1148                                         // Check illegal recursion.
1149                                         ScanEntityValue (new ArrayList ());
1150                                 }
1151                                 return entityValue;
1152                         }
1153                 }
1154
1155                 // It returns whether the entity contains references to external entities.
1156                 public void ScanEntityValue (ArrayList refs)
1157                 {
1158                         // To modify this code, beware nesting between this and EntityValue.
1159                         string value = EntityValue;
1160                         if (this.SystemId != null)
1161                                 hasExternalReference = true;
1162
1163                         if (recursed)
1164                                 throw NotWFError ("Entity recursion was found.");
1165                         recursed = true;
1166
1167                         if (scanned) {
1168                                 foreach (string referenced in refs)
1169                                         if (this.ReferencingEntities.Contains (referenced))
1170                                                 throw NotWFError (String.Format (
1171                                                         "Nested entity was found between {0} and {1}",
1172                                                         referenced, Name));
1173                                 recursed = false;
1174                                 return;
1175                         }
1176
1177                         int len = value.Length;
1178                         int start = 0;
1179                         for (int i=0; i<len; i++) {
1180                                 switch (value [i]) {
1181                                 case '&':
1182                                         start = i+1;
1183                                         break;
1184                                 case ';':
1185                                         if (start == 0)
1186                                                 break;
1187                                         string name = value.Substring (start, i - start);
1188                                         if (name.Length == 0)
1189                                                 throw NotWFError ("Entity reference name is missing.");
1190                                         if (name [0] == '#')
1191                                                 break;  // character reference
1192                                         if (XmlChar.GetPredefinedEntity (name) >= 0)
1193                                                 break;  // predefined reference
1194
1195                                         this.ReferencingEntities.Add (name);
1196                                         DTDEntityDeclaration decl = Root.EntityDecls [name];
1197                                         if (decl != null) {
1198                                                 if (decl.SystemId != null)
1199                                                         hasExternalReference = true;
1200                                                 refs.Add (Name);
1201                                                 decl.ScanEntityValue (refs);
1202                                                 foreach (string str in decl.ReferencingEntities)
1203                                                         ReferencingEntities.Add (str);
1204                                                 refs.Remove (Name);
1205                                                 value = value.Remove (start - 1, name.Length + 2);
1206                                                 value = value.Insert (start - 1, decl.EntityValue);
1207                                                 i -= name.Length + 1; // not +2, because of immediate i++ .
1208                                                 len = value.Length;
1209                                         }
1210                                         start = 0;
1211                                         break;
1212                                 }
1213                         }
1214                         if (start != 0)
1215                                 Root.AddError (new XmlSchemaException ("Invalid reference character '&' is specified.",
1216                                         this.LineNumber, this.LinePosition, null, this.BaseURI, null));
1217                         scanned = true;
1218                         recursed = false;
1219                 }
1220         }
1221
1222         internal class DTDNotationDeclaration : DTDNode
1223         {
1224                 string name;
1225                 string localName;
1226                 string prefix;
1227                 string publicId;
1228                 string systemId;
1229
1230                 public string Name {
1231                         get { return name; }
1232                         set { name = value; }
1233                 }
1234
1235                 public string PublicId {
1236                         get { return publicId; }
1237                         set { publicId = value; }
1238                 }
1239
1240                 public string SystemId {
1241                         get { return systemId; }
1242                         set { systemId = value; }
1243                 }
1244
1245                 public string LocalName {
1246                         get { return localName; }
1247                         set { localName = value; }
1248                 }
1249
1250                 public string Prefix {
1251                         get { return prefix; }
1252                         set { prefix = value; }
1253                 }
1254
1255                 internal DTDNotationDeclaration (DTDObjectModel root)
1256                 {
1257                         SetRoot (root);
1258                 }
1259         }
1260
1261         internal class DTDParameterEntityDeclarationCollection
1262         {
1263                 Hashtable peDecls = new Hashtable ();
1264                 DTDObjectModel root;
1265
1266                 public DTDParameterEntityDeclarationCollection (DTDObjectModel root)
1267                 {
1268                         this.root = root;
1269                 }
1270
1271                 public DTDParameterEntityDeclaration this [string name] {
1272                         get { return peDecls [name] as DTDParameterEntityDeclaration; }
1273                 }
1274
1275                 public void Add (string name, DTDParameterEntityDeclaration decl)
1276                 {
1277                         // PEDecl can be overriden.
1278                         if (peDecls [name] != null)
1279                                 return;
1280                         decl.SetRoot (root);
1281                         peDecls.Add (name, decl);
1282                 }
1283
1284                 public ICollection Keys {
1285                         get { return peDecls.Keys; }
1286                 }
1287
1288                 public ICollection Values {
1289                         get { return peDecls.Values; }
1290                 }
1291         }
1292
1293         internal class DTDParameterEntityDeclaration : DTDEntityBase
1294         {
1295                 internal DTDParameterEntityDeclaration (DTDObjectModel root) : base (root)
1296                 {
1297                 }
1298         }
1299
1300         internal enum DTDContentOrderType
1301         {
1302                 None,
1303                 Seq,
1304                 Or
1305         }
1306
1307         internal enum DTDAttributeOccurenceType
1308         {
1309                 None,
1310                 Required,
1311                 Optional,
1312                 Fixed
1313         }
1314
1315         internal enum DTDOccurence
1316         {
1317                 One,
1318                 Optional,
1319                 ZeroOrMore,
1320                 OneOrMore
1321         }
1322 }