2005-12-05 Lluis Sanchez Gual <lluis@novell.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 = 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                                 if (Attributes != null)
687                                         foreach (DTDAttributeDefinition a in
688                                                 Attributes.Definitions)
689                                                 ct.Attributes.Add (a.CreateXsdAttribute ());
690                                 if (IsMixedContent)
691                                         ct.IsMixed = true;
692                                 ct.Particle = ContentModel.CreateXsdParticle ();
693                                 el.SchemaType = ct;
694                         }
695                         return el;
696                 }
697         }
698
699         internal class DTDAttributeDefinition : DTDNode
700         {
701                 string name;
702                 XmlSchemaDatatype datatype;
703                 ArrayList enumeratedLiterals;
704                 string unresolvedDefault;
705                 ArrayList enumeratedNotations;
706                 DTDAttributeOccurenceType occurenceType = DTDAttributeOccurenceType.None;
707                 string resolvedDefaultValue;
708                 string resolvedNormalizedDefaultValue;
709
710                 internal DTDAttributeDefinition (DTDObjectModel root)
711                 {
712                         this.SetRoot (root);
713                 }
714
715                 public string Name {
716                         get { return name; }
717                         set { name =value; }
718                 }
719
720                 public XmlSchemaDatatype Datatype {
721                         get { return datatype; }
722                         set { datatype = value; }
723                 }
724
725                 public DTDAttributeOccurenceType OccurenceType {
726                         get { return this.occurenceType; }
727                         set { this.occurenceType = value; }
728                 }
729
730                 // entity reference inside enumerated values are not allowed,
731                 // but on the other hand, they are allowed inside default value.
732                 // Then I decided to use string ArrayList for enumerated values,
733                 // and unresolved string value for DefaultValue.
734                 public ArrayList EnumeratedAttributeDeclaration {
735                         get {
736                                 if (enumeratedLiterals == null)
737                                         enumeratedLiterals = new ArrayList ();
738                                 return this.enumeratedLiterals;
739                         }
740                 }
741
742                 public ArrayList EnumeratedNotations {
743                         get {
744                                 if (enumeratedNotations == null)
745                                         enumeratedNotations = new ArrayList ();
746                                 return this.enumeratedNotations;
747                         }
748                 }
749
750                 public string DefaultValue {
751                         get {
752                                 if (resolvedDefaultValue == null)
753                                         resolvedDefaultValue = ComputeDefaultValue ();
754                                 return resolvedDefaultValue;
755                         }
756                 }
757
758                 public string NormalizedDefaultValue {
759                         get {
760                                 if (resolvedNormalizedDefaultValue == null) {
761                                         string s = ComputeDefaultValue ();
762                                         try {
763                                                 object o = Datatype.ParseValue (s, null, null);
764                                                 resolvedNormalizedDefaultValue = 
765                                                         (o is string []) ? 
766                                                         String.Join (" ", (string []) o) :
767                                                         o is IFormattable ? ((IFormattable) o).ToString (null, CultureInfo.InvariantCulture) : o.ToString ();
768                                         } catch (Exception) {
769                                                 // This is for non-error-reporting reader
770                                                 resolvedNormalizedDefaultValue = Datatype.Normalize (s);
771                                         }
772                                 }
773                                 return resolvedNormalizedDefaultValue;
774                         }
775                 }
776
777                 public string UnresolvedDefaultValue {
778                         get { return this.unresolvedDefault; }
779                         set { this.unresolvedDefault = value; }
780                 }
781
782                 public char QuoteChar {
783                         get {
784                                 return UnresolvedDefaultValue.Length > 0 ?
785                                         this.UnresolvedDefaultValue [0] :
786                                         '"';
787                         }
788                 }
789
790                 internal XmlSchemaAttribute CreateXsdAttribute ()
791                 {
792                         XmlSchemaAttribute a = new XmlSchemaAttribute ();
793                         SetLineInfo (a);
794                         a.Name = Name;
795                         a.DefaultValue = resolvedNormalizedDefaultValue;
796
797                         XmlQualifiedName qname = XmlQualifiedName.Empty;
798                         ArrayList enumeration = null;
799                         if (enumeratedNotations != null && enumeratedNotations.Count > 0) {
800                                 qname = new XmlQualifiedName ("NOTATION", XmlSchema.Namespace);
801                                 enumeration = enumeratedNotations;
802                         }
803                         else if (enumeratedLiterals != null)
804                                 enumeration = enumeratedLiterals;
805                         else {
806                                 switch (Datatype.TokenizedType) {
807                                 case XmlTokenizedType.ID:
808                                         qname = new XmlQualifiedName ("ID", XmlSchema.Namespace); break;
809                                 case XmlTokenizedType.IDREF:
810                                         qname = new XmlQualifiedName ("IDREF", XmlSchema.Namespace); break;
811                                 case XmlTokenizedType.IDREFS:
812                                         qname = new XmlQualifiedName ("IDREFS", XmlSchema.Namespace); break;
813                                 case XmlTokenizedType.ENTITY:
814                                         qname = new XmlQualifiedName ("ENTITY", XmlSchema.Namespace); break;
815                                 case XmlTokenizedType.ENTITIES:
816                                         qname = new XmlQualifiedName ("ENTITIES", XmlSchema.Namespace); break;
817                                 case XmlTokenizedType.NMTOKEN:
818                                         qname = new XmlQualifiedName ("NMTOKEN", XmlSchema.Namespace); break;
819                                 case XmlTokenizedType.NMTOKENS:
820                                         qname = new XmlQualifiedName ("NMTOKENS", XmlSchema.Namespace); break;
821                                 case XmlTokenizedType.NOTATION:
822                                         qname = new XmlQualifiedName ("NOTATION", XmlSchema.Namespace); break;
823                                 }
824                         }
825
826                         if (enumeration != null) {
827                                 XmlSchemaSimpleType st = new XmlSchemaSimpleType ();
828                                 SetLineInfo (st);
829                                 XmlSchemaSimpleTypeRestriction r =
830                                         new XmlSchemaSimpleTypeRestriction ();
831                                 SetLineInfo (r);
832                                 r.BaseTypeName = qname;
833                                 if (enumeratedNotations != null) {
834                                         foreach (string name in enumeratedNotations) {
835                                                 XmlSchemaEnumerationFacet f =
836                                                         new XmlSchemaEnumerationFacet ();
837                                                 SetLineInfo (f);
838                                                 r.Facets.Add (f);
839                                                 f.Value = name;
840                                         }
841                                 }
842                         }
843                         else if (qname != XmlQualifiedName.Empty)
844                                 a.SchemaTypeName = qname;
845                         return a;
846                 }
847
848                 internal string ComputeDefaultValue ()
849                 {
850                         if (UnresolvedDefaultValue == null)
851                                 return null;
852
853                         StringBuilder sb = new StringBuilder ();
854                         int pos = 0;
855                         int next = 0;
856                         string value = this.UnresolvedDefaultValue;
857                         while ((next = value.IndexOf ('&', pos)) >= 0) {
858                                 int semicolon = value.IndexOf (';', next);
859                                 if (value [next + 1] == '#') {
860                                         // character reference.
861                                         char c = value [next + 2];
862                                         NumberStyles style = NumberStyles.Integer;
863                                         string spec;
864                                         if (c == 'x' || c == 'X') {
865                                                 spec = value.Substring (next + 3, semicolon - next - 3);
866                                                 style |= NumberStyles.HexNumber;
867                                         }
868                                         else
869                                                 spec = value.Substring (next + 2, semicolon - next - 2);
870                                         sb.Append ((char) int.Parse (spec, style, CultureInfo.InvariantCulture));
871                                 } else {
872                                         sb.Append (value.Substring (pos, next - 1));
873                                         string name = value.Substring (next + 1, semicolon - 2);
874                                         int predefined = XmlChar.GetPredefinedEntity (name);
875                                         if (predefined >= 0)
876                                                 sb.Append (predefined);
877                                         else
878                                                 sb.Append (Root.ResolveEntity (name));
879                                 }
880                                 pos = semicolon + 1;
881                         }
882                         sb.Append (value.Substring (pos));
883                         // strip quote chars
884                         string ret = sb.ToString (1, sb.Length - 2);
885                         sb.Length = 0;
886                         return ret;
887                 }
888
889         }
890
891         internal class DTDAttListDeclaration : DTDNode
892         {
893                 string name;
894                 Hashtable attributeOrders = new Hashtable ();
895                 ArrayList attributes = new ArrayList ();
896
897                 internal DTDAttListDeclaration (DTDObjectModel root)
898                 {
899                         SetRoot (root);
900                 }
901
902                 public string Name {
903                         get { return name; }
904                         set { name = value; }
905                 }
906
907                 public DTDAttributeDefinition this [int i] {
908                         get { return Get (i); }
909                 }
910
911                 public DTDAttributeDefinition this [string name] {
912                         get { return Get (name); }
913                 }
914
915                 public DTDAttributeDefinition Get (int i)
916                 {
917                         return attributes [i] as DTDAttributeDefinition;
918                 }
919
920                 public DTDAttributeDefinition Get (string name)
921                 {
922                         object o = attributeOrders [name];
923                         if (o != null)
924                                 return attributes [(int) o] as DTDAttributeDefinition;
925                         else
926                                 return null;
927                 }
928
929                 public IList Definitions {
930                         get { return attributes; }
931                 }
932
933                 public void Add (DTDAttributeDefinition def)
934                 {
935                         if (attributeOrders [def.Name] != null)
936                                 throw new InvalidOperationException (String.Format (
937                                         "Attribute definition for {0} was already added at element {1}.",
938                                         def.Name, this.Name));
939                         def.SetRoot (Root);
940                         attributeOrders.Add (def.Name, attributes.Count);
941                         attributes.Add (def);
942                 }
943
944                 public int Count {
945                         get { return attributeOrders.Count; }
946                 }
947         }
948
949         internal class DTDEntityBase : DTDNode
950         {
951                 string name;
952                 string publicId;
953                 string systemId;
954                 string literalValue;
955                 string replacementText;
956                 string uriString;
957                 Uri absUri;
958                 bool isInvalid;
959 //              Exception loadException;
960                 bool loadFailed;
961                 XmlResolver resolver;
962
963                 protected DTDEntityBase (DTDObjectModel root)
964                 {
965                         SetRoot (root);
966                 }
967
968                 internal bool IsInvalid {
969                         get { return isInvalid; }
970                         set { isInvalid = value; }
971                 }
972
973                 public bool LoadFailed {
974                         get { return loadFailed; }
975                         set { loadFailed = value; }
976                 }
977
978                 public string Name {
979                         get { return name; }
980                         set { name = value; }
981                 }
982
983                 public string PublicId {
984                         get { return publicId; }
985                         set { publicId = value; }
986                 }
987
988                 public string SystemId {
989                         get { return systemId; }
990                         set { systemId = value; }
991                 }
992
993                 public string LiteralEntityValue {
994                         get { return literalValue; }
995                         set { literalValue = value; }
996                 }
997
998                 public string ReplacementText {
999                         get { return replacementText; }
1000                         set { replacementText = value; }
1001                 }
1002
1003                 public XmlResolver XmlResolver {
1004                         set { resolver = value; }
1005                 }
1006
1007                 public string ActualUri {
1008                         get {
1009                                 if (uriString == null) {
1010                                         if (resolver == null || SystemId == null || SystemId.Length == 0)
1011                                                 uriString = BaseURI;
1012                                         else {
1013                                                 Uri baseUri = null;
1014                                                 try {
1015                                                         if (BaseURI != null && BaseURI.Length > 0)
1016                                                                 baseUri = new Uri (BaseURI);
1017                                                 } catch (UriFormatException) {
1018                                                 }
1019
1020                                                 absUri = resolver.ResolveUri (baseUri, SystemId);
1021                                                 uriString = absUri != null ? absUri.ToString () : String.Empty;
1022                                         }
1023                                 }
1024                                 return uriString;
1025                         }
1026                 }
1027
1028                 public void Resolve ()
1029                 {
1030                         if (ActualUri == String.Empty) {
1031                                 LoadFailed = true;
1032                                 LiteralEntityValue = String.Empty;
1033                                 return;
1034                         }
1035
1036                         if (Root.ExternalResources.ContainsKey (ActualUri))
1037                                 LiteralEntityValue = (string) Root.ExternalResources [ActualUri];
1038                         Stream s = null;
1039                         try {
1040                                 s = resolver.GetEntity (absUri, null, typeof (Stream)) as Stream;
1041                                 XmlTextReaderImpl xtr = new XmlTextReaderImpl (ActualUri, s, Root.NameTable);
1042                                 // Don't skip Text declaration here. LiteralEntityValue contains it. See spec 4.5
1043                                 LiteralEntityValue = xtr.GetRemainder ().ReadToEnd ();
1044
1045                                 Root.ExternalResources.Add (ActualUri, LiteralEntityValue);
1046                                 if (Root.ExternalResources.Count > DTDObjectModel.AllowedExternalEntitiesMax)
1047                                         throw new InvalidOperationException ("The total amount of external entities exceeded the allowed number.");
1048
1049                         } catch (Exception ex) {
1050 //                              loadException = ex;
1051                                 LiteralEntityValue = String.Empty;
1052                                 LoadFailed = true;
1053 //                              throw NotWFError ("Cannot resolve external entity. URI is " + ActualUri + " .");
1054                         } finally {
1055                                 if (s != null)
1056                                         s.Close ();
1057                         }
1058                 }
1059         }
1060
1061         internal class DTDEntityDeclaration : DTDEntityBase
1062         {
1063                 string entityValue;
1064                 string notationName;
1065
1066                 ArrayList ReferencingEntities = new ArrayList ();
1067
1068                 bool scanned;
1069                 bool recursed;
1070                 bool hasExternalReference;
1071
1072                 internal DTDEntityDeclaration (DTDObjectModel root) : base (root)
1073                 {
1074                 }
1075
1076                 public string NotationName {
1077                         get { return notationName; }
1078                         set { notationName = value; }
1079                 }
1080
1081                 public bool HasExternalReference {
1082                         get {
1083                                 if (!scanned)
1084                                         ScanEntityValue (new ArrayList ());
1085                                 return hasExternalReference;
1086                         }
1087                 }
1088
1089                 public string EntityValue {
1090                         get {
1091                                 if (this.IsInvalid)
1092                                         return String.Empty;
1093
1094                                 if (PublicId == null && SystemId == null && LiteralEntityValue == null)
1095                                         return String.Empty;
1096
1097                                 if (entityValue == null) {
1098                                         if (NotationName != null)
1099                                                 entityValue = "";
1100                                         else if (SystemId == null || SystemId == String.Empty) {
1101                                                 entityValue = ReplacementText;
1102                                                 if (entityValue == null)
1103                                                         entityValue = String.Empty;
1104                                         } else {
1105                                                 entityValue = ReplacementText;
1106                                         }
1107                                         // Check illegal recursion.
1108                                         ScanEntityValue (new ArrayList ());
1109                                 }
1110                                 return entityValue;
1111                         }
1112                 }
1113
1114                 // It returns whether the entity contains references to external entities.
1115                 public void ScanEntityValue (ArrayList refs)
1116                 {
1117                         // To modify this code, beware nesting between this and EntityValue.
1118                         string value = EntityValue;
1119                         if (this.SystemId != null)
1120                                 hasExternalReference = true;
1121
1122                         if (recursed)
1123                                 throw NotWFError ("Entity recursion was found.");
1124                         recursed = true;
1125
1126                         if (scanned) {
1127                                 foreach (string referenced in refs)
1128                                         if (this.ReferencingEntities.Contains (referenced))
1129                                                 throw NotWFError (String.Format (
1130                                                         "Nested entity was found between {0} and {1}",
1131                                                         referenced, Name));
1132                                 recursed = false;
1133                                 return;
1134                         }
1135
1136                         int len = value.Length;
1137                         int start = 0;
1138                         for (int i=0; i<len; i++) {
1139                                 switch (value [i]) {
1140                                 case '&':
1141                                         start = i+1;
1142                                         break;
1143                                 case ';':
1144                                         if (start == 0)
1145                                                 break;
1146                                         string name = value.Substring (start, i - start);
1147                                         if (name.Length == 0)
1148                                                 throw NotWFError ("Entity reference name is missing.");
1149                                         if (name [0] == '#')
1150                                                 break;  // character reference
1151                                         if (XmlChar.GetPredefinedEntity (name) >= 0)
1152                                                 break;  // predefined reference
1153
1154                                         this.ReferencingEntities.Add (name);
1155                                         DTDEntityDeclaration decl = Root.EntityDecls [name];
1156                                         if (decl != null) {
1157                                                 if (decl.SystemId != null)
1158                                                         hasExternalReference = true;
1159                                                 refs.Add (Name);
1160                                                 decl.ScanEntityValue (refs);
1161                                                 foreach (string str in decl.ReferencingEntities)
1162                                                         ReferencingEntities.Add (str);
1163                                                 refs.Remove (Name);
1164                                                 value = value.Remove (start - 1, name.Length + 2);
1165                                                 value = value.Insert (start - 1, decl.EntityValue);
1166                                                 i -= name.Length + 1; // not +2, because of immediate i++ .
1167                                                 len = value.Length;
1168                                         }
1169                                         start = 0;
1170                                         break;
1171                                 }
1172                         }
1173                         if (start != 0)
1174                                 Root.AddError (new XmlSchemaException ("Invalid reference character '&' is specified.",
1175                                         this.LineNumber, this.LinePosition, null, this.BaseURI, null));
1176                         scanned = true;
1177                         recursed = false;
1178                 }
1179         }
1180
1181         internal class DTDNotationDeclaration : DTDNode
1182         {
1183                 string name;
1184                 string localName;
1185                 string prefix;
1186                 string publicId;
1187                 string systemId;
1188
1189                 public string Name {
1190                         get { return name; }
1191                         set { name = value; }
1192                 }
1193
1194                 public string PublicId {
1195                         get { return publicId; }
1196                         set { publicId = value; }
1197                 }
1198
1199                 public string SystemId {
1200                         get { return systemId; }
1201                         set { systemId = value; }
1202                 }
1203
1204                 public string LocalName {
1205                         get { return localName; }
1206                         set { localName = value; }
1207                 }
1208
1209                 public string Prefix {
1210                         get { return prefix; }
1211                         set { prefix = value; }
1212                 }
1213
1214                 internal DTDNotationDeclaration (DTDObjectModel root)
1215                 {
1216                         SetRoot (root);
1217                 }
1218         }
1219
1220         internal class DTDParameterEntityDeclarationCollection
1221         {
1222                 Hashtable peDecls = new Hashtable ();
1223                 DTDObjectModel root;
1224
1225                 public DTDParameterEntityDeclarationCollection (DTDObjectModel root)
1226                 {
1227                         this.root = root;
1228                 }
1229
1230                 public DTDParameterEntityDeclaration this [string name] {
1231                         get { return peDecls [name] as DTDParameterEntityDeclaration; }
1232                 }
1233
1234                 public void Add (string name, DTDParameterEntityDeclaration decl)
1235                 {
1236                         // PEDecl can be overriden.
1237                         if (peDecls [name] != null)
1238                                 return;
1239                         decl.SetRoot (root);
1240                         peDecls.Add (name, decl);
1241                 }
1242
1243                 public ICollection Keys {
1244                         get { return peDecls.Keys; }
1245                 }
1246
1247                 public ICollection Values {
1248                         get { return peDecls.Values; }
1249                 }
1250         }
1251
1252         internal class DTDParameterEntityDeclaration : DTDEntityBase
1253         {
1254                 internal DTDParameterEntityDeclaration (DTDObjectModel root) : base (root)
1255                 {
1256                 }
1257         }
1258
1259         internal enum DTDContentOrderType
1260         {
1261                 None,
1262                 Seq,
1263                 Or
1264         }
1265
1266         internal enum DTDAttributeOccurenceType
1267         {
1268                 None,
1269                 Required,
1270                 Optional,
1271                 Fixed
1272         }
1273
1274         internal enum DTDOccurence
1275         {
1276                 One,
1277                 Optional,
1278                 ZeroOrMore,
1279                 OneOrMore
1280         }
1281 }