ensure the file is closed in XmlWriter.Create(filename)
[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                                 xtr.SkipTextDeclaration ();
648                                 if (decl is DTDEntityDeclaration && DTD.EntityDecls [decl.Name] == null) {
649                                         // GE - also checked as valid contents
650                                         StringBuilder sb = new StringBuilder ();
651                                         xtr.Normalization = this.Normalization;
652                                         xtr.Read ();
653                                         while (!xtr.EOF)
654                                                 sb.Append (xtr.ReadOuterXml ());
655                                         decl.ReplacementText = sb.ToString ();
656                                 }
657                                 else
658                                         // PE
659                                         decl.ReplacementText = xtr.GetRemainder ().ReadToEnd ();
660                         }
661                         else
662                                 decl.ReplacementText = decl.LiteralEntityValue;
663                 }
664
665                 private void ResolveInternalEntityReplacementText (DTDEntityBase decl)
666                 {
667                         string value = decl.LiteralEntityValue;
668                         int len = value.Length;
669                         ClearValueBuffer ();
670                         for (int i = 0; i < len; i++) {
671                                 int ch = value [i];
672                                 int end = 0;
673                                 string name;
674                                 switch (ch) {
675                                 case '&':
676                                         i++;
677                                         end = value.IndexOf (';', i);
678                                         if (end < i + 1)
679                                                 throw new XmlException (decl, decl.BaseURI, "Invalid reference markup.");
680                                         // expand charref
681                                         if (value [i] == '#') {
682                                                 i++;
683                                                 ch = GetCharacterReference (decl, value, ref i, end);
684                                                 if (XmlChar.IsInvalid (ch))
685                                                         throw NotWFError ("Invalid character was used to define parameter entity.");
686
687                                         } else {
688                                                 name = value.Substring (i, end - i);
689                                                 if (!XmlChar.IsName (name))
690                                                         throw NotWFError (String.Format ("'{0}' is not a valid entity reference name.", name));
691                                                 // don't expand "general" entity.
692                                                 AppendValueChar ('&');
693                                                 valueBuffer.Append (name);
694                                                 AppendValueChar (';');
695                                                 i = end;
696                                                 break;
697                                         }
698                                         if (XmlChar.IsInvalid (ch))
699                                                 throw new XmlException (decl, decl.BaseURI, "Invalid character was found in the entity declaration.");
700                                         AppendValueChar (ch);
701                                         break;
702                                 case '%':
703                                         i++;
704                                         end = value.IndexOf (';', i);
705                                         if (end < i + 1)
706                                                 throw new XmlException (decl, decl.BaseURI, "Invalid reference markup.");
707                                         name = value.Substring (i, end - i);
708                                         valueBuffer.Append (GetPEValue (name));
709                                         i = end;
710                                         break;
711                                 default:
712                                         AppendValueChar (ch);
713                                         break;
714                                 }
715                         }
716                         decl.ReplacementText = CreateValueString ();
717
718                         ClearValueBuffer ();
719                 }
720
721                 private int GetCharacterReference (DTDEntityBase li, string value, ref int index, int end)
722                 {
723                         int ret = 0;
724                         if (value [index] == 'x') {
725                                 try {
726                                         ret = int.Parse (value.Substring (index + 1, end - index - 1), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
727                                 } catch (FormatException) {
728                                         throw new XmlException (li, li.BaseURI, "Invalid number for a character reference.");
729                                 }
730                         } else {
731                                 try {
732                                         ret = int.Parse (value.Substring (index, end - index), CultureInfo.InvariantCulture);
733                                 } catch (FormatException) {
734                                         throw new XmlException (li, li.BaseURI, "Invalid number for a character reference.");
735                                 }
736                         }
737                         index = end;
738                         return ret;
739                 }
740
741                 private string GetPEValue (string peName)
742                 {
743                         DTDParameterEntityDeclaration peDecl = GetPEDecl (peName);
744                         return peDecl != null ? 
745                                 peDecl.ReplacementText : String.Empty;
746                 }
747
748                 private DTDParameterEntityDeclaration GetPEDecl (string peName)
749                 {
750                         DTDParameterEntityDeclaration peDecl =
751                                 DTD.PEDecls [peName] as DTDParameterEntityDeclaration;
752                         if (peDecl != null) {
753                                 if (peDecl.IsInternalSubset)
754                                         throw NotWFError ("Parameter entity is not allowed in internal subset entity '" + peName + "'");
755                                 return peDecl;
756                         }
757                         // See XML 1.0 section 4.1 for both WFC and VC.
758                         if ((DTD.SystemId == null && !DTD.InternalSubsetHasPEReference) || DTD.IsStandalone)
759                                 throw NotWFError (String.Format ("Parameter entity '{0}' not found.",peName));
760                         HandleError (new XmlSchemaException (
761                                 "Parameter entity " + peName + " not found.", null));
762                         return null;
763                 }
764
765                 private bool TryExpandPERef ()
766                 {
767                         if (PeekChar () != '%')
768                                 return false;
769                         while (PeekChar () == '%') {
770                                 TryExpandPERefSpaceKeep ();
771                                 SkipWhitespace ();
772                         }
773                         return true;
774                 }
775
776                 // Tries to expand parameter entities, but it should not skip spaces
777                 private bool TryExpandPERefSpaceKeep ()
778                 {
779                         if (PeekChar () == '%') {
780                                 if (this.processingInternalSubset)
781                                         throw NotWFError ("Parameter entity reference is not allowed inside internal subset.");
782                                 ReadChar ();
783                                 ExpandPERef ();
784                                 return true;
785                         }
786                         else
787                                 return false;
788                 }
789
790                 // reader is positioned after '%'
791                 private void ExpandPERef ()
792                 {
793                         string peName = ReadName ();
794                         Expect (';');
795                         DTDParameterEntityDeclaration peDecl =
796                                 DTD.PEDecls [peName] as DTDParameterEntityDeclaration;
797                         if (peDecl == null) {
798                                 HandleError (new XmlSchemaException ("Parameter entity " + peName + " not found.", null));
799                                 return; // do nothing
800                         }
801                         currentInput.PushPEBuffer (peDecl);
802                 }
803
804                 // The reader is positioned on the head of the name.
805                 private DTDEntityDeclaration ReadEntityDecl ()
806                 {
807                         DTDEntityDeclaration decl = new DTDEntityDeclaration (DTD);
808                         decl.BaseURI = BaseURI;
809                         decl.XmlResolver = DTD.Resolver;
810                         decl.IsInternalSubset = this.processingInternalSubset;
811                         TryExpandPERef ();
812                         decl.Name = ReadName ();
813                         if (!SkipWhitespace ())
814                                 throw NotWFError ("Whitespace is required between name and content in DTD entity declaration.");
815                         TryExpandPERef ();
816
817                         if (PeekChar () == 'S' || PeekChar () == 'P') {
818                                 // external entity
819                                 ReadExternalID ();
820                                 decl.PublicId = cachedPublicId;
821                                 decl.SystemId = cachedSystemId;
822                                 if (SkipWhitespace ()) {
823                                         if (PeekChar () == 'N') {
824                                                 // NDataDecl
825                                                 Expect ("NDATA");
826                                                 if (!SkipWhitespace ())
827                                                         throw NotWFError ("Whitespace is required after NDATA.");
828                                                 decl.NotationName = ReadName ();        // ndata_name
829                                         }
830                                 }
831                                 if (decl.NotationName == null) {
832                                         decl.Resolve ();
833                                         ResolveExternalEntityReplacementText (decl);
834                                 } else {
835                                         // Unparsed entity.
836                                         decl.LiteralEntityValue = String.Empty;
837                                         decl.ReplacementText = String.Empty;
838                                 }
839                         }
840                         else {
841                                 // literal entity
842                                 ReadEntityValueDecl (decl);
843                                 ResolveInternalEntityReplacementText (decl);
844                         }
845                         SkipWhitespace ();
846                         // This expanding is only allowed as a non-validating parser.
847                         TryExpandPERef ();
848                         Expect ('>');
849                         return decl;
850                 }
851
852                 private void ReadEntityValueDecl (DTDEntityDeclaration decl)
853                 {
854                         SkipWhitespace ();
855                         // quotation char will be finally removed on unescaping
856                         int quoteChar = ReadChar ();
857                         if (quoteChar != '\'' && quoteChar != '"')
858                                 throw NotWFError ("quotation char was expected.");
859                         ClearValueBuffer ();
860
861                         while (PeekChar () != quoteChar) {
862                                 int ch = ReadChar ();
863                                 switch (ch) {
864                                 case '%':
865                                         string name = ReadName ();
866                                         Expect (';');
867                                         if (decl.IsInternalSubset)
868                                                 throw NotWFError (String.Format ("Parameter entity is not allowed in internal subset entity '{0}'", name));
869                                         valueBuffer.Append (GetPEValue (name));
870                                         break;
871                                 case -1:
872                                         throw NotWFError ("unexpected end of stream.");
873                                 default:
874                                         if (this.normalization && XmlChar.IsInvalid (ch))
875                                                 throw NotWFError ("Invalid character was found in the entity declaration.");
876                                         AppendValueChar (ch);
877                                         break;
878                                 }
879                         }
880 //                      string value = Dereference (CreateValueString (), false);
881                         string value = CreateValueString ();
882                         ClearValueBuffer ();
883
884                         Expect (quoteChar);
885                         decl.LiteralEntityValue = value;
886                 }
887
888                 private DTDAttListDeclaration ReadAttListDecl ()
889                 {
890                         TryExpandPERefSpaceKeep ();
891                         if (!SkipWhitespace ())
892                                 throw NotWFError ("Whitespace is required between ATTLIST and name in DTD attlist declaration.");
893                         TryExpandPERef ();
894                         string name = ReadName ();      // target element name
895                         DTDAttListDeclaration decl =
896                                 DTD.AttListDecls [name] as DTDAttListDeclaration;
897                         if (decl == null)
898                                 decl = new DTDAttListDeclaration (DTD);
899                         decl.IsInternalSubset = this.processingInternalSubset;
900                         decl.Name = name;
901
902                         if (!SkipWhitespace ())
903                                 if (PeekChar () != '>')
904                                         throw NotWFError ("Whitespace is required between name and content in non-empty DTD attlist declaration.");
905
906                         TryExpandPERef ();
907
908                         while (XmlChar.IsNameChar (PeekChar ())) {
909                                 DTDAttributeDefinition def = ReadAttributeDefinition ();
910                                 // There must not be two or more ID attributes.
911                                 if (def.Datatype.TokenizedType == XmlTokenizedType.ID) {
912                                         for (int i = 0; i < decl.Definitions.Count; i++) {
913                                                 DTDAttributeDefinition d = decl [i];
914                                                 if (d.Datatype.TokenizedType == XmlTokenizedType.ID) {
915                                                         HandleError (new XmlSchemaException ("AttList declaration must not contain two or more ID attributes.",
916                                                                 def.LineNumber, def.LinePosition, null, def.BaseURI, null));
917                                                         break;
918                                                 }
919                                         }
920                                 }
921                                 if (decl [def.Name] == null)
922                                         decl.Add (def);
923                                 SkipWhitespace ();
924                                 TryExpandPERef ();
925                         }
926                         SkipWhitespace ();
927                         // This expanding is only allowed as a non-validating parser.
928                         TryExpandPERef ();
929                         Expect ('>');
930                         return decl;
931                 }
932
933                 private DTDAttributeDefinition ReadAttributeDefinition ()
934                 {
935                         DTDAttributeDefinition def = new DTDAttributeDefinition (DTD);
936                         def.IsInternalSubset = this.processingInternalSubset;
937
938                         // attr_name
939                         TryExpandPERef ();
940                         def.Name = ReadName ();
941                         if (!SkipWhitespace ())
942                                 throw NotWFError ("Whitespace is required between name and content in DTD attribute definition.");
943
944                         // attr_value
945                         TryExpandPERef ();
946                         switch(PeekChar ()) {
947                         case 'C':       // CDATA
948                                 Expect ("CDATA");
949                                 def.Datatype = XmlSchemaDatatype.FromName ("normalizedString", XmlSchema.Namespace);
950                                 break;
951                         case 'I':       // ID, IDREF, IDREFS
952                                 Expect ("ID");
953                                 if(PeekChar () == 'R') {
954                                         Expect ("REF");
955                                         if(PeekChar () == 'S') {
956                                                 // IDREFS
957                                                 ReadChar ();
958                                                 def.Datatype = XmlSchemaDatatype.FromName ("IDREFS", XmlSchema.Namespace);
959                                         }
960                                         else    // IDREF
961                                                 def.Datatype = XmlSchemaDatatype.FromName ("IDREF", XmlSchema.Namespace);
962                                 }
963                                 else    // ID
964                                         def.Datatype = XmlSchemaDatatype.FromName ("ID", XmlSchema.Namespace);
965                                 break;
966                         case 'E':       // ENTITY, ENTITIES
967                                 Expect ("ENTIT");
968                                 switch(ReadChar ()) {
969                                         case 'Y':       // ENTITY
970                                                 def.Datatype = XmlSchemaDatatype.FromName ("ENTITY", XmlSchema.Namespace);
971                                                 break;
972                                         case 'I':       // ENTITIES
973                                                 Expect ("ES");
974                                                 def.Datatype = XmlSchemaDatatype.FromName ("ENTITIES", XmlSchema.Namespace);
975                                                 break;
976                                 }
977                                 break;
978                         case 'N':       // NMTOKEN, NMTOKENS, NOTATION
979                                 ReadChar ();
980                                 switch(PeekChar ()) {
981                                 case 'M':
982                                         Expect ("MTOKEN");
983                                         if(PeekChar ()=='S') {  // NMTOKENS
984                                                 ReadChar ();
985                                                 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKENS", XmlSchema.Namespace);
986                                         }
987                                         else    // NMTOKEN
988                                                 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKEN", XmlSchema.Namespace);
989                                         break;
990                                 case 'O':
991                                         Expect ("OTATION");
992                                         def.Datatype = XmlSchemaDatatype.FromName ("NOTATION", XmlSchema.Namespace);
993                                         TryExpandPERefSpaceKeep ();
994                                         if (!SkipWhitespace ())
995                                                 throw NotWFError ("Whitespace is required after notation name in DTD attribute definition.");
996                                         Expect ('(');
997                                         SkipWhitespace ();
998                                         TryExpandPERef ();
999                                         def.EnumeratedNotations.Add (ReadName ());              // notation name
1000                                         SkipWhitespace ();
1001                                         TryExpandPERef ();
1002                                         while(PeekChar () == '|') {
1003                                                 ReadChar ();
1004                                                 SkipWhitespace ();
1005                                                 TryExpandPERef ();
1006                                                 def.EnumeratedNotations.Add (ReadName ());      // notation name
1007                                                 SkipWhitespace ();
1008                                                 TryExpandPERef ();
1009                                         }
1010                                         Expect (')');
1011                                         break;
1012                                 default:
1013                                         throw NotWFError ("attribute declaration syntax error.");
1014                                 }
1015                                 break;
1016                         default:        // Enumerated Values
1017                                 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKEN", XmlSchema.Namespace);
1018                                 TryExpandPERef ();
1019                                 Expect ('(');
1020                                 SkipWhitespace ();
1021                                 TryExpandPERef ();
1022                                 def.EnumeratedAttributeDeclaration.Add (
1023                                         def.Datatype.Normalize (ReadNmToken ()));       // enum value
1024                                 SkipWhitespace ();
1025                                 while(PeekChar () == '|') {
1026                                         ReadChar ();
1027                                         SkipWhitespace ();
1028                                         TryExpandPERef ();
1029                                         def.EnumeratedAttributeDeclaration.Add (
1030                                                 def.Datatype.Normalize (ReadNmToken ()));       // enum value
1031                                         SkipWhitespace ();
1032                                         TryExpandPERef ();
1033                                 }
1034                                 Expect (')');
1035                                 break;
1036                         }
1037                         TryExpandPERefSpaceKeep ();
1038                         if (!SkipWhitespace ())
1039                                 throw NotWFError ("Whitespace is required between type and occurence in DTD attribute definition.");
1040
1041                         // def_value
1042                         ReadAttributeDefaultValue (def);
1043
1044                         return def;
1045                 }
1046
1047                 private void ReadAttributeDefaultValue (DTDAttributeDefinition def)
1048                 {
1049                         if(PeekChar () == '#')
1050                         {
1051                                 ReadChar ();
1052                                 switch(PeekChar ())
1053                                 {
1054                                 case 'R':
1055                                         Expect ("REQUIRED");
1056                                         def.OccurenceType = DTDAttributeOccurenceType.Required;
1057                                         break;
1058                                 case 'I':
1059                                         Expect ("IMPLIED");
1060                                         def.OccurenceType = DTDAttributeOccurenceType.Optional;
1061                                         break;
1062                                 case 'F':
1063                                         Expect ("FIXED");
1064                                         def.OccurenceType = DTDAttributeOccurenceType.Fixed;
1065                                         if (!SkipWhitespace ())
1066                                                 throw NotWFError ("Whitespace is required between FIXED and actual value in DTD attribute definition.");
1067                                         def.UnresolvedDefaultValue = ReadDefaultAttribute ();
1068                                         break;
1069                                 }
1070                         } else {
1071                                 // one of the enumerated value
1072                                 SkipWhitespace ();
1073                                 TryExpandPERef ();
1074                                 def.UnresolvedDefaultValue = ReadDefaultAttribute ();
1075                         }
1076
1077                         // VC: If default value exists, it should be valid.
1078                         if (def.DefaultValue != null) {
1079                                 string normalized = def.Datatype.Normalize (def.DefaultValue);
1080                                 bool breakup = false;
1081                                 object parsed = null;
1082
1083                                 // enumeration validity
1084                                 if (def.EnumeratedAttributeDeclaration.Count > 0) {
1085                                         if (!def.EnumeratedAttributeDeclaration.Contains (normalized)) {
1086                                                 HandleError (new XmlSchemaException ("Default value is not one of the enumerated values.",
1087                                                         def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1088                                                 breakup = true;
1089                                         }
1090                                 }
1091                                 if (def.EnumeratedNotations.Count > 0) {
1092                                         if (!def.EnumeratedNotations.Contains (normalized)) {
1093                                                 HandleError (new XmlSchemaException ("Default value is not one of the enumerated notation values.",
1094                                                         def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1095                                                 breakup = true;
1096                                         }
1097                                 }
1098
1099                                 // type based validity
1100                                 if (!breakup) {
1101                                         try {
1102                                                 parsed = def.Datatype.ParseValue (normalized, DTD.NameTable, null);
1103                                         } catch (Exception ex) { // FIXME: (wishlist) bad catch ;-(
1104                                                 HandleError (new XmlSchemaException ("Invalid default value for ENTITY type.",
1105                                                         def.LineNumber, def.LinePosition, null, def.BaseURI, ex));
1106                                                 breakup = true;
1107                                         }
1108                                 }
1109                                 if (!breakup) {
1110                                         switch (def.Datatype.TokenizedType) {
1111                                         case XmlTokenizedType.ENTITY:
1112                                                 if (DTD.EntityDecls [normalized] == null)
1113                                                         HandleError (new XmlSchemaException ("Specified entity declaration used by default attribute value was not found.",
1114                                                                 def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1115                                                 break;
1116                                         case XmlTokenizedType.ENTITIES:
1117                                                 string [] entities = parsed as string [];
1118                                                 for (int i = 0; i < entities.Length; i++) {
1119                                                         string entity = entities [i];
1120                                                         if (DTD.EntityDecls [entity] == null)
1121                                                                 HandleError (new XmlSchemaException ("Specified entity declaration used by default attribute value was not found.",
1122                                                                         def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1123                                                 }
1124                                                 break;
1125                                         }
1126                                 }
1127                         }
1128                         // Extra ID attribute validity check.
1129                         if (def.Datatype != null && def.Datatype.TokenizedType == XmlTokenizedType.ID)
1130                                 if (def.UnresolvedDefaultValue != null)
1131                                         HandleError (new XmlSchemaException ("ID attribute must not have fixed value constraint.",
1132                                                 def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1133
1134                 }
1135
1136                 private DTDNotationDeclaration ReadNotationDecl()
1137                 {
1138                         DTDNotationDeclaration decl = new DTDNotationDeclaration (DTD);
1139                         if (!SkipWhitespace ())
1140                                 throw NotWFError ("Whitespace is required between NOTATION and name in DTD notation declaration.");
1141                         TryExpandPERef ();
1142                         decl.Name = ReadName ();        // notation name
1143                         /*
1144                         if (namespaces) {       // copy from SetProperties ;-)
1145                                 int indexOfColon = decl.Name.IndexOf (':');
1146
1147                                 if (indexOfColon == -1) {
1148                                         decl.Prefix = String.Empty;
1149                                         decl.LocalName = decl.Name;
1150                                 } else {
1151                                         decl.Prefix = decl.Name.Substring (0, indexOfColon);
1152                                         decl.LocalName = decl.Name.Substring (indexOfColon + 1);
1153                                 }
1154                         } else {
1155                         */
1156                                 decl.Prefix = String.Empty;
1157                                 decl.LocalName = decl.Name;
1158 //                      }
1159
1160                         SkipWhitespace ();
1161                         if(PeekChar () == 'P') {
1162                                 decl.PublicId = ReadPubidLiteral ();
1163                                 bool wsSkipped = SkipWhitespace ();
1164                                 if (PeekChar () == '\'' || PeekChar () == '"') {
1165                                         if (!wsSkipped)
1166                                                 throw NotWFError ("Whitespace is required between public id and system id.");
1167                                         decl.SystemId = ReadSystemLiteral (false);
1168                                         SkipWhitespace ();
1169                                 }
1170                         } else if(PeekChar () == 'S') {
1171                                 decl.SystemId = ReadSystemLiteral (true);
1172                                 SkipWhitespace ();
1173                         }
1174                         if(decl.PublicId == null && decl.SystemId == null)
1175                                 throw NotWFError ("public or system declaration required for \"NOTATION\" declaration.");
1176                         // This expanding is only allowed as a non-validating parser.
1177                         TryExpandPERef ();
1178                         Expect ('>');
1179                         return decl;
1180                 }
1181
1182                 private void ReadExternalID () {
1183                         switch (PeekChar ()) {
1184                         case 'S':
1185                                 cachedSystemId = ReadSystemLiteral (true);
1186                                 break;
1187                         case 'P':
1188                                 cachedPublicId = ReadPubidLiteral ();
1189                                 if (!SkipWhitespace ())
1190                                         throw NotWFError ("Whitespace is required between PUBLIC id and SYSTEM id.");
1191                                 cachedSystemId = ReadSystemLiteral (false);
1192                                 break;
1193                         }
1194                 }
1195
1196                 // The reader is positioned on the first 'S' of "SYSTEM".
1197                 private string ReadSystemLiteral (bool expectSYSTEM)
1198                 {
1199                         if(expectSYSTEM) {
1200                                 Expect ("SYSTEM");
1201                                 if (!SkipWhitespace ())
1202                                         throw NotWFError ("Whitespace is required after 'SYSTEM'.");
1203                         }
1204                         else
1205                                 SkipWhitespace ();
1206                         int quoteChar = ReadChar ();    // apos or quot
1207                         int c = 0;
1208                         ClearValueBuffer ();
1209                         while (c != quoteChar) {
1210                                 c = ReadChar ();
1211                                 if (c < 0)
1212                                         throw NotWFError ("Unexpected end of stream in ExternalID.");
1213                                 if (c != quoteChar)
1214                                         AppendValueChar (c);
1215                         }
1216                         return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
1217                 }
1218
1219                 private string ReadPubidLiteral()
1220                 {
1221                         Expect ("PUBLIC");
1222                         if (!SkipWhitespace ())
1223                                 throw NotWFError ("Whitespace is required after 'PUBLIC'.");
1224                         int quoteChar = ReadChar ();
1225                         int c = 0;
1226                         ClearValueBuffer ();
1227                         while(c != quoteChar)
1228                         {
1229                                 c = ReadChar ();
1230                                 if(c < 0) throw NotWFError ("Unexpected end of stream in ExternalID.");
1231                                 if(c != quoteChar && !XmlChar.IsPubidChar (c))
1232                                         throw NotWFError (String.Format ("character '{0}' not allowed for PUBLIC ID", (char) c));
1233                                 if (c != quoteChar)
1234                                         AppendValueChar (c);
1235                         }
1236                         return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
1237                 }
1238
1239                 // The reader is positioned on the first character
1240                 // of the name.
1241                 internal string ReadName ()
1242                 {
1243                         return ReadNameOrNmToken(false);
1244                 }
1245
1246                 // The reader is positioned on the first character
1247                 // of the name.
1248                 private string ReadNmToken ()
1249                 {
1250                         return ReadNameOrNmToken(true);
1251                 }
1252
1253                 private string ReadNameOrNmToken(bool isNameToken)
1254                 {
1255                         int ch = PeekChar ();
1256                         if(isNameToken) {
1257                                 if (!XmlChar.IsNameChar (ch))
1258                                         throw NotWFError (String.Format ("a nmtoken did not start with a legal character {0} ({1})", ch, (char) ch));
1259                         }
1260                         else {
1261                                 if (!XmlChar.IsFirstNameChar (ch))
1262                                         throw NotWFError (String.Format ("a name did not start with a legal character {0} ({1})", ch, (char) ch));
1263                         }
1264
1265                         nameLength = 0;
1266
1267                         AppendNameChar (ReadChar ());
1268
1269                         while (XmlChar.IsNameChar (PeekChar ())) {
1270                                 AppendNameChar (ReadChar ());
1271                         }
1272
1273                         return CreateNameString ();
1274                 }
1275
1276                 // Read the next character and compare it against the
1277                 // specified character.
1278                 private void Expect (int expected)
1279                 {
1280                         int ch = ReadChar ();
1281
1282                         if (ch != expected) {
1283                                 throw NotWFError (String.Format (CultureInfo.InvariantCulture, 
1284                                                 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
1285                                                 (char) expected,
1286                                                 expected,
1287                                                 (char) ch,
1288                                                 ch));
1289                         }
1290                 }
1291
1292                 private void Expect (string expected)
1293                 {
1294                         int len = expected.Length;
1295                         for (int i=0; i< len; i++)
1296                                 Expect (expected [i]);
1297                 }
1298
1299                 private void ExpectAfterWhitespace (char c)
1300                 {
1301                         while (true) {
1302                                 int i = ReadChar ();
1303                                 if (XmlChar.IsWhitespace (i))
1304                                         continue;
1305                                 if (c != i)
1306                                         throw NotWFError (String.Format (CultureInfo.InvariantCulture, "Expected {0} but found {1} [{2}].", c, (char) i, i));
1307                                 break;
1308                         }
1309                 }
1310
1311                 // Does not consume the first non-whitespace character.
1312                 private bool SkipWhitespace ()
1313                 {
1314                         bool skipped = XmlChar.IsWhitespace (PeekChar ());
1315                         while (XmlChar.IsWhitespace (PeekChar ()))
1316                                 ReadChar ();
1317                         return skipped;
1318                 }
1319
1320                 private int PeekChar ()
1321                 {
1322                         return currentInput.PeekChar ();
1323                 }
1324
1325                 private int ReadChar ()
1326                 {
1327                         return currentInput.ReadChar ();
1328                 }
1329
1330                 // The reader is positioned on the first character after
1331                 // the leading '<!--'.
1332                 private void ReadComment ()
1333                 {
1334                         currentInput.AllowTextDecl = false;
1335
1336                         while (PeekChar () != -1) {
1337                                 int ch = ReadChar ();
1338
1339                                 if (ch == '-' && PeekChar () == '-') {
1340                                         ReadChar ();
1341
1342                                         if (PeekChar () != '>')
1343                                                 throw NotWFError ("comments cannot contain '--'");
1344
1345                                         ReadChar ();
1346                                         break;
1347                                 }
1348
1349                                 if (XmlChar.IsInvalid (ch))
1350                                         throw NotWFError ("Not allowed character was found.");
1351                         }
1352                 }
1353
1354                 // The reader is positioned on the first character
1355                 // of the target.
1356                 //
1357                 // It may be xml declaration or processing instruction.
1358                 private void ReadProcessingInstruction ()
1359                 {
1360                         string target = ReadName ();
1361                         if (target == "xml") {
1362                                 ReadTextDeclaration ();
1363                                 return;
1364                         } else if (String.Compare (target, "xml", true, CultureInfo.InvariantCulture) == 0)
1365                                 throw NotWFError ("Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
1366
1367                         currentInput.AllowTextDecl = false;
1368
1369                         if (!SkipWhitespace ())
1370                                 if (PeekChar () != '?')
1371                                         throw NotWFError ("Invalid processing instruction name was found.");
1372
1373                         while (PeekChar () != -1) {
1374                                 int ch = ReadChar ();
1375
1376                                 if (ch == '?' && PeekChar () == '>') {
1377                                         ReadChar ();
1378                                         break;
1379                                 }
1380                         }
1381                 }
1382
1383                 // The reader is positioned after "<?xml "
1384                 private void ReadTextDeclaration ()
1385                 {
1386                         if (!currentInput.AllowTextDecl)
1387                                 throw NotWFError ("Text declaration cannot appear in this state.");
1388
1389                         currentInput.AllowTextDecl = false;
1390
1391                         SkipWhitespace ();
1392
1393                         // version decl
1394                         if (PeekChar () == 'v') {
1395                                 Expect ("version");
1396                                 ExpectAfterWhitespace ('=');
1397                                 SkipWhitespace ();
1398                                 int quoteChar = ReadChar ();
1399                                 char [] expect1_0 = new char [3];
1400                                 int versionLength = 0;
1401                                 switch (quoteChar) {
1402                                 case '\'':
1403                                 case '"':
1404                                         while (PeekChar () != quoteChar) {
1405                                                 if (PeekChar () == -1)
1406                                                         throw NotWFError ("Invalid version declaration inside text declaration.");
1407                                                 else if (versionLength == 3)
1408                                                         throw NotWFError ("Invalid version number inside text declaration.");
1409                                                 else {
1410                                                         expect1_0 [versionLength] = (char) ReadChar ();
1411                                                         versionLength++;
1412                                                         if (versionLength == 3 && new String (expect1_0) != "1.0")
1413                                                                 throw NotWFError ("Invalid version number inside text declaration.");
1414                                                 }
1415                                         }
1416                                         ReadChar ();
1417                                         SkipWhitespace ();
1418                                         break;
1419                                 default:
1420                                         throw NotWFError ("Invalid version declaration inside text declaration.");
1421                                 }
1422                         }
1423
1424                         if (PeekChar () == 'e') {
1425                                 Expect ("encoding");
1426                                 ExpectAfterWhitespace ('=');
1427                                 SkipWhitespace ();
1428                                 int quoteChar = ReadChar ();
1429                                 switch (quoteChar) {
1430                                 case '\'':
1431                                 case '"':
1432                                         while (PeekChar () != quoteChar)
1433                                                 if (ReadChar () == -1)
1434                                                         throw NotWFError ("Invalid encoding declaration inside text declaration.");
1435                                         ReadChar ();
1436                                         SkipWhitespace ();
1437                                         break;
1438                                 default:
1439                                         throw NotWFError ("Invalid encoding declaration inside text declaration.");
1440                                 }
1441                                 // Encoding value should be checked inside XmlInputStream.
1442                         }
1443                         else
1444                                 throw NotWFError ("Encoding declaration is mandatory in text declaration.");
1445
1446                         Expect ("?>");
1447                 }
1448
1449                 // Note that now this method behaves differently from
1450                 // XmlTextReader's one. It calles AppendValueChar() internally.
1451                 private int ReadCharacterReference ()
1452                 {
1453                         int value = 0;
1454
1455                         if (PeekChar () == 'x') {
1456                                 ReadChar ();
1457
1458                                 while (PeekChar () != ';' && PeekChar () != -1) {
1459                                         int ch = ReadChar ();
1460
1461                                         if (ch >= '0' && ch <= '9')
1462                                                 value = (value << 4) + ch - '0';
1463                                         else if (ch >= 'A' && ch <= 'F')
1464                                                 value = (value << 4) + ch - 'A' + 10;
1465                                         else if (ch >= 'a' && ch <= 'f')
1466                                                 value = (value << 4) + ch - 'a' + 10;
1467                                         else
1468                                                 throw NotWFError (String.Format (
1469                                                                 CultureInfo.InvariantCulture,
1470                                                                 "invalid hexadecimal digit: {0} (#x{1:X})",
1471                                                                 (char) ch,
1472                                                                 ch));
1473                                 }
1474                         } else {
1475                                 while (PeekChar () != ';' && PeekChar () != -1) {
1476                                         int ch = ReadChar ();
1477
1478                                         if (ch >= '0' && ch <= '9')
1479                                                 value = value * 10 + ch - '0';
1480                                         else
1481                                                 throw NotWFError (String.Format (
1482                                                                 CultureInfo.InvariantCulture,
1483                                                                 "invalid decimal digit: {0} (#x{1:X})",
1484                                                                 (char) ch,
1485                                                                 ch));
1486                                 }
1487                         }
1488
1489                         ReadChar (); // ';'
1490
1491                         // There is no way to save surrogate pairs...
1492                         if (XmlChar.IsInvalid (value))
1493                                 throw NotWFError ("Referenced character was not allowed in XML.");
1494                         AppendValueChar (value);
1495                         return value;
1496                 }
1497
1498                 private void AppendNameChar (int ch)
1499                 {
1500                         CheckNameCapacity ();
1501                         if (ch < Char.MaxValue)
1502                                 nameBuffer [nameLength++] = (char) ch;
1503                         else {
1504                                 nameBuffer [nameLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
1505                                 CheckNameCapacity ();
1506                                 nameBuffer [nameLength++] = (char) (ch % 0x10000 + 0xDC00);
1507                         }
1508                 }
1509
1510                 private void CheckNameCapacity ()
1511                 {
1512                         if (nameLength == nameCapacity) {
1513                                 nameCapacity = nameCapacity * 2;
1514                                 char [] oldNameBuffer = nameBuffer;
1515                                 nameBuffer = new char [nameCapacity];
1516                                 Array.Copy (oldNameBuffer, nameBuffer, nameLength);
1517                         }
1518                 }
1519
1520                 private string CreateNameString ()
1521                 {
1522                         return DTD.NameTable.Add (nameBuffer, 0, nameLength);
1523                 }
1524
1525                 private void AppendValueChar (int ch)
1526                 {
1527                         //See http://www.faqs.org/rfcs/rfc2781.html for used algorithm
1528                         if (ch < 0x10000) {
1529                                 valueBuffer.Append ((char) ch);
1530                                 return;
1531                         }
1532                         if (ch > 0x10FFFF)
1533                                 throw new XmlException ("The numeric entity value is too large", null, LineNumber, LinePosition);
1534                         else
1535                         {
1536                                 int utag = ch - 0x10000;
1537                                 valueBuffer.Append((char) ((utag >> 10) + 0xD800));
1538                                 valueBuffer.Append((char) ((utag & 0x3FF) + 0xDC00));
1539                         }
1540                 }
1541
1542                 private string CreateValueString ()
1543                 {
1544                         return valueBuffer.ToString ();
1545                 }
1546                 
1547                 private void ClearValueBuffer ()
1548                 {
1549                         valueBuffer.Length = 0;
1550                 }
1551
1552                 // The reader is positioned on the quote character.
1553                 // *Keeps quote char* to value to get_QuoteChar() correctly.
1554                 private string ReadDefaultAttribute ()
1555                 {
1556                         ClearValueBuffer ();
1557
1558                         TryExpandPERef ();
1559
1560                         int quoteChar = ReadChar ();
1561
1562                         if (quoteChar != '\'' && quoteChar != '\"')
1563                                 throw NotWFError ("an attribute value was not quoted");
1564
1565                         AppendValueChar (quoteChar);
1566
1567                         while (PeekChar () != quoteChar) {
1568                                 int ch = ReadChar ();
1569
1570                                 switch (ch)
1571                                 {
1572                                 case '<':
1573                                         throw NotWFError ("attribute values cannot contain '<'");
1574                                 case -1:
1575                                         throw NotWFError ("unexpected end of file in an attribute value");
1576                                 case '&':
1577                                         AppendValueChar (ch);
1578                                         if (PeekChar () == '#')
1579                                                 break;
1580                                         // Check XML 1.0 section 3.1 WFC.
1581                                         string entName = ReadName ();
1582                                         Expect (';');
1583                                         if (XmlChar.GetPredefinedEntity (entName) < 0) {
1584                                                 DTDEntityDeclaration entDecl = 
1585                                                         DTD == null ? null : DTD.EntityDecls [entName];
1586                                                 if (entDecl == null || entDecl.SystemId != null)
1587                                                         // WFC: Entity Declared (see 4.1)
1588                                                         if (DTD.IsStandalone || (DTD.SystemId == null && !DTD.InternalSubsetHasPEReference))
1589                                                                 throw NotWFError ("Reference to external entities is not allowed in attribute value.");
1590                                         }
1591                                         valueBuffer.Append (entName);
1592                                         AppendValueChar (';');
1593                                         break;
1594                                 default:
1595                                         AppendValueChar (ch);
1596                                         break;
1597                                 }
1598                         }
1599
1600                         ReadChar (); // quoteChar
1601                         AppendValueChar (quoteChar);
1602
1603                         return CreateValueString ();
1604                 }
1605
1606                 private void PushParserInput (string url)
1607                 {
1608                         Uri baseUri = null;
1609                         try {
1610                                 if (DTD.BaseURI != null && DTD.BaseURI.Length > 0)
1611                                         baseUri = new Uri (DTD.BaseURI);
1612                         } catch (UriFormatException) {
1613                         }
1614
1615                         Uri absUri = url != null && url.Length > 0 ?
1616                                 DTD.Resolver.ResolveUri (baseUri, url) : baseUri;
1617                         string absPath = absUri != null ? absUri.ToString () : String.Empty;
1618
1619                         foreach (XmlParserInput i in parserInputStack.ToArray ()) {
1620                                 if (i.BaseURI == absPath)
1621                                         throw NotWFError ("Nested inclusion is not allowed: " + url);
1622                         }
1623                         parserInputStack.Push (currentInput);
1624                         Stream s = null;
1625                         try {
1626                                 s = DTD.Resolver.GetEntity (absUri, null, typeof (Stream)) as Stream;
1627                                 currentInput = new XmlParserInput (new XmlStreamReader (s), absPath);
1628                         } catch (Exception ex) { // FIXME: (wishlist) Bad exception catch ;-(
1629                                 if (s != null)
1630                                         s.Close ();
1631                                 int line = currentInput == null ? 0 : currentInput.LineNumber;
1632                                 int col = currentInput == null ? 0 : currentInput.LinePosition;
1633                                 string bu = (currentInput == null) ? String.Empty : currentInput.BaseURI;
1634                                 HandleError (new XmlSchemaException ("Specified external entity not found. Target URL is " + url + " .",
1635                                         line, col, null, bu, ex));
1636                                 currentInput = new XmlParserInput (new StringReader (String.Empty), absPath);
1637                         }
1638                 }
1639
1640                 private void PopParserInput ()
1641                 {
1642                         currentInput.Close ();
1643                         currentInput = parserInputStack.Pop () as XmlParserInput;
1644                 }
1645
1646                 private void HandleError (XmlSchemaException ex)
1647                 {
1648 #if DTD_HANDLE_EVENTS
1649                         if (this.ValidationEventHandler != null)
1650                                 ValidationEventHandler (this, new ValidationEventArgs (ex, ex.Message, XmlSeverityType.Error));
1651 #else
1652                         DTD.AddError (ex);
1653 #endif
1654                 }
1655         }
1656 }