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