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