2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[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.InitialState = 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                                 string peValue = GetPEValue (peName);
207                                 if (peValue == String.Empty)
208                                         break;
209                                 currentInput.InsertParameterEntityBuffer (peValue);
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.InitialState = 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
585                         decl.Name = ReadName ();
586                         if (!SkipWhitespace ())
587                                 throw NotWFError ("Whitespace is required after name in DTD parameter entity declaration.");
588
589                         if (PeekChar () == 'S' || PeekChar () == 'P') {
590                                 // read publicId/systemId
591                                 ReadExternalID ();
592                                 decl.PublicId = cachedPublicId;
593                                 decl.SystemId = cachedSystemId;
594                                 SkipWhitespace ();
595                                 decl.Resolve (this.DTD.Resolver);
596
597                                 ResolveExternalEntityReplacementText (decl);
598                         } else {
599                                 TryExpandPERef ();
600                                 int quoteChar = ReadChar ();
601                                 if (quoteChar != '\'' && quoteChar != '"')
602                                         throw NotWFError ("quotation char was expected.");
603                                 ClearValueBuffer ();
604                                 bool loop = true;
605                                 while (loop) {
606                                         int c = ReadChar ();
607                                         switch (c) {
608                                         case -1:
609                                                 throw NotWFError ("unexpected end of stream in entity value definition.");
610                                         case '"':
611                                                 if (quoteChar == '"')
612                                                         loop = false;
613                                                 else
614                                                         AppendValueChar ('"');
615                                                 break;
616                                         case '\'':
617                                                 if (quoteChar == '\'')
618                                                         loop = false;
619                                                 else
620                                                         AppendValueChar ('\'');
621                                                 break;
622                                         default:
623                                                 if (XmlChar.IsInvalid (c))
624                                                         throw NotWFError ("Invalid character was used to define parameter entity.");
625                                                 AppendValueChar (c);
626                                                 break;
627                                         }
628                                 }
629                                 decl.LiteralEntityValue = CreateValueString ();
630                                 ClearValueBuffer ();
631                                 ResolveInternalEntityReplacementText (decl);
632                         }
633                         ExpectAfterWhitespace ('>');
634
635
636                         if (DTD.PEDecls [decl.Name] == null) {
637                                 DTD.PEDecls.Add (decl.Name, decl);
638                         }
639                 }
640
641                 private void ResolveExternalEntityReplacementText (DTDEntityBase decl)
642                 {
643                         if (decl.LiteralEntityValue.StartsWith ("<?xml")) {
644                                 XmlTextReader xtr = new XmlTextReader (decl.LiteralEntityValue, XmlNodeType.Element, null);
645                                 if (decl is DTDEntityDeclaration) {
646                                         // GE - also checked as valid contents
647                                         StringBuilder sb = new StringBuilder ();
648                                         xtr.Normalization = this.Normalization;
649                                         xtr.Read ();
650                                         while (!xtr.EOF)
651                                                 sb.Append (xtr.ReadOuterXml ());
652                                         decl.ReplacementText = sb.ToString ();
653                                 }
654                                 else
655                                         // PE
656                                         decl.ReplacementText = xtr.GetRemainder ().ReadToEnd ();
657                         }
658                         else
659                                 decl.ReplacementText = decl.LiteralEntityValue;
660                 }
661
662                 private void ResolveInternalEntityReplacementText (DTDEntityBase decl)
663                 {
664                         string value = decl.LiteralEntityValue;
665                         int len = value.Length;
666                         ClearValueBuffer ();
667                         for (int i = 0; i < len; i++) {
668                                 int ch = value [i];
669                                 int end = 0;
670                                 string name;
671                                 switch (ch) {
672                                 case '&':
673                                         i++;
674                                         end = value.IndexOf (';', i);
675                                         if (end < i + 1)
676                                                 throw new XmlException (decl, decl.BaseURI, "Invalid reference markup.");
677                                         // expand charref
678                                         if (value [i] == '#') {
679                                                 i++;
680                                                 ch = GetCharacterReference (decl, value, ref i, end);
681                                                 if (XmlChar.IsInvalid (ch))
682                                                         throw NotWFError ("Invalid character was used to define parameter entity.");
683
684                                         } else {
685                                                 name = value.Substring (i, end - i);
686                                                 // don't expand "general" entity.
687                                                 AppendValueChar ('&');
688                                                 valueBuffer.Append (name);
689                                                 AppendValueChar (';');
690                                                 i = end;
691                                                 break;
692                                         }
693                                         if (XmlChar.IsInvalid (ch))
694                                                 throw new XmlException (decl, decl.BaseURI, "Invalid character was found in the entity declaration.");
695                                         AppendValueChar (ch);
696                                         break;
697                                 case '%':
698                                         i++;
699                                         end = value.IndexOf (';', i);
700                                         if (end < i + 1)
701                                                 throw new XmlException (decl, decl.BaseURI, "Invalid reference markup.");
702                                         name = value.Substring (i, end - i);
703                                         valueBuffer.Append (GetPEValue (name));
704                                         i = end;
705                                         break;
706                                 default:
707                                         AppendValueChar (ch);
708                                         break;
709                                 }
710                         }
711                         decl.ReplacementText = CreateValueString ();
712
713                         if (decl is DTDEntityDeclaration) {
714                                 // GE - also checked as valid contents
715                                 XmlTextReader xtr = new XmlTextReader (decl.ReplacementText, XmlNodeType.Element, null);
716                                 StringBuilder sb = new StringBuilder ();
717                                 xtr.Normalization = this.Normalization;
718                                 xtr.Read ();
719                                 while (!xtr.EOF)
720                                         sb.Append (xtr.ReadOuterXml ());
721                                 decl.ReplacementText = sb.ToString ();
722                         }
723                         ClearValueBuffer ();
724                 }
725
726                 private int GetCharacterReference (DTDEntityBase li, string value, ref int index, int end)
727                 {
728                         int ret = 0;
729                         if (value [index] == 'x') {
730                                 try {
731                                         ret = int.Parse (value.Substring (index + 1, end - index - 1), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
732                                 } catch (FormatException) {
733                                         throw new XmlException (li, li.BaseURI, "Invalid number for a character reference.");
734                                 }
735                         } else {
736                                 try {
737                                         ret = int.Parse (value.Substring (index, end - index), CultureInfo.InvariantCulture);
738                                 } catch (FormatException) {
739                                         throw new XmlException (li, li.BaseURI, "Invalid number for a character reference.");
740                                 }
741                         }
742                         index = end;
743                         return ret;
744                 }
745
746                 private string GetPEValue (string peName)
747                 {
748                         DTDParameterEntityDeclaration peDecl =
749                                 DTD.PEDecls [peName] as DTDParameterEntityDeclaration;
750                         if (peDecl != null) {
751                                 if (peDecl.IsInternalSubset)
752                                         throw NotWFError ("Parameter entity is not allowed in internal subset entity '" + peName + "'");
753                                 return peDecl.ReplacementText;
754                         }
755                         // See XML 1.0 section 4.1 for both WFC and VC.
756                         if ((DTD.SystemId == null && !DTD.InternalSubsetHasPEReference) || DTD.IsStandalone)
757                                 throw NotWFError (String.Format ("Parameter entity '{0}' not found.",peName));
758                         HandleError (new XmlSchemaException (
759                                 "Parameter entity " + peName + " not found.", null));
760                         return "";
761                 }
762
763                 private bool TryExpandPERef ()
764                 {
765                         if (PeekChar () != '%')
766                                 return false;
767                         while (PeekChar () == '%') {
768                                 TryExpandPERefSpaceKeep ();
769                                 SkipWhitespace ();
770                         }
771                         return true;
772                 }
773
774                 // Tries to expand parameter entities, but it should not skip spaces
775                 private bool TryExpandPERefSpaceKeep ()
776                 {
777                         if (PeekChar () == '%') {
778                                 if (this.processingInternalSubset)
779                                         throw NotWFError ("Parameter entity reference is not allowed inside internal subset.");
780                                 ReadChar ();
781                                 ExpandPERef ();
782                                 return true;
783                         }
784                         else
785                                 return false;
786                 }
787
788                 // reader is positioned after '%'
789                 private void ExpandPERef ()
790                 {
791                         string peName = ReadName ();
792                         Expect (';');
793                         DTDParameterEntityDeclaration peDecl =
794                                 DTD.PEDecls [peName] as DTDParameterEntityDeclaration;
795                         if (peDecl == null) {
796                                 HandleError (new XmlSchemaException ("Parameter entity " + peName + " not found.", null));
797                                 return; // do nothing
798                         }
799                         // FIXME: These leading/trailing ' ' is anyways supplied inside this method!
800 //                      currentInput.InsertParameterEntityBuffer (" " + peDecl.ReplacementText + " ");
801                         currentInput.InsertParameterEntityBuffer (peDecl.ReplacementText);
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.IsInternalSubset = this.processingInternalSubset;
809                         TryExpandPERef ();
810                         decl.Name = ReadName ();
811                         if (!SkipWhitespace ())
812                                 throw NotWFError ("Whitespace is required between name and content in DTD entity declaration.");
813                         TryExpandPERef ();
814
815                         if (PeekChar () == 'S' || PeekChar () == 'P') {
816                                 // external entity
817                                 ReadExternalID ();
818                                 decl.PublicId = cachedPublicId;
819                                 decl.SystemId = cachedSystemId;
820                                 if (SkipWhitespace ()) {
821                                         if (PeekChar () == 'N') {
822                                                 // NDataDecl
823                                                 Expect ("NDATA");
824                                                 if (!SkipWhitespace ())
825                                                         throw NotWFError ("Whitespace is required after NDATA.");
826                                                 decl.NotationName = ReadName ();        // ndata_name
827                                         }
828                                 }
829                                 if (decl.NotationName == null) {
830                                         decl.Resolve (this.DTD.Resolver);
831                                         ResolveExternalEntityReplacementText (decl);
832                                 } else {
833                                         // Unparsed entity.
834                                         decl.LiteralEntityValue = String.Empty;
835                                         decl.ReplacementText = String.Empty;
836                                 }
837                         }
838                         else {
839                                 // literal entity
840                                 ReadEntityValueDecl (decl);
841                                 ResolveInternalEntityReplacementText (decl);
842                         }
843                         SkipWhitespace ();
844                         // This expanding is only allowed as a non-validating parser.
845                         TryExpandPERef ();
846                         Expect ('>');
847                         return decl;
848                 }
849
850                 private void ReadEntityValueDecl (DTDEntityDeclaration decl)
851                 {
852                         SkipWhitespace ();
853                         // quotation char will be finally removed on unescaping
854                         int quoteChar = ReadChar ();
855                         if (quoteChar != '\'' && quoteChar != '"')
856                                 throw NotWFError ("quotation char was expected.");
857                         ClearValueBuffer ();
858
859                         while (PeekChar () != quoteChar) {
860                                 int ch = ReadChar ();
861                                 switch (ch) {
862                                 case '%':
863                                         string name = ReadName ();
864                                         Expect (';');
865                                         if (decl.IsInternalSubset)
866                                                 throw NotWFError (String.Format ("Parameter entity is not allowed in internal subset entity '{0}'", name));
867                                         valueBuffer.Append (GetPEValue (name));
868                                         break;
869                                 case -1:
870                                         throw NotWFError ("unexpected end of stream.");
871                                 default:
872                                         if (this.normalization && XmlChar.IsInvalid (ch))
873                                                 throw NotWFError ("Invalid character was found in the entity declaration.");
874                                         AppendValueChar (ch);
875                                         break;
876                                 }
877                         }
878 //                      string value = Dereference (CreateValueString (), false);
879                         string value = CreateValueString ();
880                         ClearValueBuffer ();
881
882                         Expect (quoteChar);
883                         decl.LiteralEntityValue = value;
884                 }
885
886                 private DTDAttListDeclaration ReadAttListDecl ()
887                 {
888                         TryExpandPERefSpaceKeep ();
889                         if (!SkipWhitespace ())
890                                 throw NotWFError ("Whitespace is required between ATTLIST and name in DTD attlist declaration.");
891                         TryExpandPERef ();
892                         string name = ReadName ();      // target element name
893                         DTDAttListDeclaration decl =
894                                 DTD.AttListDecls [name] as DTDAttListDeclaration;
895                         if (decl == null)
896                                 decl = new DTDAttListDeclaration (DTD);
897                         decl.IsInternalSubset = this.processingInternalSubset;
898                         decl.Name = name;
899
900                         if (!SkipWhitespace ())
901                                 if (PeekChar () != '>')
902                                         throw NotWFError ("Whitespace is required between name and content in non-empty DTD attlist declaration.");
903
904                         TryExpandPERef ();
905
906                         while (XmlChar.IsNameChar (PeekChar ())) {
907                                 DTDAttributeDefinition def = ReadAttributeDefinition ();
908                                 // There must not be two or more ID attributes.
909                                 if (def.Datatype.TokenizedType == XmlTokenizedType.ID) {
910                                         for (int i = 0; i < decl.Definitions.Count; i++) {
911                                                 DTDAttributeDefinition d = decl [i];
912                                                 if (d.Datatype.TokenizedType == XmlTokenizedType.ID) {
913                                                         HandleError (new XmlSchemaException ("AttList declaration must not contain two or more ID attributes.",
914                                                                 def.LineNumber, def.LinePosition, null, def.BaseURI, null));
915                                                         break;
916                                                 }
917                                         }
918                                 }
919                                 if (decl [def.Name] == null)
920                                         decl.Add (def);
921                                 SkipWhitespace ();
922                                 TryExpandPERef ();
923                         }
924                         SkipWhitespace ();
925                         // This expanding is only allowed as a non-validating parser.
926                         TryExpandPERef ();
927                         Expect ('>');
928                         return decl;
929                 }
930
931                 private DTDAttributeDefinition ReadAttributeDefinition ()
932                 {
933                         DTDAttributeDefinition def = new DTDAttributeDefinition (DTD);
934                         def.IsInternalSubset = this.processingInternalSubset;
935
936                         // attr_name
937                         TryExpandPERef ();
938                         def.Name = ReadName ();
939                         if (!SkipWhitespace ())
940                                 throw NotWFError ("Whitespace is required between name and content in DTD attribute definition.");
941
942                         // attr_value
943                         TryExpandPERef ();
944                         switch(PeekChar ()) {
945                         case 'C':       // CDATA
946                                 Expect ("CDATA");
947                                 def.Datatype = XmlSchemaDatatype.FromName ("normalizedString", XmlSchema.Namespace);
948                                 break;
949                         case 'I':       // ID, IDREF, IDREFS
950                                 Expect ("ID");
951                                 if(PeekChar () == 'R') {
952                                         Expect ("REF");
953                                         if(PeekChar () == 'S') {
954                                                 // IDREFS
955                                                 ReadChar ();
956                                                 def.Datatype = XmlSchemaDatatype.FromName ("IDREFS", XmlSchema.Namespace);
957                                         }
958                                         else    // IDREF
959                                                 def.Datatype = XmlSchemaDatatype.FromName ("IDREF", XmlSchema.Namespace);
960                                 }
961                                 else    // ID
962                                         def.Datatype = XmlSchemaDatatype.FromName ("ID", XmlSchema.Namespace);
963                                 break;
964                         case 'E':       // ENTITY, ENTITIES
965                                 Expect ("ENTIT");
966                                 switch(ReadChar ()) {
967                                         case 'Y':       // ENTITY
968                                                 def.Datatype = XmlSchemaDatatype.FromName ("ENTITY", XmlSchema.Namespace);
969                                                 break;
970                                         case 'I':       // ENTITIES
971                                                 Expect ("ES");
972                                                 def.Datatype = XmlSchemaDatatype.FromName ("ENTITIES", XmlSchema.Namespace);
973                                                 break;
974                                 }
975                                 break;
976                         case 'N':       // NMTOKEN, NMTOKENS, NOTATION
977                                 ReadChar ();
978                                 switch(PeekChar ()) {
979                                 case 'M':
980                                         Expect ("MTOKEN");
981                                         if(PeekChar ()=='S') {  // NMTOKENS
982                                                 ReadChar ();
983                                                 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKENS", XmlSchema.Namespace);
984                                         }
985                                         else    // NMTOKEN
986                                                 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKEN", XmlSchema.Namespace);
987                                         break;
988                                 case 'O':
989                                         Expect ("OTATION");
990                                         def.Datatype = XmlSchemaDatatype.FromName ("NOTATION", XmlSchema.Namespace);
991                                         if (!SkipWhitespace ())
992                                                 throw NotWFError ("Whitespace is required between name and content in DTD attribute definition.");
993                                         Expect ('(');
994                                         SkipWhitespace ();
995                                         def.EnumeratedNotations.Add (ReadName ());              // notation name
996                                         SkipWhitespace ();
997                                         while(PeekChar () == '|') {
998                                                 ReadChar ();
999                                                 SkipWhitespace ();
1000                                                 def.EnumeratedNotations.Add (ReadName ());      // notation name
1001                                                 SkipWhitespace ();
1002                                         }
1003                                         Expect (')');
1004                                         break;
1005                                 default:
1006                                         throw NotWFError ("attribute declaration syntax error.");
1007                                 }
1008                                 break;
1009                         default:        // Enumerated Values
1010                                 def.Datatype = XmlSchemaDatatype.FromName ("NMTOKEN", XmlSchema.Namespace);
1011                                 TryExpandPERef ();
1012                                 Expect ('(');
1013                                 SkipWhitespace ();
1014                                 def.EnumeratedAttributeDeclaration.Add (
1015                                         def.Datatype.Normalize (ReadNmToken ()));       // enum value
1016                                 SkipWhitespace ();
1017                                 while(PeekChar () == '|') {
1018                                         ReadChar ();
1019                                         SkipWhitespace ();
1020                                         def.EnumeratedAttributeDeclaration.Add (
1021                                                 def.Datatype.Normalize (ReadNmToken ()));       // enum value
1022                                         SkipWhitespace ();
1023                                 }
1024                                 Expect (')');
1025                                 break;
1026                         }
1027                         TryExpandPERefSpaceKeep ();
1028                         if (!SkipWhitespace ())
1029                                 throw NotWFError ("Whitespace is required between type and occurence in DTD attribute definition.");
1030
1031                         // def_value
1032                         ReadAttributeDefaultValue (def);
1033
1034                         return def;
1035                 }
1036
1037                 private void ReadAttributeDefaultValue (DTDAttributeDefinition def)
1038                 {
1039                         if(PeekChar () == '#')
1040                         {
1041                                 ReadChar ();
1042                                 switch(PeekChar ())
1043                                 {
1044                                 case 'R':
1045                                         Expect ("REQUIRED");
1046                                         def.OccurenceType = DTDAttributeOccurenceType.Required;
1047                                         break;
1048                                 case 'I':
1049                                         Expect ("IMPLIED");
1050                                         def.OccurenceType = DTDAttributeOccurenceType.Optional;
1051                                         break;
1052                                 case 'F':
1053                                         Expect ("FIXED");
1054                                         def.OccurenceType = DTDAttributeOccurenceType.Fixed;
1055                                         if (!SkipWhitespace ())
1056                                                 throw NotWFError ("Whitespace is required between FIXED and actual value in DTD attribute definition.");
1057                                         def.UnresolvedDefaultValue = ReadDefaultAttribute ();
1058                                         break;
1059                                 }
1060                         } else {
1061                                 // one of the enumerated value
1062                                 SkipWhitespace ();
1063                                 TryExpandPERef ();
1064                                 def.UnresolvedDefaultValue = ReadDefaultAttribute ();
1065                         }
1066
1067                         // VC: If default value exists, it should be valid.
1068                         if (def.DefaultValue != null) {
1069                                 string normalized = def.Datatype.Normalize (def.DefaultValue);
1070                                 bool breakup = false;
1071                                 object parsed = null;
1072
1073                                 // enumeration validity
1074                                 if (def.EnumeratedAttributeDeclaration.Count > 0) {
1075                                         if (!def.EnumeratedAttributeDeclaration.Contains (normalized)) {
1076                                                 HandleError (new XmlSchemaException ("Default value is not one of the enumerated values.",
1077                                                         def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1078                                                 breakup = true;
1079                                         }
1080                                 }
1081                                 if (def.EnumeratedNotations.Count > 0) {
1082                                         if (!def.EnumeratedNotations.Contains (normalized)) {
1083                                                 HandleError (new XmlSchemaException ("Default value is not one of the enumerated notation values.",
1084                                                         def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1085                                                 breakup = true;
1086                                         }
1087                                 }
1088
1089                                 // type based validity
1090                                 if (!breakup) {
1091                                         try {
1092                                                 parsed = def.Datatype.ParseValue (normalized, DTD.NameTable, null);
1093                                         } catch (Exception ex) { // FIXME: (wishlist) bad catch ;-(
1094                                                 HandleError (new XmlSchemaException ("Invalid default value for ENTITY type.",
1095                                                         def.LineNumber, def.LinePosition, null, def.BaseURI, ex));
1096                                                 breakup = true;
1097                                         }
1098                                 }
1099                                 if (!breakup) {
1100                                         switch (def.Datatype.TokenizedType) {
1101                                         case XmlTokenizedType.ENTITY:
1102                                                 if (DTD.EntityDecls [normalized] == null)
1103                                                         HandleError (new XmlSchemaException ("Specified entity declaration used by default attribute value was not found.",
1104                                                                 def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1105                                                 break;
1106                                         case XmlTokenizedType.ENTITIES:
1107                                                 string [] entities = parsed as string [];
1108                                                 for (int i = 0; i < entities.Length; i++) {
1109                                                         string entity = entities [i];
1110                                                         if (DTD.EntityDecls [entity] == null)
1111                                                                 HandleError (new XmlSchemaException ("Specified entity declaration used by default attribute value was not found.",
1112                                                                         def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1113                                                 }
1114                                                 break;
1115                                         }
1116                                 }
1117                         }
1118                         // Extra ID attribute validity check.
1119                         if (def.Datatype != null && def.Datatype.TokenizedType == XmlTokenizedType.ID)
1120                                 if (def.UnresolvedDefaultValue != null)
1121                                         HandleError (new XmlSchemaException ("ID attribute must not have fixed value constraint.",
1122                                                 def.LineNumber, def.LinePosition, null, def.BaseURI, null));
1123
1124                 }
1125
1126                 private DTDNotationDeclaration ReadNotationDecl()
1127                 {
1128                         DTDNotationDeclaration decl = new DTDNotationDeclaration (DTD);
1129                         if (!SkipWhitespace ())
1130                                 throw NotWFError ("Whitespace is required between NOTATION and name in DTD notation declaration.");
1131                         TryExpandPERef ();
1132                         decl.Name = ReadName ();        // notation name
1133                         /*
1134                         if (namespaces) {       // copy from SetProperties ;-)
1135                                 int indexOfColon = decl.Name.IndexOf (':');
1136
1137                                 if (indexOfColon == -1) {
1138                                         decl.Prefix = String.Empty;
1139                                         decl.LocalName = decl.Name;
1140                                 } else {
1141                                         decl.Prefix = decl.Name.Substring (0, indexOfColon);
1142                                         decl.LocalName = decl.Name.Substring (indexOfColon + 1);
1143                                 }
1144                         } else {
1145                         */
1146                                 decl.Prefix = String.Empty;
1147                                 decl.LocalName = decl.Name;
1148 //                      }
1149
1150                         SkipWhitespace ();
1151                         if(PeekChar () == 'P') {
1152                                 decl.PublicId = ReadPubidLiteral ();
1153                                 bool wsSkipped = SkipWhitespace ();
1154                                 if (PeekChar () == '\'' || PeekChar () == '"') {
1155                                         if (!wsSkipped)
1156                                                 throw NotWFError ("Whitespace is required between public id and system id.");
1157                                         decl.SystemId = ReadSystemLiteral (false);
1158                                         SkipWhitespace ();
1159                                 }
1160                         } else if(PeekChar () == 'S') {
1161                                 decl.SystemId = ReadSystemLiteral (true);
1162                                 SkipWhitespace ();
1163                         }
1164                         if(decl.PublicId == null && decl.SystemId == null)
1165                                 throw NotWFError ("public or system declaration required for \"NOTATION\" declaration.");
1166                         // This expanding is only allowed as a non-validating parser.
1167                         TryExpandPERef ();
1168                         Expect ('>');
1169                         return decl;
1170                 }
1171
1172                 private void ReadExternalID () {
1173                         switch (PeekChar ()) {
1174                         case 'S':
1175                                 cachedSystemId = ReadSystemLiteral (true);
1176                                 break;
1177                         case 'P':
1178                                 cachedPublicId = ReadPubidLiteral ();
1179                                 if (!SkipWhitespace ())
1180                                         throw NotWFError ("Whitespace is required between PUBLIC id and SYSTEM id.");
1181                                 cachedSystemId = ReadSystemLiteral (false);
1182                                 break;
1183                         }
1184                 }
1185
1186                 // The reader is positioned on the first 'S' of "SYSTEM".
1187                 private string ReadSystemLiteral (bool expectSYSTEM)
1188                 {
1189                         if(expectSYSTEM) {
1190                                 Expect ("SYSTEM");
1191                                 if (!SkipWhitespace ())
1192                                         throw NotWFError ("Whitespace is required after 'SYSTEM'.");
1193                         }
1194                         else
1195                                 SkipWhitespace ();
1196                         int quoteChar = ReadChar ();    // apos or quot
1197                         int c = 0;
1198                         ClearValueBuffer ();
1199                         while (c != quoteChar) {
1200                                 c = ReadChar ();
1201                                 if (c < 0)
1202                                         throw NotWFError ("Unexpected end of stream in ExternalID.");
1203                                 if (c != quoteChar)
1204                                         AppendValueChar (c);
1205                         }
1206                         return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
1207                 }
1208
1209                 private string ReadPubidLiteral()
1210                 {
1211                         Expect ("PUBLIC");
1212                         if (!SkipWhitespace ())
1213                                 throw NotWFError ("Whitespace is required after 'PUBLIC'.");
1214                         int quoteChar = ReadChar ();
1215                         int c = 0;
1216                         ClearValueBuffer ();
1217                         while(c != quoteChar)
1218                         {
1219                                 c = ReadChar ();
1220                                 if(c < 0) throw NotWFError ("Unexpected end of stream in ExternalID.");
1221                                 if(c != quoteChar && !XmlChar.IsPubidChar (c))
1222                                         throw NotWFError (String.Format ("character '{0}' not allowed for PUBLIC ID", (char) c));
1223                                 if (c != quoteChar)
1224                                         AppendValueChar (c);
1225                         }
1226                         return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
1227                 }
1228
1229                 // The reader is positioned on the first character
1230                 // of the name.
1231                 internal string ReadName ()
1232                 {
1233                         return ReadNameOrNmToken(false);
1234                 }
1235
1236                 // The reader is positioned on the first character
1237                 // of the name.
1238                 private string ReadNmToken ()
1239                 {
1240                         return ReadNameOrNmToken(true);
1241                 }
1242
1243                 private string ReadNameOrNmToken(bool isNameToken)
1244                 {
1245                         int ch = PeekChar ();
1246                         if(isNameToken) {
1247                                 if (!XmlChar.IsNameChar (ch))
1248                                         throw NotWFError (String.Format ("a nmtoken did not start with a legal character {0} ({1})", ch, (char) ch));
1249                         }
1250                         else {
1251                                 if (!XmlChar.IsFirstNameChar (ch))
1252                                         throw NotWFError (String.Format ("a name did not start with a legal character {0} ({1})", ch, (char) ch));
1253                         }
1254
1255                         nameLength = 0;
1256
1257                         AppendNameChar (ReadChar ());
1258
1259                         while (XmlChar.IsNameChar (PeekChar ())) {
1260                                 AppendNameChar (ReadChar ());
1261                         }
1262
1263                         return CreateNameString ();
1264                 }
1265
1266                 // Read the next character and compare it against the
1267                 // specified character.
1268                 private void Expect (int expected)
1269                 {
1270                         int ch = ReadChar ();
1271
1272                         if (ch != expected) {
1273                                 throw NotWFError (String.Format (CultureInfo.InvariantCulture, 
1274                                                 "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
1275                                                 (char) expected,
1276                                                 expected,
1277                                                 (char) ch,
1278                                                 ch));
1279                         }
1280                 }
1281
1282                 private void Expect (string expected)
1283                 {
1284                         int len = expected.Length;
1285                         for (int i=0; i< len; i++)
1286                                 Expect (expected [i]);
1287                 }
1288
1289                 private void ExpectAfterWhitespace (char c)
1290                 {
1291                         while (true) {
1292                                 int i = ReadChar ();
1293                                 if (XmlChar.IsWhitespace (i))
1294                                         continue;
1295                                 if (c != i)
1296                                         throw NotWFError (String.Format (CultureInfo.InvariantCulture, "Expected {0} but found {1} [{2}].", c, (char) i, i));
1297                                 break;
1298                         }
1299                 }
1300
1301                 // Does not consume the first non-whitespace character.
1302                 private bool SkipWhitespace ()
1303                 {
1304                         bool skipped = XmlChar.IsWhitespace (PeekChar ());
1305                         while (XmlChar.IsWhitespace (PeekChar ()))
1306                                 ReadChar ();
1307                         return skipped;
1308                 }
1309
1310                 private int PeekChar ()
1311                 {
1312                         return currentInput.PeekChar ();
1313                 }
1314
1315                 private int ReadChar ()
1316                 {
1317                         return currentInput.ReadChar ();
1318                 }
1319
1320                 private string ExpandSurrogateChar (int ch)
1321                 {
1322                         if (ch < Char.MaxValue)
1323                                 return ((char) ch).ToString ();
1324                         else {
1325                                 char [] tmp = new char [] {(char) (ch / 0x10000 + 0xD800 - 1), (char) (ch % 0x10000 + 0xDC00)};
1326                                 return new string (tmp);
1327                         }
1328                 }
1329
1330                 // The reader is positioned on the first character after
1331                 // the leading '<!--'.
1332                 private void ReadComment ()
1333                 {
1334                         currentInput.InitialState = 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.InitialState = 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.InitialState)
1387                                 throw NotWFError ("Text declaration cannot appear in this state.");
1388
1389                         currentInput.InitialState = 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                         if (ch < Char.MaxValue)
1528                                 valueBuffer.Append ((char) ch);
1529                         else
1530                                 valueBuffer.Append (ExpandSurrogateChar (ch));
1531                 }
1532
1533                 private string CreateValueString ()
1534                 {
1535                         return valueBuffer.ToString ();
1536                 }
1537                 
1538                 private void ClearValueBuffer ()
1539                 {
1540                         valueBuffer.Length = 0;
1541                 }
1542
1543                 // The reader is positioned on the quote character.
1544                 // *Keeps quote char* to value to get_QuoteChar() correctly.
1545                 private string ReadDefaultAttribute ()
1546                 {
1547                         ClearValueBuffer ();
1548
1549                         TryExpandPERef ();
1550
1551                         int quoteChar = ReadChar ();
1552
1553                         if (quoteChar != '\'' && quoteChar != '\"')
1554                                 throw NotWFError ("an attribute value was not quoted");
1555
1556                         AppendValueChar (quoteChar);
1557
1558                         while (PeekChar () != quoteChar) {
1559                                 int ch = ReadChar ();
1560
1561                                 switch (ch)
1562                                 {
1563                                 case '<':
1564                                         throw NotWFError ("attribute values cannot contain '<'");
1565                                 case -1:
1566                                         throw NotWFError ("unexpected end of file in an attribute value");
1567                                 case '&':
1568                                         AppendValueChar (ch);
1569                                         if (PeekChar () == '#')
1570                                                 break;
1571                                         // Check XML 1.0 section 3.1 WFC.
1572                                         string entName = ReadName ();
1573                                         Expect (';');
1574                                         if (XmlChar.GetPredefinedEntity (entName) < 0) {
1575                                                 DTDEntityDeclaration entDecl = 
1576                                                         DTD == null ? null : DTD.EntityDecls [entName];
1577                                                 if (entDecl == null || entDecl.SystemId != null)
1578                                                         // WFC: Entity Declared (see 4.1)
1579                                                         if (DTD.IsStandalone || (DTD.SystemId == null && !DTD.InternalSubsetHasPEReference))
1580                                                                 throw NotWFError ("Reference to external entities is not allowed in attribute value.");
1581                                         }
1582                                         valueBuffer.Append (entName);
1583                                         AppendValueChar (';');
1584                                         break;
1585                                 default:
1586                                         AppendValueChar (ch);
1587                                         break;
1588                                 }
1589                         }
1590
1591                         ReadChar (); // quoteChar
1592                         AppendValueChar (quoteChar);
1593
1594                         return CreateValueString ();
1595                 }
1596
1597                 private void PushParserInput (string url)
1598                 {
1599                         Uri baseUri = null;
1600                         try {
1601                                 if (DTD.BaseURI != null && DTD.BaseURI.Length > 0)
1602                                         baseUri = new Uri (DTD.BaseURI);
1603                         } catch (UriFormatException) {
1604                         }
1605
1606                         Uri absUri = DTD.Resolver.ResolveUri (baseUri, url);
1607                         string absPath = absUri != null ? absUri.ToString () : String.Empty;
1608
1609                         foreach (XmlParserInput i in parserInputStack.ToArray ()) {
1610                                 if (i.BaseURI == absPath)
1611                                         throw NotWFError ("Nested inclusion is not allowed: " + url);
1612                         }
1613                         parserInputStack.Push (currentInput);
1614                         try {
1615                                 Stream s = DTD.Resolver.GetEntity (absUri, null, typeof (Stream)) as Stream;
1616                                 currentInput = new XmlParserInput (new XmlStreamReader (s), absPath);
1617                         } catch (Exception ex) { // FIXME: (wishlist) Bad exception catch ;-(
1618                                 int line = currentInput == null ? 0 : currentInput.LineNumber;
1619                                 int col = currentInput == null ? 0 : currentInput.LinePosition;
1620                                 string bu = (currentInput == null) ? String.Empty : currentInput.BaseURI;
1621                                 HandleError (new XmlSchemaException ("Specified external entity not found. Target URL is " + url + " .",
1622                                         line, col, null, bu, ex));
1623                                 currentInput = new XmlParserInput (new StringReader (String.Empty), absPath);
1624                         }
1625                 }
1626
1627                 private void PopParserInput ()
1628                 {
1629                         currentInput = parserInputStack.Pop () as XmlParserInput;
1630                 }
1631
1632                 private void HandleError (XmlSchemaException ex)
1633                 {
1634 #if DTD_HANDLE_EVENTS
1635                         if (this.ValidationEventHandler != null)
1636                                 ValidationEventHandler (this, new ValidationEventArgs (ex, ex.Message, XmlSeverityType.Error));
1637 #else
1638                         DTD.AddError (ex);
1639 #endif
1640                 }
1641         }
1642 }