2005-12-05 Lluis Sanchez Gual <lluis@novell.com>
[mono.git] / mcs / class / System.XML / System.Xml / DTDReader.cs
1 //
2 // System.Xml.DTDReader
3 //
4 // Author:
5 //   Atsushi Enomoto  (ginga@kit.hi-ho.ne.jp)
6 //
7 // (C)2003 Atsushi Enomoto
8 // (C)2004 Novell Inc.
9 //
10 // FIXME:
11 //      When a parameter entity contains cp section, it should be closed 
12 //      within that declaration.
13 //
14 //      Resolution to external entities from different BaseURI fails (it is
15 //      the same as MS.NET 1.1, but should be fixed in the future).
16 //
17
18 //
19 // Permission is hereby granted, free of charge, to any person obtaining
20 // a copy of this software and associated documentation files (the
21 // "Software"), to deal in the Software without restriction, including
22 // without limitation the rights to use, copy, modify, merge, publish,
23 // distribute, sublicense, and/or sell copies of the Software, and to
24 // permit persons to whom the Software is furnished to do so, subject to
25 // the following conditions:
26 // 
27 // The above copyright notice and this permission notice shall be
28 // included in all copies or substantial portions of the Software.
29 // 
30 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
31 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
32 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
33 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
34 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
35 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
36 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 //
38
39 using System;
40 using System.Collections;
41 using System.Globalization;
42 using System.IO;
43 using System.Text;
44 using Mono.Xml;
45 using System.Xml.Schema;
46
47 namespace System.Xml
48 {
49         internal class DTDReader : IXmlLineInfo
50         {
51                 private XmlParserInput currentInput;
52                 private Stack parserInputStack;
53
54                 private char [] nameBuffer;
55                 private int nameLength;
56                 private int nameCapacity;
57                 private const int initialNameCapacity = 256;
58
59                 private StringBuilder valueBuffer;
60
61                 private int currentLinkedNodeLineNumber;
62                 private int currentLinkedNodeLinePosition;
63
64                 // Parameter entity placeholder
65                 private int dtdIncludeSect;
66
67                 private bool normalization;
68
69                 private bool processingInternalSubset;
70
71                 string cachedPublicId;
72                 string cachedSystemId;
73
74                 DTDObjectModel DTD;
75
76 #if DTD_HANDLE_EVENTS
77                 public event ValidationEventHandler ValidationEventHandler;
78 #endif
79
80                 // .ctor()
81
82                 public DTDReader (DTDObjectModel dtd,
83                         int startLineNumber, 
84                         int startLinePosition)
85                 {
86                         this.DTD = dtd;
87                         currentLinkedNodeLineNumber = startLineNumber;
88                         currentLinkedNodeLinePosition = startLinePosition;
89                         Init ();
90                 }
91
92                 // Properties
93
94                 public string BaseURI {
95                         get { return currentInput.BaseURI; }
96                 }
97
98                 public bool Normalization {
99                         get { return normalization; }
100                         set { normalization = value; }
101                 }
102
103                 public int LineNumber {
104                         get { return currentInput.LineNumber; }
105                 }
106
107                 public int LinePosition {
108                         get { return currentInput.LinePosition; }
109                 }
110
111                 public bool HasLineInfo ()
112                 {
113                         return true;
114                 }
115
116                 // Methods
117
118                 private XmlException NotWFError (string message)
119                 {
120                         return new XmlException (this as IXmlLineInfo, BaseURI, message);
121                 }
122
123                 private void Init ()
124                 {
125                         parserInputStack = new Stack ();
126
127                         nameBuffer = new char [initialNameCapacity];
128                         nameLength = 0;
129                         nameCapacity = initialNameCapacity;
130                         
131                         valueBuffer = new StringBuilder (512);
132                 }
133
134                 internal DTDObjectModel GenerateDTDObjectModel ()
135                 {
136                         // now compile DTD
137                         int originalParserDepth = parserInputStack.Count;
138                         bool more;
139                         if (DTD.InternalSubset != null && DTD.InternalSubset.Length > 0) {
140                                 this.processingInternalSubset = true;
141                                 XmlParserInput original = currentInput;
142
143                                 currentInput = new XmlParserInput (
144                                         new StringReader (DTD.InternalSubset),
145                                         DTD.BaseURI,
146                                         currentLinkedNodeLineNumber,
147                                         currentLinkedNodeLinePosition);
148                                 currentInput.AllowTextDecl = false;
149                                 do {
150                                         more = ProcessDTDSubset ();
151                                         if (PeekChar () == -1 && parserInputStack.Count > 0)
152                                                 PopParserInput ();
153                                 } while (more || parserInputStack.Count > originalParserDepth);
154                                 if (dtdIncludeSect != 0)
155                                         throw NotWFError ("INCLUDE section is not ended correctly.");
156
157                                 currentInput = original;
158                                 this.processingInternalSubset = false;
159                         }
160                         if (DTD.SystemId != null && DTD.SystemId != String.Empty && DTD.Resolver != null) {
161                                 PushParserInput (DTD.SystemId);
162                                 do {
163                                         more = ProcessDTDSubset ();
164                                         if (PeekChar () == -1 && parserInputStack.Count > 1)
165                                                 PopParserInput ();
166                                 } while (more || parserInputStack.Count > originalParserDepth + 1);
167                                 if (dtdIncludeSect != 0)
168                                         throw NotWFError ("INCLUDE section is not ended correctly.");
169
170                                 PopParserInput ();
171                         }
172                         ArrayList sc = new ArrayList ();
173
174                         // Entity recursion check.
175                         foreach (DTDEntityDeclaration ent in DTD.EntityDecls.Values) {
176                                 if (ent.NotationName != null) {
177                                         ent.ScanEntityValue (sc);
178                                         sc.Clear ();
179                                 }
180                         }
181                         // release unnecessary memory usage
182                         DTD.ExternalResources.Clear ();
183
184                         return DTD;
185                 }
186
187                 // Read any one of following:
188                 //   elementdecl, AttlistDecl, EntityDecl, NotationDecl,
189                 //   PI, Comment, Parameter Entity, or doctype termination char(']')
190                 //
191                 // Returns true if it may have any more contents, or false if not.
192                 private bool ProcessDTDSubset ()
193                 {
194                         SkipWhitespace ();
195                         int c2 = ReadChar ();
196                         switch(c2)
197                         {
198                         case -1:
199                                 return false;
200                         case '%':
201                                 // It affects on entity references' well-formedness
202                                 if (this.processingInternalSubset)
203                                         DTD.InternalSubsetHasPEReference = true;
204                                 string peName = ReadName ();
205                                 Expect (';');
206                                 DTDParameterEntityDeclaration peDecl = GetPEDecl (peName);
207                                 if (peDecl == null)
208                                         break;
209                                 currentInput.PushPEBuffer (peDecl);
210 //                              int currentLine = currentInput.LineNumber;
211 //                              int currentColumn = currentInput.LinePosition;
212                                 while (currentInput.HasPEBuffer)
213                                         ProcessDTDSubset ();
214                                 SkipWhitespace ();
215                                 // FIXME: Implement correct nest-level check.
216                                 // Don't depend on lineinfo (might not be supplied)
217 //                              if (currentInput.LineNumber != currentLine ||
218 //                                      currentInput.LinePosition != currentColumn)
219 //                                      throw NotWFError ("Incorrectly nested parameter entity.");
220                                 break;
221                         case '<':
222                                 int c = ReadChar ();
223                                 switch(c)
224                                 {
225                                 case '?':
226                                         // Only read, no store.
227                                         ReadProcessingInstruction ();
228                                         break;
229                                 case '!':
230                                         CompileDeclaration ();
231                                         break;
232                                 case -1:
233                                         throw NotWFError ("Unexpected end of stream.");
234                                 default:
235                                         throw NotWFError ("Syntax Error after '<' character: " + (char) c);
236                                 }
237                                 break;
238                         case ']':
239                                 if (dtdIncludeSect == 0)
240                                         throw NotWFError ("Unbalanced end of INCLUDE/IGNORE section.");
241                                 // End of inclusion
242                                 Expect ("]>");
243                                 dtdIncludeSect--;
244                                 SkipWhitespace ();
245                                 break;
246                         default:
247                                 throw NotWFError (String.Format ("Syntax Error inside doctypedecl markup : {0}({1})", c2, (char) c2));
248                         }
249                         currentInput.AllowTextDecl = false;
250                         return true;
251                 }
252
253                 private void CompileDeclaration ()
254                 {
255                         switch(ReadChar ())
256                         {
257                         case '-':
258                                 Expect ('-');
259                                 // Only read, no store.
260                                 ReadComment ();
261                                 break;
262                         case 'E':
263                                 switch(ReadChar ())
264                                 {
265                                 case 'N':
266                                         Expect ("TITY");
267                                         if (!SkipWhitespace ())
268                                                 throw NotWFError (
269                                                         "Whitespace is required after '<!ENTITY' in DTD entity declaration.");
270                                         LOOPBACK:
271                                         if (PeekChar () == '%') {
272                                                 ReadChar ();
273                                                 if (!SkipWhitespace ()) {
274                                                         ExpandPERef ();
275                                                         goto LOOPBACK;
276                                                 } else {
277                                                         // FIXME: Is this allowed? <!ENTITY % %name; ...> 
278                                                         // (i.e. Can PE name be replaced by another PE?)
279                                                         TryExpandPERef ();
280                                                         if (XmlChar.IsNameChar (PeekChar ()))
281                                                                 ReadParameterEntityDecl ();
282                                                         else
283                                                                 throw NotWFError ("expected name character");
284                                                 }
285                                                 break;
286                                         }
287                                         DTDEntityDeclaration ent = ReadEntityDecl ();
288                                         if (DTD.EntityDecls [ent.Name] == null)
289                                                 DTD.EntityDecls.Add (ent.Name, ent);
290                                         break;
291                                 case 'L':
292                                         Expect ("EMENT");
293                                         DTDElementDeclaration el = ReadElementDecl ();
294                                         DTD.ElementDecls.Add (el.Name, el);
295                                         break;
296                                 default:
297                                         throw NotWFError ("Syntax Error after '<!E' (ELEMENT or ENTITY must be found)");
298                                 }
299                                 break;
300                         case 'A':
301                                 Expect ("TTLIST");
302                                 DTDAttListDeclaration atl = ReadAttListDecl ();
303                                 DTD.AttListDecls.Add (atl.Name, atl);
304                                 break;
305                         case 'N':
306                                 Expect ("OTATION");
307                                 DTDNotationDeclaration not = ReadNotationDecl ();
308                                 DTD.NotationDecls.Add (not.Name, not);
309                                 break;
310                         case '[':
311                                 // conditional sections
312                                 SkipWhitespace ();
313                                 TryExpandPERef ();
314                                 Expect ('I');
315                                 switch (ReadChar ()) {
316                                 case 'N':
317                                         Expect ("CLUDE");
318                                         ExpectAfterWhitespace ('[');
319                                         dtdIncludeSect++;
320                                         break;
321                                 case 'G':
322                                         Expect ("NORE");
323                                         ReadIgnoreSect ();
324                                         break;
325                                 }
326                                 break;
327                         default:
328                                 throw NotWFError ("Syntax Error after '<!' characters.");
329                         }
330                 }
331
332                 private void ReadIgnoreSect ()
333                 {
334                         ExpectAfterWhitespace ('[');
335                         int dtdIgnoreSect = 1;
336
337                         while (dtdIgnoreSect > 0) {
338                                 switch (ReadChar ()) {
339                                 case -1:
340                                         throw NotWFError ("Unexpected IGNORE section end.");
341                                 case '<':
342                                         if (PeekChar () != '!')
343                                                 break;
344                                         ReadChar ();
345                                         if (PeekChar () != '[')
346                                                 break;
347                                         ReadChar ();
348                                         dtdIgnoreSect++;
349                                         break;
350                                 case ']':
351                                         if (PeekChar () != ']')
352                                                 break;
353                                         ReadChar ();
354                                         if (PeekChar () != '>')
355                                                 break;
356                                         ReadChar ();
357                                                 dtdIgnoreSect--;
358                                         break;
359                                 }
360                         }
361                         if (dtdIgnoreSect != 0)
362                                 throw NotWFError ("IGNORE section is not ended correctly.");
363                 }
364
365                 // The reader is positioned on the head of the name.
366                 private DTDElementDeclaration ReadElementDecl ()
367                 {
368                         DTDElementDeclaration decl = new DTDElementDeclaration (DTD);
369                         decl.IsInternalSubset = this.processingInternalSubset;
370
371                         if (!SkipWhitespace ())
372                                 throw NotWFError ("Whitespace is required between '<!ELEMENT' and name in DTD element declaration.");
373                         TryExpandPERef ();
374                         decl.Name = ReadName ();
375                         if (!SkipWhitespace ())
376                                 throw NotWFError ("Whitespace is required between name and content in DTD element declaration.");
377                         TryExpandPERef ();
378                         ReadContentSpec (decl);
379                         SkipWhitespace ();
380                         // This expanding is only allowed as a non-validating parser.
381                         TryExpandPERef ();
382                         Expect ('>');
383                         return decl;
384                 }
385
386                 // read 'children'(BNF) of contentspec
387                 private void ReadContentSpec (DTDElementDeclaration decl)
388                 {
389                         TryExpandPERef ();
390                         switch(ReadChar ())
391                         {
392                         case 'E':
393                                 decl.IsEmpty = true;
394                                 Expect ("MPTY");
395                                 break;
396                         case 'A':
397                                 decl.IsAny = true;
398                                 Expect ("NY");
399                                 break;
400                         case '(':
401                                 DTDContentModel model = decl.ContentModel;
402                                 SkipWhitespace ();
403                                 TryExpandPERef ();
404                                 if(PeekChar () == '#') {
405                                         // Mixed Contents. "#PCDATA" must appear first.
406                                         decl.IsMixedContent = true;
407                                         model.Occurence = DTDOccurence.ZeroOrMore;
408                                         model.OrderType = DTDContentOrderType.Or;
409                                         Expect ("#PCDATA");
410                                         SkipWhitespace ();
411                                         TryExpandPERef ();
412                                         while(PeekChar () != ')') {
413                                                 SkipWhitespace ();
414                                                 if (PeekChar () == '%') {
415                                                         TryExpandPERef ();
416                                                         continue;
417                                                 }
418                                                 Expect('|');
419                                                 SkipWhitespace ();
420                                                 TryExpandPERef ();
421                                                 DTDContentModel elem = new DTDContentModel (DTD, decl.Name);
422 //                                              elem.LineNumber = currentInput.LineNumber;
423 //                                              elem.LinePosition = currentInput.LinePosition;
424                                                 elem.ElementName = ReadName ();
425                                                 this.AddContentModel (model.ChildModels, elem);
426                                                 SkipWhitespace ();
427                                                 TryExpandPERef ();
428                                         }
429                                         Expect (')');
430                                         if (model.ChildModels.Count > 0)
431                                                 Expect ('*');
432                                         else if (PeekChar () == '*')
433                                                 Expect ('*');
434                                 } else {
435                                         // Non-Mixed Contents
436                                         model.ChildModels.Add (ReadCP (decl));
437                                         SkipWhitespace ();
438
439                                         do {    // copied from ReadCP() ...;-)
440                                                 if (PeekChar () == '%') {
441                                                         TryExpandPERef ();
442                                                         continue;
443                                                 }
444                                                 if(PeekChar ()=='|') {
445                                                         // CPType=Or
446                                                         if (model.OrderType == DTDContentOrderType.Seq)
447                                                                 throw NotWFError ("Inconsistent choice markup in sequence cp.");
448                                                         model.OrderType = DTDContentOrderType.Or;
449                                                         ReadChar ();
450                                                         SkipWhitespace ();
451                                                         AddContentModel (model.ChildModels, ReadCP (decl));
452                                                         SkipWhitespace ();
453                                                 }
454                                                 else if(PeekChar () == ',')
455                                                 {
456                                                         // CPType=Seq
457                                                         if (model.OrderType == DTDContentOrderType.Or)
458                                                                 throw NotWFError ("Inconsistent sequence markup in choice cp.");
459                                                         model.OrderType = DTDContentOrderType.Seq;
460                                                         ReadChar ();
461                                                         SkipWhitespace ();
462                                                         model.ChildModels.Add (ReadCP (decl));
463                                                         SkipWhitespace ();
464                                                 }
465                                                 else
466                                                         break;
467                                         }
468                                         while(true);
469
470                                         Expect (')');
471                                         switch(PeekChar ())
472                                         {
473                                         case '?':
474                                                 model.Occurence = DTDOccurence.Optional;
475                                                 ReadChar ();
476                                                 break;
477                                         case '*':
478                                                 model.Occurence = DTDOccurence.ZeroOrMore;
479                                                 ReadChar ();
480                                                 break;
481                                         case '+':
482                                                 model.Occurence = DTDOccurence.OneOrMore;
483                                                 ReadChar ();
484                                                 break;
485                                         }
486                                         SkipWhitespace ();
487                                 }
488                                 SkipWhitespace ();
489                                 break;
490                         default:
491                                 throw NotWFError ("ContentSpec is missing.");
492                         }
493                 }
494
495                 // Read 'cp' (BNF) of contentdecl (BNF)
496                 private DTDContentModel ReadCP (DTDElementDeclaration elem)
497                 {
498                         DTDContentModel model = null;
499                         TryExpandPERef ();
500                         if(PeekChar () == '(') {
501                                 model = new DTDContentModel (DTD, elem.Name);
502                                 ReadChar ();
503                                 SkipWhitespace ();
504                                 model.ChildModels.Add (ReadCP (elem));
505                                 SkipWhitespace ();
506                                 do {
507                                         if (PeekChar () == '%') {
508                                                 TryExpandPERef ();
509                                                 continue;
510                                         }
511                                         if(PeekChar ()=='|') {
512                                                 // CPType=Or
513                                                 if (model.OrderType == DTDContentOrderType.Seq)
514                                                         throw NotWFError ("Inconsistent choice markup in sequence cp.");
515                                                 model.OrderType = DTDContentOrderType.Or;
516                                                 ReadChar ();
517                                                 SkipWhitespace ();
518                                                 AddContentModel (model.ChildModels, ReadCP (elem));
519                                                 SkipWhitespace ();
520                                         }
521                                         else if(PeekChar () == ',') {
522                                                 // CPType=Seq
523                                                 if (model.OrderType == DTDContentOrderType.Or)
524                                                         throw NotWFError ("Inconsistent sequence markup in choice cp.");
525                                                 model.OrderType = DTDContentOrderType.Seq;
526                                                 ReadChar ();
527                                                 SkipWhitespace ();
528                                                 model.ChildModels.Add (ReadCP (elem));
529                                                 SkipWhitespace ();
530                                         }
531                                         else
532                                                 break;
533                                 }
534                                 while(true);
535                                 ExpectAfterWhitespace (')');
536                         }
537                         else {
538                                 TryExpandPERef ();
539                                 model = new DTDContentModel (DTD, elem.Name);
540                                 model.ElementName = ReadName ();
541                         }
542
543                         switch(PeekChar ()) {
544                         case '?':
545                                 model.Occurence = DTDOccurence.Optional;
546                                 ReadChar ();
547                                 break;
548                         case '*':
549                                 model.Occurence = DTDOccurence.ZeroOrMore;
550                                 ReadChar ();
551                                 break;
552                         case '+':
553                                 model.Occurence = DTDOccurence.OneOrMore;
554                                 ReadChar ();
555                                 break;
556                         }
557                         return model;
558                 }
559
560                 private void AddContentModel (DTDContentModelCollection cmc, DTDContentModel cm)
561                 {
562                         if (cm.ElementName != null) {
563                                 for (int i = 0; i < cmc.Count; i++) {
564                                         if (cmc [i].ElementName == cm.ElementName) {
565                                                 HandleError (new XmlSchemaException ("Element content must be unique inside mixed content model.",
566                                                         this.LineNumber,
567                                                         this.LinePosition,
568                                                         null,
569                                                         this.BaseURI,
570                                                         null));
571                                                 return;
572                                         }
573                                 }
574                         }
575                         cmc.Add (cm);
576                 }
577
578                 // The reader is positioned on the first name char.
579                 private void ReadParameterEntityDecl ()
580                 {
581                         DTDParameterEntityDeclaration decl = 
582                                 new DTDParameterEntityDeclaration (DTD);
583                         decl.BaseURI = BaseURI;
584                         decl.XmlResolver = DTD.Resolver;
585
586                         decl.Name = ReadName ();
587                         if (!SkipWhitespace ())
588                                 throw NotWFError ("Whitespace is required after name in DTD parameter entity declaration.");
589
590                         if (PeekChar () == 'S' || PeekChar () == 'P') {
591                                 // read publicId/systemId
592                                 ReadExternalID ();
593                                 decl.PublicId = cachedPublicId;
594                                 decl.SystemId = cachedSystemId;
595                                 SkipWhitespace ();
596                                 decl.Resolve ();
597
598                                 ResolveExternalEntityReplacementText (decl);
599                         } else {
600                                 TryExpandPERef ();
601                                 int quoteChar = ReadChar ();
602                                 if (quoteChar != '\'' && quoteChar != '"')
603                                         throw NotWFError ("quotation char was expected.");
604                                 ClearValueBuffer ();
605                                 bool loop = true;
606                                 while (loop) {
607                                         int c = ReadChar ();
608                                         switch (c) {
609                                         case -1:
610                                                 throw NotWFError ("unexpected end of stream in entity value definition.");
611                                         case '"':
612                                                 if (quoteChar == '"')
613                                                         loop = false;
614                                                 else
615                                                         AppendValueChar ('"');
616                                                 break;
617                                         case '\'':
618                                                 if (quoteChar == '\'')
619                                                         loop = false;
620                                                 else
621                                                         AppendValueChar ('\'');
622                                                 break;
623                                         default:
624                                                 if (XmlChar.IsInvalid (c))
625                                                         throw NotWFError ("Invalid character was used to define parameter entity.");
626                                                 AppendValueChar (c);
627                                                 break;
628                                         }
629                                 }
630                                 decl.LiteralEntityValue = CreateValueString ();
631                                 ClearValueBuffer ();
632                                 ResolveInternalEntityReplacementText (decl);
633                         }
634                         ExpectAfterWhitespace ('>');
635
636
637                         if (DTD.PEDecls [decl.Name] == null) {
638                                 DTD.PEDecls.Add (decl.Name, decl);
639                         }
640                 }
641
642                 private void ResolveExternalEntityReplacementText (DTDEntityBase decl)
643                 {
644                         if (decl.SystemId != null && decl.SystemId.Length > 0) {
645                                 // FIXME: not always it should be read in Element context
646                                 XmlTextReader xtr = new XmlTextReader (decl.LiteralEntityValue, XmlNodeType.Element, null);
647                                 if (decl is DTDEntityDeclaration && DTD.EntityDecls [decl.Name] == null) {
648                                         // GE - also checked as valid contents
649                                         StringBuilder sb = new StringBuilder ();
650                                         xtr.Normalization = this.Normalization;
651                                         xtr.Read ();
652                                         while (!xtr.EOF)
653                                                 sb.Append (xtr.ReadOuterXml ());
654                                         decl.ReplacementText = sb.ToString ();
655                                 }
656                                 else
657                                         // PE
658                                         decl.ReplacementText = xtr.GetRemainder ().ReadToEnd ();
659                         }
660                         else
661                                 decl.ReplacementText = decl.LiteralEntityValue;
662                 }
663
664                 private void ResolveInternalEntityReplacementText (DTDEntityBase decl)
665                 {
666                         string value = decl.LiteralEntityValue;
667                         int len = value.Length;
668                         ClearValueBuffer ();
669                         for (int i = 0; i < len; i++) {
670                                 int ch = value [i];
671                                 int end = 0;
672                                 string name;
673                                 switch (ch) {
674                                 case '&':
675                                         i++;
676                                         end = value.IndexOf (';', i);
677                                         if (end < i + 1)
678                                                 throw new XmlException (decl, decl.BaseURI, "Invalid reference markup.");
679                                         // expand charref
680                                         if (value [i] == '#') {
681                                                 i++;
682                                                 ch = GetCharacterReference (decl, value, ref i, end);
683                                                 if (XmlChar.IsInvalid (ch))
684                                                         throw NotWFError ("Invalid character was used to define parameter entity.");
685
686                                         } else {
687                                                 name = value.Substring (i, end - i);
688                                                 // don't expand "general" entity.
689                                                 AppendValueChar ('&');
690                                                 valueBuffer.Append (name);
691                                                 AppendValueChar (';');
692                                                 i = end;
693                                                 break;
694                                         }
695                                         if (XmlChar.IsInvalid (ch))
696                                                 throw new XmlException (decl, decl.BaseURI, "Invalid character was found in the entity declaration.");
697                                         AppendValueChar (ch);
698                                         break;
699                                 case '%':
700                                         i++;
701                                         end = value.IndexOf (';', i);
702                                         if (end < i + 1)
703                                                 throw new XmlException (decl, decl.BaseURI, "Invalid reference markup.");
704                                         name = value.Substring (i, end - i);
705                                         valueBuffer.Append (GetPEValue (name));
706                                         i = end;
707                                         break;
708                                 default:
709                                         AppendValueChar (ch);
710                                         break;
711                                 }
712                         }
713                         decl.ReplacementText = CreateValueString ();
714
715                         ClearValueBuffer ();
716                 }
717
718                 private int GetCharacterReference (DTDEntityBase li, string value, ref int index, int end)
719                 {
720                         int ret = 0;
721                         if (value [index] == 'x') {
722                                 try {
723                                         ret = int.Parse (value.Substring (index + 1, end - index - 1), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
724                                 } catch (FormatException) {
725                                         throw new XmlException (li, li.BaseURI, "Invalid number for a character reference.");
726                                 }
727                         } else {
728                                 try {
729                                         ret = int.Parse (value.Substring (index, end - index), CultureInfo.InvariantCulture);
730                                 } catch (FormatException) {
731                                         throw new XmlException (li, li.BaseURI, "Invalid number for a character reference.");
732                                 }
733                         }
734                         index = end;
735                         return ret;
736                 }
737
738                 private string GetPEValue (string peName)
739                 {
740                         DTDParameterEntityDeclaration peDecl = GetPEDecl (peName);
741                         return peDecl != null ? 
742                                 peDecl.ReplacementText : String.Empty;
743                 }
744
745                 private DTDParameterEntityDeclaration GetPEDecl (string peName)
746                 {
747                         DTDParameterEntityDeclaration peDecl =
748                                 DTD.PEDecls [peName] as DTDParameterEntityDeclaration;
749                         if (peDecl != null) {
750                                 if (peDecl.IsInternalSubset)
751                                         throw NotWFError ("Parameter entity is not allowed in internal subset entity '" + peName + "'");
752                                 return peDecl;
753                         }
754                         // See XML 1.0 section 4.1 for both WFC and VC.
755                         if ((DTD.SystemId == null && !DTD.InternalSubsetHasPEReference) || DTD.IsStandalone)
756                                 throw NotWFError (String.Format ("Parameter entity '{0}' not found.",peName));
757                         HandleError (new XmlSchemaException (
758                                 "Parameter entity " + peName + " not found.", null));
759                         return null;
760                 }
761
762                 private bool TryExpandPERef ()
763                 {
764                         if (PeekChar () != '%')
765                                 return false;
766                         while (PeekChar () == '%') {
767                                 TryExpandPERefSpaceKeep ();
768                                 SkipWhitespace ();
769                         }
770                         return true;
771                 }
772
773                 // Tries to expand parameter entities, but it should not skip spaces
774                 private bool TryExpandPERefSpaceKeep ()
775                 {
776                         if (PeekChar () == '%') {
777                                 if (this.processingInternalSubset)
778                                         throw NotWFError ("Parameter entity reference is not allowed inside internal subset.");
779                                 ReadChar ();
780                                 ExpandPERef ();
781                                 return true;
782                         }
783                         else
784                                 return false;
785                 }
786
787                 // reader is positioned after '%'
788                 private void ExpandPERef ()
789                 {
790                         string peName = ReadName ();
791                         Expect (';');
792                         DTDParameterEntityDeclaration peDecl =
793                                 DTD.PEDecls [peName] as DTDParameterEntityDeclaration;
794                         if (peDecl == null) {
795                                 HandleError (new XmlSchemaException ("Parameter entity " + peName + " not found.", null));
796                                 return; // do nothing
797                         }
798                         currentInput.PushPEBuffer (peDecl);
799                 }
800
801                 // The reader is positioned on the head of the name.
802                 private DTDEntityDeclaration ReadEntityDecl ()
803                 {
804                         DTDEntityDeclaration decl = new DTDEntityDeclaration (DTD);
805                         decl.BaseURI = BaseURI;
806                         decl.XmlResolver = DTD.Resolver;
807                         decl.IsInternalSubset = this.processingInternalSubset;
808                         TryExpandPERef ();
809                         decl.Name = ReadName ();
810                         if (!SkipWhitespace ())
811                                 throw NotWFError ("Whitespace is required between name and content in DTD entity declaration.");
812                         TryExpandPERef ();
813
814                         if (PeekChar () == 'S' || PeekChar () == 'P') {
815                                 // external entity
816                                 ReadExternalID ();
817                                 decl.PublicId = cachedPublicId;
818                                 decl.SystemId = cachedSystemId;
819                                 if (SkipWhitespace ()) {
820                                         if (PeekChar () == 'N') {
821                                                 // NDataDecl
822                                                 Expect ("NDATA");
823                                                 if (!SkipWhitespace ())
824                                                         throw NotWFError ("Whitespace is required after NDATA.");
825                                                 decl.NotationName = ReadName ();        // ndata_name
826                                         }
827                                 }
828                                 if (decl.NotationName == null) {
829                                         decl.Resolve ();
830                                         ResolveExternalEntityReplacementText (decl);
831                                 } else {
832                                         // Unparsed entity.
833                                         decl.LiteralEntityValue = String.Empty;
834                                         decl.ReplacementText = String.Empty;
835                                 }
836                         }
837                         else {
838                                 // literal entity
839                                 ReadEntityValueDecl (decl);
840                                 ResolveInternalEntityReplacementText (decl);
841                         }
842                         SkipWhitespace ();
843                         // This expanding is only allowed as a non-validating parser.
844                         TryExpandPERef ();
845                         Expect ('>');
846                         return decl;
847                 }
848
849                 private void ReadEntityValueDecl (DTDEntityDeclaration decl)
850                 {
851                         SkipWhitespace ();
852                         // quotation char will be finally removed on unescaping
853                         int quoteChar = ReadChar ();
854                         if (quoteChar != '\'' && quoteChar != '"')
855                                 throw NotWFError ("quotation char was expected.");
856                         ClearValueBuffer ();
857
858                         while (PeekChar () != quoteChar) {
859                                 int ch = ReadChar ();
860                                 switch (ch) {
861                                 case '%':
862                                         string name = ReadName ();
863                                         Expect (';');
864                                         if (decl.IsInternalSubset)
865                                                 throw NotWFError (String.Format ("Parameter entity is not allowed in internal subset entity '{0}'", name));
866                                         valueBuffer.Append (GetPEValue (name));
867                                         break;
868                                 case -1:
869                                         throw NotWFError ("unexpected end of stream.");
870                                 default:
871                                         if (this.normalization && XmlChar.IsInvalid (ch))
872                                                 throw NotWFError ("Invalid character was found in the entity declaration.");
873                                         AppendValueChar (ch);
874                                         break;
875                                 }
876                         }
877 //                      string value = Dereference (CreateValueString (), false);
878                         string value = CreateValueString ();
879                         ClearValueBuffer ();
880
881                         Expect (quoteChar);
882                         decl.LiteralEntityValue = value;
883                 }
884
885                 private DTDAttListDeclaration ReadAttListDecl ()
886                 {
887                         TryExpandPERefSpaceKeep ();
888                         if (!SkipWhitespace ())
889                                 throw NotWFError ("Whitespace is required between ATTLIST and name in DTD attlist declaration.");
890                         TryExpandPERef ();
891                         string name = ReadName ();      // target element name
892                         DTDAttListDeclaration decl =
893                                 DTD.AttListDecls [name] as DTDAttListDeclaration;
894                         if (decl == null)
895                                 decl = new DTDAttListDeclaration (DTD);
896                         decl.IsInternalSubset = this.processingInternalSubset;
897                         decl.Name = name;
898
899                         if (!SkipWhitespace ())
900                                 if (PeekChar () != '>')
901                                         throw NotWFError ("Whitespace is required between name and content in non-empty DTD attlist declaration.");
902
903                         TryExpandPERef ();
904
905                         while (XmlChar.IsNameChar (PeekChar ())) {
906                                 DTDAttributeDefinition def = ReadAttributeDefinition ();
907                                 // There must not be two or more ID attributes.
908                                 if (def.Datatype.TokenizedType == XmlTokenizedType.ID) {
909                                         for (int i = 0; i < decl.Definitions.Count; i++) {
910                                                 DTDAttributeDefinition d = decl [i];
911                                                 if (d.Datatype.TokenizedType == XmlTokenizedType.ID) {
912                                                         HandleError (new XmlSchemaException ("AttList declaration must not contain two or more ID attributes.",
913                                                                 def.LineNumber, def.LinePosition, null, def.BaseURI, null));
914                                                         break;
915                                                 }
916                                         }
917                                 }
918                                 if (decl [def.Name] == null)
919                                         decl.Add (def);
920                                 SkipWhitespace ();
921                                 TryExpandPERef ();
922                         }
923                         SkipWhitespace ();
924                         // This expanding is only allowed as a non-validating parser.
925                         TryExpandPERef ();
926                         Expect ('>');
927                         return decl;
928                 }
929
930                 private DTDAttributeDefinition ReadAttributeDefinition ()
931                 {
932                         DTDAttributeDefinition def = new DTDAttributeDefinition (DTD);
933                         def.IsInternalSubset = this.processingInternalSubset;
934
935                         // attr_name
936                         TryExpandPERef ();
937                         def.Name = ReadName ();
938                         if (!SkipWhitespace ())
939                                 throw NotWFError ("Whitespace is required between name and content in DTD attribute definition.");
940
941                         // attr_value
942                         TryExpandPERef ();
943                         switch(PeekChar ()) {
944                         case 'C':       // CDATA
945                                 Expect ("CDATA");
946                                 def.Datatype = XmlSchemaDatatype.FromName ("normalizedString", XmlSchema.Namespace);
947                                 break;
948                         case 'I':       // ID, IDREF, IDREFS
949                                 Expect ("ID");
950                                 if(PeekChar () == 'R') {
951                                         Expect ("REF");
952                                         if(PeekChar () == 'S') {
953                                                 // IDREFS
954                                                 ReadChar ();
955                                                 def.Datatype = XmlSchemaDatatype.FromName ("IDREFS", XmlSchema.Namespace);
956                                         }
957                                         else    // IDREF
958                                                 def.Datatype = XmlSchemaDatatype.FromName ("IDREF", XmlSchema.Namespace);
959                                 }
960                                 else    // ID
961                                         def.Datatype = XmlSchemaDatatype.FromName ("ID", XmlSchema.Namespace);
962                                 break;
963                         case 'E':       // ENTITY, ENTITIES
964                                 Expect ("ENTIT");
965                                 switch(ReadChar ()) {
966                                         case 'Y':       // ENTITY
967                                                 def.Datatype = XmlSchemaDatatype.FromName ("ENTITY", XmlSchema.Namespace);
968                                                 break;
969                                         case 'I':       // ENTITIES
970                                                 Expect ("ES");
971                                                 def.Datatype = XmlSchemaDatatype.FromName ("ENTITIES", XmlSchema.Namespace);
972                                                 break;
973                                 }
974                                 break;
975                         case 'N':       // NMTOKEN, NMTOKENS, NOTATION
976                                 ReadChar ();
977                                 switch(PeekChar ()) {
978                                 case 'M':
979                                         Expect ("MTOKEN");
980                                         if(PeekChar ()=='S') {  // NMTOKENS
981                                                 ReadChar ();
982                                                 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKENS", XmlSchema.Namespace);
983                                         }
984                                         else    // NMTOKEN
985                                                 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKEN", XmlSchema.Namespace);
986                                         break;
987                                 case 'O':
988                                         Expect ("OTATION");
989                                         def.Datatype = XmlSchemaDatatype.FromName ("NOTATION", XmlSchema.Namespace);
990                                         if (!SkipWhitespace ())
991                                                 throw NotWFError ("Whitespace is required between name and content in DTD attribute definition.");
992                                         Expect ('(');
993                                         SkipWhitespace ();
994                                         def.EnumeratedNotations.Add (ReadName ());              // notation name
995                                         SkipWhitespace ();
996                                         while(PeekChar () == '|') {
997                                                 ReadChar ();
998                                                 SkipWhitespace ();
999                                                 def.EnumeratedNotations.Add (ReadName ());      // notation name
1000                                                 SkipWhitespace ();
1001                                         }
1002                                         Expect (')');
1003                                         break;
1004                                 default:
1005                                         throw NotWFError ("attribute declaration syntax error.");
1006                                 }
1007                                 break;
1008                         default:        // Enumerated Values
1009                                 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKEN", XmlSchema.Namespace);
1010                                 TryExpandPERef ();
1011                                 Expect ('(');
1012                                 SkipWhitespace ();
1013                                 def.EnumeratedAttributeDeclaration.Add (
1014                                         def.Datatype.Normalize (ReadNmToken ()));       // enum value
1015                                 SkipWhitespace ();
1016                                 while(PeekChar () == '|') {
1017                                         ReadChar ();
1018                                         SkipWhitespace ();
1019                                         def.EnumeratedAttributeDeclaration.Add (
1020                                                 def.Datatype.Normalize (ReadNmToken ()));       // enum value
1021                                         SkipWhitespace ();
1022                                 }
1023                                 Expect (')');
1024                                 break;
1025                         }
1026                         TryExpandPERefSpaceKeep ();
1027                         if (!SkipWhitespace ())
1028                                 throw NotWFError ("Whitespace is required between type and occurence in DTD attribute definition.");
1029
1030                         // def_value
1031                         ReadAttributeDefaultValue (def);
1032
1033                         return def;
1034                 }
1035
1036                 private void ReadAttributeDefaultValue (DTDAttributeDefinition def)
1037                 {
1038                         if(PeekChar () == '#')
1039                         {
1040                                 ReadChar ();
1041                                 switch(PeekChar ())
1042                                 {
1043                                 case 'R':
1044                                         Expect ("REQUIRED");
1045                                         def.OccurenceType = DTDAttributeOccurenceType.Required;
1046                                         break;
1047                                 case 'I':
1048                                         Expect ("IMPLIED");
1049                                         def.OccurenceType = DTDAttributeOccurenceType.Optional;
1050                                         break;
1051                                 case 'F':
1052                                         Expect ("FIXED");
1053                                         def.OccurenceType = DTDAttributeOccurenceType.Fixed;
1054                                         if (!SkipWhitespace ())
1055                                                 throw NotWFError ("Whitespace is required between FIXED and actual value in DTD attribute definition.");
1056                                         def.UnresolvedDefaultValue = ReadDefaultAttribute ();
1057                                         break;
1058                                 }
1059                         } else {
1060                                 // one of the enumerated value
1061                                 SkipWhitespace ();
1062                                 TryExpandPERef ();
1063                                 def.UnresolvedDefaultValue = ReadDefaultAttribute ();
1064                         }
1065
1066                         // VC: If default value exists, it should be valid.
1067                         if (def.DefaultValue != null) {
1068                                 string normalized = def.Datatype.Normalize (def.DefaultValue);
1069                                 bool breakup = false;
1070                                 object parsed = null;
1071
1072                                 // enumeration validity
1073                                 if (def.EnumeratedAttributeDeclaration.Count > 0) {
1074                                         if (!def.EnumeratedAttributeDeclaration.Contains (normalized)) {
1075                                                 HandleError (new XmlSchemaException ("Default value is not one of the enumerated values.",
1076                                                         def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1077                                                 breakup = true;
1078                                         }
1079                                 }
1080                                 if (def.EnumeratedNotations.Count > 0) {
1081                                         if (!def.EnumeratedNotations.Contains (normalized)) {
1082                                                 HandleError (new XmlSchemaException ("Default value is not one of the enumerated notation values.",
1083                                                         def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1084                                                 breakup = true;
1085                                         }
1086                                 }
1087
1088                                 // type based validity
1089                                 if (!breakup) {
1090                                         try {
1091                                                 parsed = def.Datatype.ParseValue (normalized, DTD.NameTable, null);
1092                                         } catch (Exception ex) { // FIXME: (wishlist) bad catch ;-(
1093                                                 HandleError (new XmlSchemaException ("Invalid default value for ENTITY type.",
1094                                                         def.LineNumber, def.LinePosition, null, def.BaseURI, ex));
1095                                                 breakup = true;
1096                                         }
1097                                 }
1098                                 if (!breakup) {
1099                                         switch (def.Datatype.TokenizedType) {
1100                                         case XmlTokenizedType.ENTITY:
1101                                                 if (DTD.EntityDecls [normalized] == null)
1102                                                         HandleError (new XmlSchemaException ("Specified entity declaration used by default attribute value was not found.",
1103                                                                 def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1104                                                 break;
1105                                         case XmlTokenizedType.ENTITIES:
1106                                                 string [] entities = parsed as string [];
1107                                                 for (int i = 0; i < entities.Length; i++) {
1108                                                         string entity = entities [i];
1109                                                         if (DTD.EntityDecls [entity] == null)
1110                                                                 HandleError (new XmlSchemaException ("Specified entity declaration used by default attribute value was not found.",
1111                                                                         def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1112                                                 }
1113                                                 break;
1114                                         }
1115                                 }
1116                         }
1117                         // Extra ID attribute validity check.
1118                         if (def.Datatype != null && def.Datatype.TokenizedType == XmlTokenizedType.ID)
1119                                 if (def.UnresolvedDefaultValue != null)
1120                                         HandleError (new XmlSchemaException ("ID attribute must not have fixed value constraint.",
1121                                                 def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1122
1123                 }
1124
1125                 private DTDNotationDeclaration ReadNotationDecl()
1126                 {
1127                         DTDNotationDeclaration decl = new DTDNotationDeclaration (DTD);
1128                         if (!SkipWhitespace ())
1129                                 throw NotWFError ("Whitespace is required between NOTATION and name in DTD notation declaration.");
1130                         TryExpandPERef ();
1131                         decl.Name = ReadName ();        // notation name
1132                         /*
1133                         if (namespaces) {       // copy from SetProperties ;-)
1134                                 int indexOfColon = decl.Name.IndexOf (':');
1135
1136                                 if (indexOfColon == -1) {
1137                                         decl.Prefix = String.Empty;
1138                                         decl.LocalName = decl.Name;
1139                                 } else {
1140                                         decl.Prefix = decl.Name.Substring (0, indexOfColon);
1141                                         decl.LocalName = decl.Name.Substring (indexOfColon + 1);
1142                                 }
1143                         } else {
1144                         */
1145                                 decl.Prefix = String.Empty;
1146                                 decl.LocalName = decl.Name;
1147 //                      }
1148
1149                         SkipWhitespace ();
1150                         if(PeekChar () == 'P') {
1151                                 decl.PublicId = ReadPubidLiteral ();
1152                                 bool wsSkipped = SkipWhitespace ();
1153                                 if (PeekChar () == '\'' || PeekChar () == '"') {
1154                                         if (!wsSkipped)
1155                                                 throw NotWFError ("Whitespace is required between public id and system id.");
1156                                         decl.SystemId = ReadSystemLiteral (false);
1157                                         SkipWhitespace ();
1158                                 }
1159                         } else if(PeekChar () == 'S') {
1160                                 decl.SystemId = ReadSystemLiteral (true);
1161                                 SkipWhitespace ();
1162                         }
1163                         if(decl.PublicId == null && decl.SystemId == null)
1164                                 throw NotWFError ("public or system declaration required for \"NOTATION\" declaration.");
1165                         // This expanding is only allowed as a non-validating parser.
1166                         TryExpandPERef ();
1167                         Expect ('>');
1168                         return decl;
1169                 }
1170
1171                 private void ReadExternalID () {
1172                         switch (PeekChar ()) {
1173                         case 'S':
1174                                 cachedSystemId = ReadSystemLiteral (true);
1175                                 break;
1176                         case 'P':
1177                                 cachedPublicId = ReadPubidLiteral ();
1178                                 if (!SkipWhitespace ())
1179                                         throw NotWFError ("Whitespace is required between PUBLIC id and SYSTEM id.");
1180                                 cachedSystemId = ReadSystemLiteral (false);
1181                                 break;
1182                         }
1183                 }
1184
1185                 // The reader is positioned on the first 'S' of "SYSTEM".
1186                 private string ReadSystemLiteral (bool expectSYSTEM)
1187                 {
1188                         if(expectSYSTEM) {
1189                                 Expect ("SYSTEM");
1190                                 if (!SkipWhitespace ())
1191                                         throw NotWFError ("Whitespace is required after 'SYSTEM'.");
1192                         }
1193                         else
1194                                 SkipWhitespace ();
1195                         int quoteChar = ReadChar ();    // apos or quot
1196                         int c = 0;
1197                         ClearValueBuffer ();
1198                         while (c != quoteChar) {
1199                                 c = ReadChar ();
1200                                 if (c < 0)
1201                                         throw NotWFError ("Unexpected end of stream in ExternalID.");
1202                                 if (c != quoteChar)
1203                                         AppendValueChar (c);
1204                         }
1205                         return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
1206                 }
1207
1208                 private string ReadPubidLiteral()
1209                 {
1210                         Expect ("PUBLIC");
1211                         if (!SkipWhitespace ())
1212                                 throw NotWFError ("Whitespace is required after 'PUBLIC'.");
1213                         int quoteChar = ReadChar ();
1214                         int c = 0;
1215                         ClearValueBuffer ();
1216                         while(c != quoteChar)
1217                         {
1218                                 c = ReadChar ();
1219                                 if(c < 0) throw NotWFError ("Unexpected end of stream in ExternalID.");
1220                                 if(c != quoteChar && !XmlChar.IsPubidChar (c))
1221                                         throw NotWFError (String.Format ("character '{0}' not allowed for PUBLIC ID", (char) c));
1222                                 if (c != quoteChar)
1223                                         AppendValueChar (c);
1224                         }
1225                         return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
1226                 }
1227
1228                 // The reader is positioned on the first character
1229                 // of the name.
1230                 internal string ReadName ()
1231                 {
1232                         return ReadNameOrNmToken(false);
1233                 }
1234
1235                 // The reader is positioned on the first character
1236                 // of the name.
1237                 private string ReadNmToken ()
1238                 {
1239                         return ReadNameOrNmToken(true);
1240                 }
1241
1242                 private string ReadNameOrNmToken(bool isNameToken)
1243                 {
1244                         int ch = PeekChar ();
1245                         if(isNameToken) {
1246                                 if (!XmlChar.IsNameChar (ch))
1247                                         throw NotWFError (String.Format ("a nmtoken did not start with a legal character {0} ({1})", ch, (char) ch));
1248                         }
1249                         else {
1250                                 if (!XmlChar.IsFirstNameChar (ch))
1251                                         throw NotWFError (String.Format ("a name did not start with a legal character {0} ({1})", ch, (char) ch));
1252                         }
1253
1254                         nameLength = 0;
1255
1256                         AppendNameChar (ReadChar ());
1257
1258                         while (XmlChar.IsNameChar (PeekChar ())) {
1259                                 AppendNameChar (ReadChar ());
1260                         }
1261
1262                         return CreateNameString ();
1263                 }
1264
1265                 // Read the next character and compare it against the
1266                 // specified character.
1267                 private void Expect (int expected)
1268                 {
1269                         int ch = ReadChar ();
1270
1271                         if (ch != expected) {
1272                                 throw NotWFError (String.Format (CultureInfo.InvariantCulture, 
1273                                                 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
1274                                                 (char) expected,
1275                                                 expected,
1276                                                 (char) ch,
1277                                                 ch));
1278                         }
1279                 }
1280
1281                 private void Expect (string expected)
1282                 {
1283                         int len = expected.Length;
1284                         for (int i=0; i< len; i++)
1285                                 Expect (expected [i]);
1286                 }
1287
1288                 private void ExpectAfterWhitespace (char c)
1289                 {
1290                         while (true) {
1291                                 int i = ReadChar ();
1292                                 if (XmlChar.IsWhitespace (i))
1293                                         continue;
1294                                 if (c != i)
1295                                         throw NotWFError (String.Format (CultureInfo.InvariantCulture, "Expected {0} but found {1} [{2}].", c, (char) i, i));
1296                                 break;
1297                         }
1298                 }
1299
1300                 // Does not consume the first non-whitespace character.
1301                 private bool SkipWhitespace ()
1302                 {
1303                         bool skipped = XmlChar.IsWhitespace (PeekChar ());
1304                         while (XmlChar.IsWhitespace (PeekChar ()))
1305                                 ReadChar ();
1306                         return skipped;
1307                 }
1308
1309                 private int PeekChar ()
1310                 {
1311                         return currentInput.PeekChar ();
1312                 }
1313
1314                 private int ReadChar ()
1315                 {
1316                         return currentInput.ReadChar ();
1317                 }
1318
1319                 // The reader is positioned on the first character after
1320                 // the leading '<!--'.
1321                 private void ReadComment ()
1322                 {
1323                         currentInput.AllowTextDecl = false;
1324
1325                         while (PeekChar () != -1) {
1326                                 int ch = ReadChar ();
1327
1328                                 if (ch == '-' && PeekChar () == '-') {
1329                                         ReadChar ();
1330
1331                                         if (PeekChar () != '>')
1332                                                 throw NotWFError ("comments cannot contain '--'");
1333
1334                                         ReadChar ();
1335                                         break;
1336                                 }
1337
1338                                 if (XmlChar.IsInvalid (ch))
1339                                         throw NotWFError ("Not allowed character was found.");
1340                         }
1341                 }
1342
1343                 // The reader is positioned on the first character
1344                 // of the target.
1345                 //
1346                 // It may be xml declaration or processing instruction.
1347                 private void ReadProcessingInstruction ()
1348                 {
1349                         string target = ReadName ();
1350                         if (target == "xml") {
1351                                 ReadTextDeclaration ();
1352                                 return;
1353                         } else if (String.Compare (target, "xml", true, CultureInfo.InvariantCulture) == 0)
1354                                 throw NotWFError ("Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
1355
1356                         currentInput.AllowTextDecl = false;
1357
1358                         if (!SkipWhitespace ())
1359                                 if (PeekChar () != '?')
1360                                         throw NotWFError ("Invalid processing instruction name was found.");
1361
1362                         while (PeekChar () != -1) {
1363                                 int ch = ReadChar ();
1364
1365                                 if (ch == '?' && PeekChar () == '>') {
1366                                         ReadChar ();
1367                                         break;
1368                                 }
1369                         }
1370                 }
1371
1372                 // The reader is positioned after "<?xml "
1373                 private void ReadTextDeclaration ()
1374                 {
1375                         if (!currentInput.AllowTextDecl)
1376                                 throw NotWFError ("Text declaration cannot appear in this state.");
1377
1378                         currentInput.AllowTextDecl = false;
1379
1380                         SkipWhitespace ();
1381
1382                         // version decl
1383                         if (PeekChar () == 'v') {
1384                                 Expect ("version");
1385                                 ExpectAfterWhitespace ('=');
1386                                 SkipWhitespace ();
1387                                 int quoteChar = ReadChar ();
1388                                 char [] expect1_0 = new char [3];
1389                                 int versionLength = 0;
1390                                 switch (quoteChar) {
1391                                 case '\'':
1392                                 case '"':
1393                                         while (PeekChar () != quoteChar) {
1394                                                 if (PeekChar () == -1)
1395                                                         throw NotWFError ("Invalid version declaration inside text declaration.");
1396                                                 else if (versionLength == 3)
1397                                                         throw NotWFError ("Invalid version number inside text declaration.");
1398                                                 else {
1399                                                         expect1_0 [versionLength] = (char) ReadChar ();
1400                                                         versionLength++;
1401                                                         if (versionLength == 3 && new String (expect1_0) != "1.0")
1402                                                                 throw NotWFError ("Invalid version number inside text declaration.");
1403                                                 }
1404                                         }
1405                                         ReadChar ();
1406                                         SkipWhitespace ();
1407                                         break;
1408                                 default:
1409                                         throw NotWFError ("Invalid version declaration inside text declaration.");
1410                                 }
1411                         }
1412
1413                         if (PeekChar () == 'e') {
1414                                 Expect ("encoding");
1415                                 ExpectAfterWhitespace ('=');
1416                                 SkipWhitespace ();
1417                                 int quoteChar = ReadChar ();
1418                                 switch (quoteChar) {
1419                                 case '\'':
1420                                 case '"':
1421                                         while (PeekChar () != quoteChar)
1422                                                 if (ReadChar () == -1)
1423                                                         throw NotWFError ("Invalid encoding declaration inside text declaration.");
1424                                         ReadChar ();
1425                                         SkipWhitespace ();
1426                                         break;
1427                                 default:
1428                                         throw NotWFError ("Invalid encoding declaration inside text declaration.");
1429                                 }
1430                                 // Encoding value should be checked inside XmlInputStream.
1431                         }
1432                         else
1433                                 throw NotWFError ("Encoding declaration is mandatory in text declaration.");
1434
1435                         Expect ("?>");
1436                 }
1437
1438                 // Note that now this method behaves differently from
1439                 // XmlTextReader's one. It calles AppendValueChar() internally.
1440                 private int ReadCharacterReference ()
1441                 {
1442                         int value = 0;
1443
1444                         if (PeekChar () == 'x') {
1445                                 ReadChar ();
1446
1447                                 while (PeekChar () != ';' && PeekChar () != -1) {
1448                                         int ch = ReadChar ();
1449
1450                                         if (ch >= '0' && ch <= '9')
1451                                                 value = (value << 4) + ch - '0';
1452                                         else if (ch >= 'A' && ch <= 'F')
1453                                                 value = (value << 4) + ch - 'A' + 10;
1454                                         else if (ch >= 'a' && ch <= 'f')
1455                                                 value = (value << 4) + ch - 'a' + 10;
1456                                         else
1457                                                 throw NotWFError (String.Format (
1458                                                                 CultureInfo.InvariantCulture,
1459                                                                 "invalid hexadecimal digit: {0} (#x{1:X})",
1460                                                                 (char) ch,
1461                                                                 ch));
1462                                 }
1463                         } else {
1464                                 while (PeekChar () != ';' && PeekChar () != -1) {
1465                                         int ch = ReadChar ();
1466
1467                                         if (ch >= '0' && ch <= '9')
1468                                                 value = value * 10 + ch - '0';
1469                                         else
1470                                                 throw NotWFError (String.Format (
1471                                                                 CultureInfo.InvariantCulture,
1472                                                                 "invalid decimal digit: {0} (#x{1:X})",
1473                                                                 (char) ch,
1474                                                                 ch));
1475                                 }
1476                         }
1477
1478                         ReadChar (); // ';'
1479
1480                         // There is no way to save surrogate pairs...
1481                         if (XmlChar.IsInvalid (value))
1482                                 throw NotWFError ("Referenced character was not allowed in XML.");
1483                         AppendValueChar (value);
1484                         return value;
1485                 }
1486
1487                 private void AppendNameChar (int ch)
1488                 {
1489                         CheckNameCapacity ();
1490                         if (ch < Char.MaxValue)
1491                                 nameBuffer [nameLength++] = (char) ch;
1492                         else {
1493                                 nameBuffer [nameLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1494                                 CheckNameCapacity ();
1495                                 nameBuffer [nameLength++] = (char) (ch % 0x10000 + 0xDC00);
1496                         }
1497                 }
1498
1499                 private void CheckNameCapacity ()
1500                 {
1501                         if (nameLength == nameCapacity) {
1502                                 nameCapacity = nameCapacity * 2;
1503                                 char [] oldNameBuffer = nameBuffer;
1504                                 nameBuffer = new char [nameCapacity];
1505                                 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1506                         }
1507                 }
1508
1509                 private string CreateNameString ()
1510                 {
1511                         return DTD.NameTable.Add (nameBuffer, 0, nameLength);
1512                 }
1513
1514                 private void AppendValueChar (int ch)
1515                 {
1516                         //See http://www.faqs.org/rfcs/rfc2781.html for used algorithm
1517                         if (ch < 0x10000) {
1518                                 valueBuffer.Append ((char) ch);
1519                                 return;
1520                         }
1521                         if (ch > 0x10FFFF)
1522                                 throw new XmlException ("The numeric entity value is too large", null, LineNumber, LinePosition);
1523                         else
1524                         {
1525                                 int utag = ch - 0x10000;
1526                                 valueBuffer.Append((char) ((utag >> 10) + 0xD800));
1527                                 valueBuffer.Append((char) ((utag & 0x3FF) + 0xDC00));
1528                         }
1529                 }
1530
1531                 private string CreateValueString ()
1532                 {
1533                         return valueBuffer.ToString ();
1534                 }
1535                 
1536                 private void ClearValueBuffer ()
1537                 {
1538                         valueBuffer.Length = 0;
1539                 }
1540
1541                 // The reader is positioned on the quote character.
1542                 // *Keeps quote char* to value to get_QuoteChar() correctly.
1543                 private string ReadDefaultAttribute ()
1544                 {
1545                         ClearValueBuffer ();
1546
1547                         TryExpandPERef ();
1548
1549                         int quoteChar = ReadChar ();
1550
1551                         if (quoteChar != '\'' && quoteChar != '\"')
1552                                 throw NotWFError ("an attribute value was not quoted");
1553
1554                         AppendValueChar (quoteChar);
1555
1556                         while (PeekChar () != quoteChar) {
1557                                 int ch = ReadChar ();
1558
1559                                 switch (ch)
1560                                 {
1561                                 case '<':
1562                                         throw NotWFError ("attribute values cannot contain '<'");
1563                                 case -1:
1564                                         throw NotWFError ("unexpected end of file in an attribute value");
1565                                 case '&':
1566                                         AppendValueChar (ch);
1567                                         if (PeekChar () == '#')
1568                                                 break;
1569                                         // Check XML 1.0 section 3.1 WFC.
1570                                         string entName = ReadName ();
1571                                         Expect (';');
1572                                         if (XmlChar.GetPredefinedEntity (entName) < 0) {
1573                                                 DTDEntityDeclaration entDecl = 
1574                                                         DTD == null ? null : DTD.EntityDecls [entName];
1575                                                 if (entDecl == null || entDecl.SystemId != null)
1576                                                         // WFC: Entity Declared (see 4.1)
1577                                                         if (DTD.IsStandalone || (DTD.SystemId == null && !DTD.InternalSubsetHasPEReference))
1578                                                                 throw NotWFError ("Reference to external entities is not allowed in attribute value.");
1579                                         }
1580                                         valueBuffer.Append (entName);
1581                                         AppendValueChar (';');
1582                                         break;
1583                                 default:
1584                                         AppendValueChar (ch);
1585                                         break;
1586                                 }
1587                         }
1588
1589                         ReadChar (); // quoteChar
1590                         AppendValueChar (quoteChar);
1591
1592                         return CreateValueString ();
1593                 }
1594
1595                 private void PushParserInput (string url)
1596                 {
1597                         Uri baseUri = null;
1598                         try {
1599                                 if (DTD.BaseURI != null && DTD.BaseURI.Length > 0)
1600                                         baseUri = new Uri (DTD.BaseURI);
1601                         } catch (UriFormatException) {
1602                         }
1603
1604                         Uri absUri = url != null && url.Length > 0 ?
1605                                 DTD.Resolver.ResolveUri (baseUri, url) : baseUri;
1606                         string absPath = absUri != null ? absUri.ToString () : String.Empty;
1607
1608                         foreach (XmlParserInput i in parserInputStack.ToArray ()) {
1609                                 if (i.BaseURI == absPath)
1610                                         throw NotWFError ("Nested inclusion is not allowed: " + url);
1611                         }
1612                         parserInputStack.Push (currentInput);
1613                         Stream s = null;
1614                         try {
1615                                 s = DTD.Resolver.GetEntity (absUri, null, typeof (Stream)) as Stream;
1616                                 currentInput = new XmlParserInput (new XmlStreamReader (s), absPath);
1617                         } catch (Exception ex) { // FIXME: (wishlist) Bad exception catch ;-(
1618                                 if (s != null)
1619                                         s.Close ();
1620                                 int line = currentInput == null ? 0 : currentInput.LineNumber;
1621                                 int col = currentInput == null ? 0 : currentInput.LinePosition;
1622                                 string bu = (currentInput == null) ? String.Empty : currentInput.BaseURI;
1623                                 HandleError (new XmlSchemaException ("Specified external entity not found. Target URL is " + url + " .",
1624                                         line, col, null, bu, ex));
1625                                 currentInput = new XmlParserInput (new StringReader (String.Empty), absPath);
1626                         }
1627                 }
1628
1629                 private void PopParserInput ()
1630                 {
1631                         currentInput.Close ();
1632                         currentInput = parserInputStack.Pop () as XmlParserInput;
1633                 }
1634
1635                 private void HandleError (XmlSchemaException ex)
1636                 {
1637 #if DTD_HANDLE_EVENTS
1638                         if (this.ValidationEventHandler != null)
1639                                 ValidationEventHandler (this, new ValidationEventArgs (ex, ex.Message, XmlSeverityType.Error));
1640 #else
1641                         DTD.AddError (ex);
1642 #endif
1643                 }
1644         }
1645 }