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