**** Merged from MCS ****
[mono-project.git] / mcs / class / System.XML / System.Xml / DTDObjectModel.cs
blobdba05d65c1959dc7c38395aa4f11f28acc8f68a3
1 //
2 // Mono.Xml.DTDObjectModel
3 //
4 // Author:
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
6 //
7 // (C)2003 Atsushi Enomoto
8 //
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.
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;
39 #if NET_2_0
40 using XmlTextReaderImpl = Mono.Xml2.XmlTextReader;
41 #else
42 using XmlTextReaderImpl = System.Xml.XmlTextReader;
43 #endif
45 namespace Mono.Xml
47 internal class DTDObjectModel
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;
55 DTDAutomataFactory factory;
56 DTDElementAutomata rootAutomata;
57 DTDEmptyAutomata emptyAutomata;
58 DTDAnyAutomata anyAutomata;
59 DTDInvalidAutomata invalidAutomata;
61 DTDElementDeclarationCollection elementDecls;
62 DTDAttListDeclarationCollection attListDecls;
63 DTDParameterEntityDeclarationCollection peDecls;
64 DTDEntityDeclarationCollection entityDecls;
65 DTDNotationDeclarationCollection notationDecls;
66 ArrayList validationErrors;
67 XmlResolver resolver;
68 XmlNameTable nameTable;
70 Hashtable externalResources;
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;
82 public DTDObjectModel (XmlNameTable nameTable)
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 ();
95 public string BaseURI {
96 get { return baseURI; }
97 set { baseURI = value; }
100 public bool IsStandalone {
101 get { return isStandalone; }
102 set { isStandalone = value; }
105 public string Name {
106 get { return name; }
107 set { name = value; }
110 public XmlNameTable NameTable {
111 get { return nameTable; }
114 public string PublicId {
115 get { return publicId; }
116 set { publicId = value; }
119 public string SystemId {
120 get { return systemId; }
121 set { systemId = value; }
124 public string InternalSubset {
125 get { return intSubset; }
126 set { intSubset = value; }
129 public bool InternalSubsetHasPEReference {
130 get { return intSubsetHasPERef; }
131 set { intSubsetHasPERef = value; }
134 public int LineNumber {
135 get { return lineNumber; }
136 set { lineNumber = value; }
139 public int LinePosition {
140 get { return linePosition; }
141 set { linePosition = value; }
144 public string ResolveEntity (string name)
146 DTDEntityDeclaration decl = EntityDecls [name]
147 as DTDEntityDeclaration;
148 if (decl == null) {
149 AddError (new XmlSchemaException ("Required entity was not found.",
150 this.LineNumber, this.LinePosition, null, this.BaseURI, null));
151 return " ";
153 else
154 return decl.EntityValue;
157 internal XmlResolver Resolver {
158 get { return resolver; }
161 public XmlResolver XmlResolver {
162 set { resolver = value; }
165 internal Hashtable ExternalResources {
166 get { return externalResources; }
169 public DTDAutomataFactory Factory {
170 get { return factory; }
173 public DTDElementDeclaration RootElement {
174 get { return ElementDecls [Name]; }
177 public DTDElementDeclarationCollection ElementDecls {
178 get { return elementDecls; }
181 public DTDAttListDeclarationCollection AttListDecls {
182 get { return attListDecls; }
185 public DTDEntityDeclarationCollection EntityDecls {
186 get { return entityDecls; }
189 public DTDParameterEntityDeclarationCollection PEDecls {
190 get { return peDecls; }
193 public DTDNotationDeclarationCollection NotationDecls {
194 get { return notationDecls; }
197 public DTDAutomata RootAutomata {
198 get {
199 if (rootAutomata == null)
200 rootAutomata = new DTDElementAutomata (this, this.Name);
201 return rootAutomata;
205 public DTDEmptyAutomata Empty {
206 get {
207 if (emptyAutomata == null)
208 emptyAutomata = new DTDEmptyAutomata (this);
209 return emptyAutomata;
213 public DTDAnyAutomata Any {
214 get {
215 if (anyAutomata == null)
216 anyAutomata = new DTDAnyAutomata (this);
217 return anyAutomata;
221 public DTDInvalidAutomata Invalid {
222 get {
223 if (invalidAutomata == null)
224 invalidAutomata = new DTDInvalidAutomata (this);
225 return invalidAutomata;
229 public XmlSchemaException [] Errors {
230 get { return validationErrors.ToArray (typeof (XmlSchemaException)) as XmlSchemaException []; }
233 public void AddError (XmlSchemaException ex)
235 validationErrors.Add (ex);
238 #if NET_2_0
239 internal string GenerateEntityAttributeText (string entityName)
241 DTDEntityDeclaration entity = EntityDecls [entityName] as DTDEntityDeclaration;
242 if (entity == null)
243 return null;
244 return entity.EntityValue;
247 internal XmlTextReaderImpl GenerateEntityContentReader (string entityName, XmlParserContext context)
249 DTDEntityDeclaration entity = EntityDecls [entityName] as DTDEntityDeclaration;
250 if (entity == null)
251 return null;
253 if (entity.SystemId != null) {
254 Uri baseUri = entity.BaseURI == null ? null : new Uri (entity.BaseURI);
255 Stream stream = resolver.GetEntity (resolver.ResolveUri (baseUri, entity.SystemId), null, typeof (Stream)) as Stream;
256 return new XmlTextReaderImpl (stream, XmlNodeType.Element, context);
258 else
259 return new XmlTextReaderImpl (entity.EntityValue, XmlNodeType.Element, context);
261 #endif
264 internal class DTDCollectionBase : DictionaryBase
266 DTDObjectModel root;
268 protected DTDCollectionBase (DTDObjectModel root)
270 this.root = root;
273 protected DTDObjectModel Root {
274 get { return root; }
277 public ICollection Keys {
278 get { return InnerHashtable.Keys; }
281 public ICollection Values {
282 get { return InnerHashtable.Values; }
286 internal class DTDElementDeclarationCollection : DTDCollectionBase
289 public DTDElementDeclarationCollection (DTDObjectModel root) : base (root) {}
291 public DTDElementDeclaration this [string name] {
292 get { return Get (name); }
295 public DTDElementDeclaration Get (string name)
297 return InnerHashtable [name] as DTDElementDeclaration;
300 public void Add (string name, DTDElementDeclaration decl)
302 if (InnerHashtable.Contains (name)) {
303 Root.AddError (new XmlSchemaException (String.Format (
304 "Element declaration for {0} was already added.",
305 name), null));
306 return;
308 decl.SetRoot (Root);
309 InnerHashtable.Add (name, decl);
313 internal class DTDAttListDeclarationCollection : DTDCollectionBase
315 public DTDAttListDeclarationCollection (DTDObjectModel root) : base (root) {}
317 public DTDAttListDeclaration this [string name] {
318 get { return InnerHashtable [name] as DTDAttListDeclaration; }
321 public void Add (string name, DTDAttListDeclaration decl)
323 DTDAttListDeclaration existing = this [name];
324 if (existing != null) {
325 // It is valid, that is additive declaration.
326 foreach (DTDAttributeDefinition def in decl.Definitions)
327 if (decl.Get (def.Name) == null)
328 existing.Add (def);
329 } else {
330 decl.SetRoot (Root);
331 InnerHashtable.Add (name, decl);
336 internal class DTDEntityDeclarationCollection : DTDCollectionBase
338 public DTDEntityDeclarationCollection (DTDObjectModel root) : base (root) {}
340 public DTDEntityDeclaration this [string name] {
341 get { return InnerHashtable [name] as DTDEntityDeclaration; }
344 public void Add (string name, DTDEntityDeclaration decl)
346 if (InnerHashtable [name] != null)
347 throw new InvalidOperationException (String.Format (
348 "Entity declaration for {0} was already added.",
349 name));
350 decl.SetRoot (Root);
351 InnerHashtable.Add (name, decl);
355 internal class DTDNotationDeclarationCollection : DTDCollectionBase
357 public DTDNotationDeclarationCollection (DTDObjectModel root) : base (root) {}
359 public DTDNotationDeclaration this [string name] {
360 get { return InnerHashtable [name] as DTDNotationDeclaration; }
363 public void Add (string name, DTDNotationDeclaration decl)
365 if (InnerHashtable [name] != null)
366 throw new InvalidOperationException (String.Format (
367 "Notation declaration for {0} was already added.",
368 name));
369 decl.SetRoot (Root);
370 InnerHashtable.Add (name, decl);
374 // This class contains either ElementName or ChildModels.
375 internal class DTDContentModel : DTDNode
377 DTDObjectModel root;
378 DTDAutomata compiledAutomata;
380 string ownerElementName;
381 string elementName;
382 DTDContentOrderType orderType = DTDContentOrderType.None;
383 DTDContentModelCollection childModels = new DTDContentModelCollection ();
384 DTDOccurence occurence = DTDOccurence.One;
386 internal DTDContentModel (DTDObjectModel root, string ownerElementName)
388 this.root = root;
389 this.ownerElementName = ownerElementName;
392 public DTDContentModelCollection ChildModels {
393 get { return childModels; }
394 set { childModels = value; }
397 public DTDElementDeclaration ElementDecl {
398 get { return root.ElementDecls [ownerElementName]; }
401 public string ElementName {
402 get { return elementName; }
403 set { elementName = value; }
406 public DTDOccurence Occurence {
407 get { return occurence; }
408 set { occurence = value; }
411 public DTDContentOrderType OrderType {
412 get { return orderType; }
413 set { orderType = value; }
416 public DTDAutomata GetAutomata ()
418 if (compiledAutomata == null)
419 Compile ();
420 return compiledAutomata;
423 public DTDAutomata Compile ()
425 compiledAutomata = CompileInternal ();
426 return compiledAutomata;
429 private DTDAutomata CompileInternal ()
431 if (ElementDecl.IsAny)
432 return root.Any;
433 if (ElementDecl.IsEmpty)
434 return root.Empty;
436 DTDAutomata basis = GetBasicContentAutomata ();
437 switch (Occurence) {
438 case DTDOccurence.One:
439 return basis;
440 case DTDOccurence.Optional:
441 return Choice (root.Empty, basis);
442 case DTDOccurence.OneOrMore:
443 return new DTDOneOrMoreAutomata (root, basis);
444 case DTDOccurence.ZeroOrMore:
445 return Choice (root.Empty, new DTDOneOrMoreAutomata (root, basis));
447 throw new InvalidOperationException ();
450 private DTDAutomata GetBasicContentAutomata ()
452 if (ElementName != null)
453 return new DTDElementAutomata (root, ElementName);
454 switch (ChildModels.Count) {
455 case 0:
456 return root.Empty;
457 case 1:
458 return ChildModels [0].GetAutomata ();
461 DTDAutomata current = null;
462 int childCount = ChildModels.Count;
463 switch (OrderType) {
464 case DTDContentOrderType.Seq:
465 current = Sequence (
466 ChildModels [childCount - 2].GetAutomata (),
467 ChildModels [childCount - 1].GetAutomata ());
468 for (int i = childCount - 2; i > 0; i--)
469 current = Sequence (
470 ChildModels [i - 1].GetAutomata (), current);
471 return current;
472 case DTDContentOrderType.Or:
473 current = Choice (
474 ChildModels [childCount - 2].GetAutomata (),
475 ChildModels [childCount - 1].GetAutomata ());
476 for (int i = childCount - 2; i > 0; i--)
477 current = Choice (
478 ChildModels [i - 1].GetAutomata (), current);
479 return current;
480 default:
481 throw new InvalidOperationException ("Invalid pattern specification");
485 private DTDAutomata Sequence (DTDAutomata l, DTDAutomata r)
487 return root.Factory.Sequence (l, r);
490 private DTDAutomata Choice (DTDAutomata l, DTDAutomata r)
492 return l.MakeChoice (r);
497 internal class DTDContentModelCollection
499 ArrayList contentModel = new ArrayList ();
501 public DTDContentModelCollection ()
505 public DTDContentModel this [int i] {
506 get { return contentModel [i] as DTDContentModel; }
509 public int Count {
510 get { return contentModel.Count; }
513 public void Add (DTDContentModel model)
515 contentModel.Add (model);
519 internal abstract class DTDNode : IXmlLineInfo
521 DTDObjectModel root;
522 bool isInternalSubset;
523 string baseURI;
524 int lineNumber;
525 int linePosition;
527 public virtual string BaseURI {
528 get { return baseURI; }
529 set { baseURI = value; }
532 public bool IsInternalSubset {
533 get { return isInternalSubset; }
534 set { isInternalSubset = value; }
537 public int LineNumber {
538 get { return lineNumber; }
539 set { lineNumber = value; }
542 public int LinePosition {
543 get { return linePosition; }
544 set { linePosition = value; }
547 public bool HasLineInfo ()
549 return lineNumber != 0;
552 internal void SetRoot (DTDObjectModel root)
554 this.root = root;
555 if (baseURI == null)
556 this.BaseURI = root.BaseURI;
559 protected DTDObjectModel Root {
560 get { return root; }
563 internal XmlException NotWFError (string message)
565 return new XmlException (this as IXmlLineInfo, BaseURI, message);
569 internal class DTDElementDeclaration : DTDNode
571 DTDObjectModel root;
572 DTDContentModel contentModel;
573 string name;
574 bool isEmpty;
575 bool isAny;
576 bool isMixedContent;
578 internal DTDElementDeclaration (DTDObjectModel root)
580 this.root = root;
583 public string Name {
584 get { return name; }
585 set { name = value; }
587 public bool IsEmpty {
588 get { return isEmpty; }
589 set { isEmpty = value; }
592 public bool IsAny {
593 get { return isAny; }
594 set { isAny = value; }
597 public bool IsMixedContent {
598 get { return isMixedContent; }
599 set { isMixedContent = value; }
602 public DTDContentModel ContentModel {
603 get {
604 if (contentModel == null)
605 contentModel = new DTDContentModel (root, Name);
606 return contentModel;
610 public DTDAttListDeclaration Attributes {
611 get {
612 return Root.AttListDecls [Name];
617 internal class DTDAttributeDefinition : DTDNode
619 string name;
620 XmlSchemaDatatype datatype;
621 ArrayList enumeratedLiterals;
622 string unresolvedDefault;
623 ArrayList enumeratedNotations;
624 DTDAttributeOccurenceType occurenceType = DTDAttributeOccurenceType.None;
625 string resolvedDefaultValue;
626 string resolvedNormalizedDefaultValue;
628 internal DTDAttributeDefinition (DTDObjectModel root)
630 this.SetRoot (root);
633 public string Name {
634 get { return name; }
635 set { name =value; }
638 public XmlSchemaDatatype Datatype {
639 get { return datatype; }
640 set { datatype = value; }
643 public DTDAttributeOccurenceType OccurenceType {
644 get { return this.occurenceType; }
645 set { this.occurenceType = value; }
648 // entity reference inside enumerated values are not allowed,
649 // but on the other hand, they are allowed inside default value.
650 // Then I decided to use string ArrayList for enumerated values,
651 // and unresolved string value for DefaultValue.
652 public ArrayList EnumeratedAttributeDeclaration {
653 get {
654 if (enumeratedLiterals == null)
655 enumeratedLiterals = new ArrayList ();
656 return this.enumeratedLiterals;
660 public ArrayList EnumeratedNotations {
661 get {
662 if (enumeratedNotations == null)
663 enumeratedNotations = new ArrayList ();
664 return this.enumeratedNotations;
668 public string DefaultValue {
669 get {
670 if (resolvedDefaultValue == null)
671 resolvedDefaultValue = ComputeDefaultValue ();
672 return resolvedDefaultValue;
676 public string NormalizedDefaultValue {
677 get {
678 if (resolvedNormalizedDefaultValue == null) {
679 string s = ComputeDefaultValue ();
680 try {
681 object o = Datatype.ParseValue (s, null, null);
682 resolvedNormalizedDefaultValue =
683 (o is string []) ?
684 String.Join (" ", (string []) o) :
685 o is IFormattable ? ((IFormattable) o).ToString (null, CultureInfo.InvariantCulture) : o.ToString ();
686 } catch (Exception) {
687 // This is for non-error-reporting reader
688 resolvedNormalizedDefaultValue = Datatype.Normalize (s);
691 return resolvedNormalizedDefaultValue;
695 public string UnresolvedDefaultValue {
696 get { return this.unresolvedDefault; }
697 set { this.unresolvedDefault = value; }
700 public char QuoteChar {
701 get {
702 return UnresolvedDefaultValue.Length > 0 ?
703 this.UnresolvedDefaultValue [0] :
704 '"';
708 internal string ComputeDefaultValue ()
710 if (UnresolvedDefaultValue == null)
711 return null;
713 StringBuilder sb = new StringBuilder ();
714 int pos = 0;
715 int next = 0;
716 string value = this.UnresolvedDefaultValue;
717 while ((next = value.IndexOf ('&', pos)) >= 0) {
718 int semicolon = value.IndexOf (';', next);
719 if (value [next + 1] == '#') {
720 // character reference.
721 char c = value [next + 2];
722 NumberStyles style = NumberStyles.Integer;
723 string spec;
724 if (c == 'x' || c == 'X') {
725 spec = value.Substring (next + 3, semicolon - next - 3);
726 style |= NumberStyles.HexNumber;
728 else
729 spec = value.Substring (next + 2, semicolon - next - 2);
730 sb.Append ((char) int.Parse (spec, style, CultureInfo.InvariantCulture));
731 } else {
732 sb.Append (value.Substring (pos, next - 1));
733 string name = value.Substring (next + 1, semicolon - 2);
734 int predefined = XmlChar.GetPredefinedEntity (name);
735 if (predefined >= 0)
736 sb.Append (predefined);
737 else
738 sb.Append (Root.ResolveEntity (name));
740 pos = semicolon + 1;
742 sb.Append (value.Substring (pos));
743 // strip quote chars
744 string ret = sb.ToString (1, sb.Length - 2);
745 sb.Length = 0;
746 return ret;
751 internal class DTDAttListDeclaration : DTDNode
753 string name;
754 Hashtable attributeOrders = new Hashtable ();
755 ArrayList attributes = new ArrayList ();
757 internal DTDAttListDeclaration (DTDObjectModel root)
759 SetRoot (root);
762 public string Name {
763 get { return name; }
764 set { name = value; }
767 public DTDAttributeDefinition this [int i] {
768 get { return Get (i); }
771 public DTDAttributeDefinition this [string name] {
772 get { return Get (name); }
775 public DTDAttributeDefinition Get (int i)
777 return attributes [i] as DTDAttributeDefinition;
780 public DTDAttributeDefinition Get (string name)
782 object o = attributeOrders [name];
783 if (o != null)
784 return attributes [(int) o] as DTDAttributeDefinition;
785 else
786 return null;
789 public IList Definitions {
790 get { return attributes; }
793 public void Add (DTDAttributeDefinition def)
795 if (attributeOrders [def.Name] != null)
796 throw new InvalidOperationException (String.Format (
797 "Attribute definition for {0} was already added at element {1}.",
798 def.Name, this.Name));
799 def.SetRoot (Root);
800 attributeOrders.Add (def.Name, attributes.Count);
801 attributes.Add (def);
804 public int Count {
805 get { return attributeOrders.Count; }
809 internal class DTDEntityBase : DTDNode
811 string name;
812 string publicId;
813 string systemId;
814 string literalValue;
815 string replacementText;
816 bool isInvalid;
817 Exception loadException;
818 bool loadFailed;
820 protected DTDEntityBase (DTDObjectModel root)
822 SetRoot (root);
825 internal bool IsInvalid {
826 get { return isInvalid; }
827 set { isInvalid = value; }
830 public bool LoadFailed {
831 get { return loadFailed; }
832 set { loadFailed = value; }
835 public string Name {
836 get { return name; }
837 set { name = value; }
840 public string PublicId {
841 get { return publicId; }
842 set { publicId = value; }
845 public string SystemId {
846 get { return systemId; }
847 set { systemId = value; }
850 public string LiteralEntityValue {
851 get { return literalValue; }
852 set { literalValue = value; }
855 public string ReplacementText {
856 get { return replacementText; }
857 set { replacementText = value; }
860 public void Resolve (XmlResolver resolver)
862 if (resolver == null || SystemId == null || SystemId.Length == 0) {
863 LoadFailed = true;
864 LiteralEntityValue = String.Empty;
865 return;
868 Uri baseUri = null;
869 try {
870 if (BaseURI != null && BaseURI.Length > 0)
871 baseUri = new Uri (BaseURI);
872 } catch (UriFormatException) {
875 Uri absUri = resolver.ResolveUri (baseUri, SystemId);
876 string absPath = absUri.ToString ();
877 if (Root.ExternalResources.ContainsKey (absPath))
878 LiteralEntityValue = (string) Root.ExternalResources [absPath];
879 Stream s = null;
880 try {
881 s = resolver.GetEntity (absUri, null, typeof (Stream)) as Stream;
882 XmlTextReaderImpl xtr = new XmlTextReaderImpl (absPath, s, Root.NameTable);
883 // Don't skip Text declaration here. LiteralEntityValue contains it. See spec 4.5
884 this.BaseURI = absPath;
885 LiteralEntityValue = xtr.GetRemainder ().ReadToEnd ();
887 Root.ExternalResources.Add (absPath, LiteralEntityValue);
888 if (Root.ExternalResources.Count > DTDObjectModel.AllowedExternalEntitiesMax)
889 throw new InvalidOperationException ("The total amount of external entities exceeded the allowed number.");
891 } catch (Exception ex) {
892 loadException = ex;
893 LiteralEntityValue = String.Empty;
894 LoadFailed = true;
895 // throw NotWFError ("Cannot resolve external entity. URI is " + absPath + " .");
896 } finally {
897 if (s != null)
898 s.Close ();
903 internal class DTDEntityDeclaration : DTDEntityBase
905 string entityValue;
906 string notationName;
908 ArrayList ReferencingEntities = new ArrayList ();
910 bool scanned;
911 bool recursed;
912 bool hasExternalReference;
914 internal DTDEntityDeclaration (DTDObjectModel root) : base (root)
918 public string NotationName {
919 get { return notationName; }
920 set { notationName = value; }
923 public bool HasExternalReference {
924 get {
925 if (!scanned)
926 ScanEntityValue (new ArrayList ());
927 return hasExternalReference;
931 public string EntityValue {
932 get {
933 if (this.IsInvalid)
934 return String.Empty;
936 if (PublicId == null && SystemId == null && LiteralEntityValue == null)
937 return String.Empty;
939 if (entityValue == null) {
940 if (NotationName != null)
941 entityValue = "";
942 else if (SystemId == null || SystemId == String.Empty) {
943 entityValue = ReplacementText;
944 if (entityValue == null)
945 entityValue = String.Empty;
946 } else {
947 entityValue = ReplacementText;
949 // Check illegal recursion.
950 ScanEntityValue (new ArrayList ());
952 return entityValue;
956 // It returns whether the entity contains references to external entities.
957 public void ScanEntityValue (ArrayList refs)
959 // To modify this code, beware nesting between this and EntityValue.
960 string value = EntityValue;
961 if (this.SystemId != null)
962 hasExternalReference = true;
964 if (recursed)
965 throw NotWFError ("Entity recursion was found.");
966 recursed = true;
968 if (scanned) {
969 foreach (string referenced in refs)
970 if (this.ReferencingEntities.Contains (referenced))
971 throw NotWFError (String.Format (
972 "Nested entity was found between {0} and {1}",
973 referenced, Name));
974 recursed = false;
975 return;
978 int len = value.Length;
979 int start = 0;
980 for (int i=0; i<len; i++) {
981 switch (value [i]) {
982 case '&':
983 start = i+1;
984 break;
985 case ';':
986 if (start == 0)
987 break;
988 string name = value.Substring (start, i - start);
989 if (name.Length == 0)
990 throw NotWFError ("Entity reference name is missing.");
991 if (name [0] == '#')
992 break; // character reference
993 if (XmlChar.GetPredefinedEntity (name) >= 0)
994 break; // predefined reference
996 this.ReferencingEntities.Add (name);
997 DTDEntityDeclaration decl = Root.EntityDecls [name];
998 if (decl != null) {
999 if (decl.SystemId != null)
1000 hasExternalReference = true;
1001 refs.Add (Name);
1002 decl.ScanEntityValue (refs);
1003 foreach (string str in decl.ReferencingEntities)
1004 ReferencingEntities.Add (str);
1005 refs.Remove (Name);
1006 value = value.Remove (start - 1, name.Length + 2);
1007 value = value.Insert (start - 1, decl.EntityValue);
1008 i -= name.Length + 1; // not +2, because of immediate i++ .
1009 len = value.Length;
1011 start = 0;
1012 break;
1015 if (start != 0)
1016 Root.AddError (new XmlSchemaException ("Invalid reference character '&' is specified.",
1017 this.LineNumber, this.LinePosition, null, this.BaseURI, null));
1018 scanned = true;
1019 recursed = false;
1023 internal class DTDNotationDeclaration : DTDNode
1025 string name;
1026 string localName;
1027 string prefix;
1028 string publicId;
1029 string systemId;
1031 public string Name {
1032 get { return name; }
1033 set { name = value; }
1036 public string PublicId {
1037 get { return publicId; }
1038 set { publicId = value; }
1041 public string SystemId {
1042 get { return systemId; }
1043 set { systemId = value; }
1046 public string LocalName {
1047 get { return localName; }
1048 set { localName = value; }
1051 public string Prefix {
1052 get { return prefix; }
1053 set { prefix = value; }
1056 internal DTDNotationDeclaration (DTDObjectModel root)
1058 SetRoot (root);
1062 internal class DTDParameterEntityDeclarationCollection
1064 Hashtable peDecls = new Hashtable ();
1065 DTDObjectModel root;
1067 public DTDParameterEntityDeclarationCollection (DTDObjectModel root)
1069 this.root = root;
1072 public DTDParameterEntityDeclaration this [string name] {
1073 get { return peDecls [name] as DTDParameterEntityDeclaration; }
1076 public void Add (string name, DTDParameterEntityDeclaration decl)
1078 // PEDecl can be overriden.
1079 if (peDecls [name] != null)
1080 return;
1081 decl.SetRoot (root);
1082 peDecls.Add (name, decl);
1085 public ICollection Keys {
1086 get { return peDecls.Keys; }
1089 public ICollection Values {
1090 get { return peDecls.Values; }
1094 internal class DTDParameterEntityDeclaration : DTDEntityBase
1096 internal DTDParameterEntityDeclaration (DTDObjectModel root) : base (root)
1101 internal enum DTDContentOrderType
1103 None,
1104 Seq,
1108 internal enum DTDAttributeOccurenceType
1110 None,
1111 Required,
1112 Optional,
1113 Fixed
1116 internal enum DTDOccurence
1118 One,
1119 Optional,
1120 ZeroOrMore,
1121 OneOrMore