2004-12-26 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 == null ? 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 = null;
443                         if (ElementName != null) {
444                                 XmlSchemaElement el = new XmlSchemaElement ();
445                                 SetLineInfo (el);
446                                 el.RefName = new XmlQualifiedName (ElementName);
447                                 return el;
448                         } else {
449                                 XmlSchemaGroupBase gb = 
450                                         (OrderType == DTDContentOrderType.Seq) ?
451                                                 (XmlSchemaGroupBase)
452                                                 new XmlSchemaSequence () :
453                                                 new XmlSchemaChoice ();
454                                 SetLineInfo (gb);
455                                 foreach (DTDContentModel cm in ChildModels.Items)
456                                         gb.Items.Add (cm.CreateXsdParticle ());
457                                 p = gb;
458                         }
459                         switch (Occurence) {
460                         case DTDOccurence.Optional:
461                                 p.MinOccurs = 0;
462                                 break;
463                         case DTDOccurence.OneOrMore:
464                                 p.MaxOccursString = "unbounded";
465                                 break;
466                         case DTDOccurence.ZeroOrMore:
467                                 p.MinOccurs = 0;
468                                 p.MaxOccursString = "unbounded";
469                                 break;
470                         }
471                         return p;
472                 }
473
474                 private DTDAutomata CompileInternal ()
475                 {
476                         if (ElementDecl.IsAny)
477                                 return root.Any;
478                         if (ElementDecl.IsEmpty)
479                                 return root.Empty;
480
481                         DTDAutomata basis = GetBasicContentAutomata ();
482                         switch (Occurence) {
483                         case DTDOccurence.One:
484                                 return basis;
485                         case DTDOccurence.Optional:
486                                 return Choice (root.Empty, basis);
487                         case DTDOccurence.OneOrMore:
488                                 return new DTDOneOrMoreAutomata (root, basis);
489                         case DTDOccurence.ZeroOrMore:
490                                 return Choice (root.Empty, new DTDOneOrMoreAutomata (root, basis));
491                         }
492                         throw new InvalidOperationException ();
493                 }
494
495                 private DTDAutomata GetBasicContentAutomata ()
496                 {
497                         if (ElementName != null)
498                                 return new DTDElementAutomata (root, ElementName);
499                         switch (ChildModels.Count) {
500                         case 0:
501                                 return root.Empty;
502                         case 1:
503                                 return ChildModels [0].GetAutomata ();
504                         }
505
506                         DTDAutomata current = null;
507                         int childCount = ChildModels.Count;
508                         switch (OrderType) {
509                         case DTDContentOrderType.Seq:
510                                 current = Sequence (
511                                         ChildModels [childCount - 2].GetAutomata (),
512                                         ChildModels [childCount - 1].GetAutomata ());
513                                 for (int i = childCount - 2; i > 0; i--)
514                                         current = Sequence (
515                                                 ChildModels [i - 1].GetAutomata (), current);
516                                 return current;
517                         case DTDContentOrderType.Or:
518                                 current = Choice (
519                                         ChildModels [childCount - 2].GetAutomata (),
520                                         ChildModels [childCount - 1].GetAutomata ());
521                                 for (int i = childCount - 2; i > 0; i--)
522                                         current = Choice (
523                                                 ChildModels [i - 1].GetAutomata (), current);
524                                 return current;
525                         default:
526                                 throw new InvalidOperationException ("Invalid pattern specification");
527                         }
528                 }
529
530                 private DTDAutomata Sequence (DTDAutomata l, DTDAutomata r)
531                 {
532                         return root.Factory.Sequence (l, r);
533                 }
534
535                 private DTDAutomata Choice (DTDAutomata l, DTDAutomata r)
536                 {
537                         return l.MakeChoice (r);
538                 }
539         }
540
541         internal class DTDContentModelCollection
542         {
543                 ArrayList contentModel = new ArrayList ();
544
545                 public DTDContentModelCollection ()
546                 {
547                 }
548
549                 public IList Items {
550                         get { return contentModel; }
551                 }
552
553                 public DTDContentModel this [int i] {
554                         get { return contentModel [i] as DTDContentModel; }
555                 }
556
557                 public int Count {
558                         get { return contentModel.Count; }
559                 }
560
561                 public void Add (DTDContentModel model)
562                 {
563                         contentModel.Add (model);
564                 }
565         }
566
567         internal abstract class DTDNode : IXmlLineInfo
568         {
569                 DTDObjectModel root;
570                 bool isInternalSubset;
571                 string baseURI;
572                 int lineNumber;
573                 int linePosition;
574
575                 public virtual string BaseURI {
576                         get { return baseURI; }
577                         set { baseURI = value; }
578                 }
579
580                 public bool IsInternalSubset {
581                         get { return isInternalSubset; }
582                         set { isInternalSubset = value; }
583                 }
584
585                 public int LineNumber {
586                         get { return lineNumber; }
587                         set { lineNumber = value; }
588                 }
589
590                 public int LinePosition {
591                         get { return linePosition; }
592                         set { linePosition = value; }
593                 }
594
595                 public bool HasLineInfo ()
596                 {
597                         return lineNumber != 0;
598                 }
599
600                 internal void SetRoot (DTDObjectModel root)
601                 {
602                         this.root = root;
603                         if (baseURI == null)
604                                 this.BaseURI = root.BaseURI;
605                 }
606
607                 protected DTDObjectModel Root {
608                         get { return root; }
609                 }
610
611                 internal XmlException NotWFError (string message)
612                 {
613                         return new XmlException (this as IXmlLineInfo, BaseURI, message);
614                 }
615
616                 public void SetLineInfo (XmlSchemaObject obj)
617                 {
618                         obj.SourceUri = BaseURI;
619                         obj.LineNumber = LineNumber;
620                         obj.LinePosition = LinePosition;
621                 }
622         }
623
624         internal class DTDElementDeclaration : DTDNode
625         {
626                 DTDObjectModel root;
627                 DTDContentModel contentModel;
628                 string name;
629                 bool isEmpty;
630                 bool isAny;
631                 bool isMixedContent;
632
633                 internal DTDElementDeclaration (DTDObjectModel root)
634                 {
635                         this.root = root;
636                 }
637
638                 public string Name {
639                         get { return name; }
640                         set { name = value; }
641                 }
642                 public bool IsEmpty {
643                         get { return isEmpty; }
644                         set { isEmpty = value; }
645                 }
646
647                 public bool IsAny {
648                         get { return isAny; }
649                         set { isAny = value; }
650                 }
651
652                 public bool IsMixedContent {
653                         get { return isMixedContent; }
654                         set { isMixedContent = value; }
655                 }
656
657                 public DTDContentModel ContentModel {
658                         get {
659                                 if (contentModel == null)
660                                         contentModel = new DTDContentModel (root, Name);
661                                 return contentModel;
662                         }
663                 }
664
665                 public DTDAttListDeclaration Attributes {
666                         get {
667                                 return Root.AttListDecls [Name];
668                         }
669                 }
670
671                 internal XmlSchemaElement CreateXsdElement ()
672                 {
673                         XmlSchemaElement el = new XmlSchemaElement ();
674                         SetLineInfo (el);
675                         el.Name = Name;
676                         if (IsEmpty) {
677                                 el.SchemaType = new XmlSchemaComplexType ();
678                                 SetLineInfo (el.SchemaType);
679                         }
680                         else if (IsAny)
681                                 el.SchemaTypeName = new XmlQualifiedName (
682                                         "anyType", XmlSchema.Namespace);
683                         else {
684                                 XmlSchemaComplexType ct = new XmlSchemaComplexType ();
685                                 SetLineInfo (ct);
686                                 foreach (DTDAttributeDefinition a in
687                                         Attributes.Definitions)
688                                         ct.Attributes.Add (a.CreateXsdAttribute ());
689                                 if (IsMixedContent)
690                                         ct.IsMixed = true;
691                                 ct.Particle = ContentModel.CreateXsdParticle ();
692                                 el.SchemaType = ct;
693                         }
694                         return el;
695                 }
696         }
697
698         internal class DTDAttributeDefinition : DTDNode
699         {
700                 string name;
701                 XmlSchemaDatatype datatype;
702                 ArrayList enumeratedLiterals;
703                 string unresolvedDefault;
704                 ArrayList enumeratedNotations;
705                 DTDAttributeOccurenceType occurenceType = DTDAttributeOccurenceType.None;
706                 string resolvedDefaultValue;
707                 string resolvedNormalizedDefaultValue;
708
709                 internal DTDAttributeDefinition (DTDObjectModel root)
710                 {
711                         this.SetRoot (root);
712                 }
713
714                 public string Name {
715                         get { return name; }
716                         set { name =value; }
717                 }
718
719                 public XmlSchemaDatatype Datatype {
720                         get { return datatype; }
721                         set { datatype = value; }
722                 }
723
724                 public DTDAttributeOccurenceType OccurenceType {
725                         get { return this.occurenceType; }
726                         set { this.occurenceType = value; }
727                 }
728
729                 // entity reference inside enumerated values are not allowed,
730                 // but on the other hand, they are allowed inside default value.
731                 // Then I decided to use string ArrayList for enumerated values,
732                 // and unresolved string value for DefaultValue.
733                 public ArrayList EnumeratedAttributeDeclaration {
734                         get {
735                                 if (enumeratedLiterals == null)
736                                         enumeratedLiterals = new ArrayList ();
737                                 return this.enumeratedLiterals;
738                         }
739                 }
740
741                 public ArrayList EnumeratedNotations {
742                         get {
743                                 if (enumeratedNotations == null)
744                                         enumeratedNotations = new ArrayList ();
745                                 return this.enumeratedNotations;
746                         }
747                 }
748
749                 public string DefaultValue {
750                         get {
751                                 if (resolvedDefaultValue == null)
752                                         resolvedDefaultValue = ComputeDefaultValue ();
753                                 return resolvedDefaultValue;
754                         }
755                 }
756
757                 public string NormalizedDefaultValue {
758                         get {
759                                 if (resolvedNormalizedDefaultValue == null) {
760                                         string s = ComputeDefaultValue ();
761                                         try {
762                                                 object o = Datatype.ParseValue (s, null, null);
763                                                 resolvedNormalizedDefaultValue = 
764                                                         (o is string []) ? 
765                                                         String.Join (" ", (string []) o) :
766                                                         o is IFormattable ? ((IFormattable) o).ToString (null, CultureInfo.InvariantCulture) : o.ToString ();
767                                         } catch (Exception) {
768                                                 // This is for non-error-reporting reader
769                                                 resolvedNormalizedDefaultValue = Datatype.Normalize (s);
770                                         }
771                                 }
772                                 return resolvedNormalizedDefaultValue;
773                         }
774                 }
775
776                 public string UnresolvedDefaultValue {
777                         get { return this.unresolvedDefault; }
778                         set { this.unresolvedDefault = value; }
779                 }
780
781                 public char QuoteChar {
782                         get {
783                                 return UnresolvedDefaultValue.Length > 0 ?
784                                         this.UnresolvedDefaultValue [0] :
785                                         '"';
786                         }
787                 }
788
789                 internal XmlSchemaAttribute CreateXsdAttribute ()
790                 {
791                         XmlSchemaAttribute a = new XmlSchemaAttribute ();
792                         SetLineInfo (a);
793                         a.Name = Name;
794                         a.DefaultValue = resolvedNormalizedDefaultValue;
795
796                         XmlQualifiedName qname = XmlQualifiedName.Empty;
797                         ArrayList enumeration = null;
798                         if (enumeratedNotations != null && enumeratedNotations.Count > 0) {
799                                 qname = new XmlQualifiedName ("NOTATION", XmlSchema.Namespace);
800                                 enumeration = enumeratedNotations;
801                         }
802                         else if (enumeratedLiterals != null)
803                                 enumeration = enumeratedLiterals;
804                         else {
805                                 switch (Datatype.TokenizedType) {
806                                 case XmlTokenizedType.ID:
807                                         qname = new XmlQualifiedName ("ID", XmlSchema.Namespace); break;
808                                 case XmlTokenizedType.IDREF:
809                                         qname = new XmlQualifiedName ("IDREF", XmlSchema.Namespace); break;
810                                 case XmlTokenizedType.IDREFS:
811                                         qname = new XmlQualifiedName ("IDREFS", XmlSchema.Namespace); break;
812                                 case XmlTokenizedType.ENTITY:
813                                         qname = new XmlQualifiedName ("ENTITY", XmlSchema.Namespace); break;
814                                 case XmlTokenizedType.ENTITIES:
815                                         qname = new XmlQualifiedName ("ENTITIES", XmlSchema.Namespace); break;
816                                 case XmlTokenizedType.NMTOKEN:
817                                         qname = new XmlQualifiedName ("NMTOKEN", XmlSchema.Namespace); break;
818                                 case XmlTokenizedType.NMTOKENS:
819                                         qname = new XmlQualifiedName ("NMTOKENS", XmlSchema.Namespace); break;
820                                 case XmlTokenizedType.NOTATION:
821                                         qname = new XmlQualifiedName ("NOTATION", XmlSchema.Namespace); break;
822                                 }
823                         }
824
825                         if (enumeration != null) {
826                                 XmlSchemaSimpleType st = new XmlSchemaSimpleType ();
827                                 SetLineInfo (st);
828                                 XmlSchemaSimpleTypeRestriction r =
829                                         new XmlSchemaSimpleTypeRestriction ();
830                                 SetLineInfo (r);
831                                 r.BaseTypeName = qname;
832                                 foreach (string name in enumeratedNotations) {
833                                         XmlSchemaEnumerationFacet f =
834                                                 new XmlSchemaEnumerationFacet ();
835                                         SetLineInfo (f);
836                                         r.Facets.Add (f);
837                                         f.Value = name;
838                                 }
839                         }
840                         else if (qname != XmlQualifiedName.Empty)
841                                 a.SchemaTypeName = qname;
842                         return a;
843                 }
844
845                 internal string ComputeDefaultValue ()
846                 {
847                         if (UnresolvedDefaultValue == null)
848                                 return null;
849
850                         StringBuilder sb = new StringBuilder ();
851                         int pos = 0;
852                         int next = 0;
853                         string value = this.UnresolvedDefaultValue;
854                         while ((next = value.IndexOf ('&', pos)) >= 0) {
855                                 int semicolon = value.IndexOf (';', next);
856                                 if (value [next + 1] == '#') {
857                                         // character reference.
858                                         char c = value [next + 2];
859                                         NumberStyles style = NumberStyles.Integer;
860                                         string spec;
861                                         if (c == 'x' || c == 'X') {
862                                                 spec = value.Substring (next + 3, semicolon - next - 3);
863                                                 style |= NumberStyles.HexNumber;
864                                         }
865                                         else
866                                                 spec = value.Substring (next + 2, semicolon - next - 2);
867                                         sb.Append ((char) int.Parse (spec, style, CultureInfo.InvariantCulture));
868                                 } else {
869                                         sb.Append (value.Substring (pos, next - 1));
870                                         string name = value.Substring (next + 1, semicolon - 2);
871                                         int predefined = XmlChar.GetPredefinedEntity (name);
872                                         if (predefined >= 0)
873                                                 sb.Append (predefined);
874                                         else
875                                                 sb.Append (Root.ResolveEntity (name));
876                                 }
877                                 pos = semicolon + 1;
878                         }
879                         sb.Append (value.Substring (pos));
880                         // strip quote chars
881                         string ret = sb.ToString (1, sb.Length - 2);
882                         sb.Length = 0;
883                         return ret;
884                 }
885
886         }
887
888         internal class DTDAttListDeclaration : DTDNode
889         {
890                 string name;
891                 Hashtable attributeOrders = new Hashtable ();
892                 ArrayList attributes = new ArrayList ();
893
894                 internal DTDAttListDeclaration (DTDObjectModel root)
895                 {
896                         SetRoot (root);
897                 }
898
899                 public string Name {
900                         get { return name; }
901                         set { name = value; }
902                 }
903
904                 public DTDAttributeDefinition this [int i] {
905                         get { return Get (i); }
906                 }
907
908                 public DTDAttributeDefinition this [string name] {
909                         get { return Get (name); }
910                 }
911
912                 public DTDAttributeDefinition Get (int i)
913                 {
914                         return attributes [i] as DTDAttributeDefinition;
915                 }
916
917                 public DTDAttributeDefinition Get (string name)
918                 {
919                         object o = attributeOrders [name];
920                         if (o != null)
921                                 return attributes [(int) o] as DTDAttributeDefinition;
922                         else
923                                 return null;
924                 }
925
926                 public IList Definitions {
927                         get { return attributes; }
928                 }
929
930                 public void Add (DTDAttributeDefinition def)
931                 {
932                         if (attributeOrders [def.Name] != null)
933                                 throw new InvalidOperationException (String.Format (
934                                         "Attribute definition for {0} was already added at element {1}.",
935                                         def.Name, this.Name));
936                         def.SetRoot (Root);
937                         attributeOrders.Add (def.Name, attributes.Count);
938                         attributes.Add (def);
939                 }
940
941                 public int Count {
942                         get { return attributeOrders.Count; }
943                 }
944         }
945
946         internal class DTDEntityBase : DTDNode
947         {
948                 string name;
949                 string publicId;
950                 string systemId;
951                 string literalValue;
952                 string replacementText;
953                 bool isInvalid;
954 //              Exception loadException;
955                 bool loadFailed;
956
957                 protected DTDEntityBase (DTDObjectModel root)
958                 {
959                         SetRoot (root);
960                 }
961
962                 internal bool IsInvalid {
963                         get { return isInvalid; }
964                         set { isInvalid = value; }
965                 }
966
967                 public bool LoadFailed {
968                         get { return loadFailed; }
969                         set { loadFailed = value; }
970                 }
971
972                 public string Name {
973                         get { return name; }
974                         set { name = value; }
975                 }
976
977                 public string PublicId {
978                         get { return publicId; }
979                         set { publicId = value; }
980                 }
981
982                 public string SystemId {
983                         get { return systemId; }
984                         set { systemId = value; }
985                 }
986
987                 public string LiteralEntityValue {
988                         get { return literalValue; }
989                         set { literalValue = value; }
990                 }
991
992                 public string ReplacementText {
993                         get { return replacementText; }
994                         set { replacementText = value; }
995                 }
996
997                 public void Resolve (XmlResolver resolver)
998                 {
999                         if (resolver == null || SystemId == null || SystemId.Length == 0) {
1000                                 LoadFailed = true;
1001                                 LiteralEntityValue = String.Empty;
1002                                 return;
1003                         }
1004
1005                         Uri baseUri = null;
1006                         try {
1007                                 if (BaseURI != null && BaseURI.Length > 0)
1008                                         baseUri = new Uri (BaseURI);
1009                         } catch (UriFormatException) {
1010                         }
1011
1012                         Uri absUri = resolver.ResolveUri (baseUri, SystemId);
1013                         string absPath = absUri != null ? absUri.ToString () : String.Empty;
1014                         if (Root.ExternalResources.ContainsKey (absPath))
1015                                 LiteralEntityValue = (string) Root.ExternalResources [absPath];
1016                         Stream s = null;
1017                         try {
1018                                 s = resolver.GetEntity (absUri, null, typeof (Stream)) as Stream;
1019                                 XmlTextReaderImpl xtr = new XmlTextReaderImpl (absPath, s, Root.NameTable);
1020                                 // Don't skip Text declaration here. LiteralEntityValue contains it. See spec 4.5
1021                                 this.BaseURI = absPath;
1022                                 LiteralEntityValue = xtr.GetRemainder ().ReadToEnd ();
1023
1024                                 Root.ExternalResources.Add (absPath, LiteralEntityValue);
1025                                 if (Root.ExternalResources.Count > DTDObjectModel.AllowedExternalEntitiesMax)
1026                                         throw new InvalidOperationException ("The total amount of external entities exceeded the allowed number.");
1027
1028                         } catch (Exception ex) {
1029 //                              loadException = ex;
1030                                 LiteralEntityValue = String.Empty;
1031                                 LoadFailed = true;
1032 //                              throw NotWFError ("Cannot resolve external entity. URI is " + absPath + " .");
1033                         } finally {
1034                                 if (s != null)
1035                                         s.Close ();
1036                         }
1037                 }
1038         }
1039
1040         internal class DTDEntityDeclaration : DTDEntityBase
1041         {
1042                 string entityValue;
1043                 string notationName;
1044
1045                 ArrayList ReferencingEntities = new ArrayList ();
1046
1047                 bool scanned;
1048                 bool recursed;
1049                 bool hasExternalReference;
1050
1051                 internal DTDEntityDeclaration (DTDObjectModel root) : base (root)
1052                 {
1053                 }
1054
1055                 public string NotationName {
1056                         get { return notationName; }
1057                         set { notationName = value; }
1058                 }
1059
1060                 public bool HasExternalReference {
1061                         get {
1062                                 if (!scanned)
1063                                         ScanEntityValue (new ArrayList ());
1064                                 return hasExternalReference;
1065                         }
1066                 }
1067
1068                 public string EntityValue {
1069                         get {
1070                                 if (this.IsInvalid)
1071                                         return String.Empty;
1072
1073                                 if (PublicId == null && SystemId == null && LiteralEntityValue == null)
1074                                         return String.Empty;
1075
1076                                 if (entityValue == null) {
1077                                         if (NotationName != null)
1078                                                 entityValue = "";
1079                                         else if (SystemId == null || SystemId == String.Empty) {
1080                                                 entityValue = ReplacementText;
1081                                                 if (entityValue == null)
1082                                                         entityValue = String.Empty;
1083                                         } else {
1084                                                 entityValue = ReplacementText;
1085                                         }
1086                                         // Check illegal recursion.
1087                                         ScanEntityValue (new ArrayList ());
1088                                 }
1089                                 return entityValue;
1090                         }
1091                 }
1092
1093                 // It returns whether the entity contains references to external entities.
1094                 public void ScanEntityValue (ArrayList refs)
1095                 {
1096                         // To modify this code, beware nesting between this and EntityValue.
1097                         string value = EntityValue;
1098                         if (this.SystemId != null)
1099                                 hasExternalReference = true;
1100
1101                         if (recursed)
1102                                 throw NotWFError ("Entity recursion was found.");
1103                         recursed = true;
1104
1105                         if (scanned) {
1106                                 foreach (string referenced in refs)
1107                                         if (this.ReferencingEntities.Contains (referenced))
1108                                                 throw NotWFError (String.Format (
1109                                                         "Nested entity was found between {0} and {1}",
1110                                                         referenced, Name));
1111                                 recursed = false;
1112                                 return;
1113                         }
1114
1115                         int len = value.Length;
1116                         int start = 0;
1117                         for (int i=0; i<len; i++) {
1118                                 switch (value [i]) {
1119                                 case '&':
1120                                         start = i+1;
1121                                         break;
1122                                 case ';':
1123                                         if (start == 0)
1124                                                 break;
1125                                         string name = value.Substring (start, i - start);
1126                                         if (name.Length == 0)
1127                                                 throw NotWFError ("Entity reference name is missing.");
1128                                         if (name [0] == '#')
1129                                                 break;  // character reference
1130                                         if (XmlChar.GetPredefinedEntity (name) >= 0)
1131                                                 break;  // predefined reference
1132
1133                                         this.ReferencingEntities.Add (name);
1134                                         DTDEntityDeclaration decl = Root.EntityDecls [name];
1135                                         if (decl != null) {
1136                                                 if (decl.SystemId != null)
1137                                                         hasExternalReference = true;
1138                                                 refs.Add (Name);
1139                                                 decl.ScanEntityValue (refs);
1140                                                 foreach (string str in decl.ReferencingEntities)
1141                                                         ReferencingEntities.Add (str);
1142                                                 refs.Remove (Name);
1143                                                 value = value.Remove (start - 1, name.Length + 2);
1144                                                 value = value.Insert (start - 1, decl.EntityValue);
1145                                                 i -= name.Length + 1; // not +2, because of immediate i++ .
1146                                                 len = value.Length;
1147                                         }
1148                                         start = 0;
1149                                         break;
1150                                 }
1151                         }
1152                         if (start != 0)
1153                                 Root.AddError (new XmlSchemaException ("Invalid reference character '&' is specified.",
1154                                         this.LineNumber, this.LinePosition, null, this.BaseURI, null));
1155                         scanned = true;
1156                         recursed = false;
1157                 }
1158         }
1159
1160         internal class DTDNotationDeclaration : DTDNode
1161         {
1162                 string name;
1163                 string localName;
1164                 string prefix;
1165                 string publicId;
1166                 string systemId;
1167
1168                 public string Name {
1169                         get { return name; }
1170                         set { name = value; }
1171                 }
1172
1173                 public string PublicId {
1174                         get { return publicId; }
1175                         set { publicId = value; }
1176                 }
1177
1178                 public string SystemId {
1179                         get { return systemId; }
1180                         set { systemId = value; }
1181                 }
1182
1183                 public string LocalName {
1184                         get { return localName; }
1185                         set { localName = value; }
1186                 }
1187
1188                 public string Prefix {
1189                         get { return prefix; }
1190                         set { prefix = value; }
1191                 }
1192
1193                 internal DTDNotationDeclaration (DTDObjectModel root)
1194                 {
1195                         SetRoot (root);
1196                 }
1197         }
1198
1199         internal class DTDParameterEntityDeclarationCollection
1200         {
1201                 Hashtable peDecls = new Hashtable ();
1202                 DTDObjectModel root;
1203
1204                 public DTDParameterEntityDeclarationCollection (DTDObjectModel root)
1205                 {
1206                         this.root = root;
1207                 }
1208
1209                 public DTDParameterEntityDeclaration this [string name] {
1210                         get { return peDecls [name] as DTDParameterEntityDeclaration; }
1211                 }
1212
1213                 public void Add (string name, DTDParameterEntityDeclaration decl)
1214                 {
1215                         // PEDecl can be overriden.
1216                         if (peDecls [name] != null)
1217                                 return;
1218                         decl.SetRoot (root);
1219                         peDecls.Add (name, decl);
1220                 }
1221
1222                 public ICollection Keys {
1223                         get { return peDecls.Keys; }
1224                 }
1225
1226                 public ICollection Values {
1227                         get { return peDecls.Values; }
1228                 }
1229         }
1230
1231         internal class DTDParameterEntityDeclaration : DTDEntityBase
1232         {
1233                 internal DTDParameterEntityDeclaration (DTDObjectModel root) : base (root)
1234                 {
1235                 }
1236         }
1237
1238         internal enum DTDContentOrderType
1239         {
1240                 None,
1241                 Seq,
1242                 Or
1243         }
1244
1245         internal enum DTDAttributeOccurenceType
1246         {
1247                 None,
1248                 Required,
1249                 Optional,
1250                 Fixed
1251         }
1252
1253         internal enum DTDOccurence
1254         {
1255                 One,
1256                 Optional,
1257                 ZeroOrMore,
1258                 OneOrMore
1259         }
1260 }