KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > xml > XmlParser


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Scott Ferguson
27  */

28
29 package com.caucho.xml;
30
31 import com.caucho.util.CharBuffer;
32 import com.caucho.vfs.Path;
33 import com.caucho.vfs.ReadStream;
34 import com.caucho.vfs.ReaderWriterStream;
35 import com.caucho.vfs.Vfs;
36 import com.caucho.vfs.WriteStream;
37 import com.caucho.xml.readers.MacroReader;
38 import com.caucho.xml.readers.Utf16Reader;
39 import com.caucho.xml.readers.Utf8Reader;
40 import com.caucho.xml.readers.XmlReader;
41
42 import org.w3c.dom.Document JavaDoc;
43 import org.w3c.dom.Node JavaDoc;
44 import org.xml.sax.InputSource JavaDoc;
45 import org.xml.sax.Locator JavaDoc;
46 import org.xml.sax.SAXException JavaDoc;
47 import org.xml.sax.SAXParseException JavaDoc;
48
49 import java.io.FileNotFoundException JavaDoc;
50 import java.io.IOException JavaDoc;
51 import java.io.InputStream JavaDoc;
52 import java.util.ArrayList JavaDoc;
53 import java.util.Arrays JavaDoc;
54 import java.util.logging.Level JavaDoc;
55
56 /**
57  * A configurable XML parser. Loose versions of XML and HTML are supported
58  * by changing the Policy object.
59  *
60  * <p>Normally, applications will use Xml, LooseXml, Html, or LooseHtml.
61  */

62 public class XmlParser extends AbstractParser {
63   // Xerces uses the following
64
public static final String JavaDoc XMLNS = "http://www.w3.org/2000/xmlns/";
65   public static final String JavaDoc XML = "http://www.w3.org/XML/1998/namespace";
66
67   static final QName DOC_NAME = new QName(null, "#document", null);
68   static final QName TEXT_NAME = new QName(null, "#text", null);
69   static final QName JSP_NAME = new QName(null, "#jsp", null);
70   static final QName WHITESPACE_NAME = new QName(null, "#whitespace", null);
71   static final QName JSP_ATTRIBUTE_NAME = new QName("xtp", "jsp-attribute", null);
72   
73   QAttributes _attributes;
74   QAttributes _nullAttributes;
75
76   boolean _inDtd;
77   
78   CharBuffer _text;
79   CharBuffer _eltName;
80   CharBuffer _cb;
81   CharBuffer _buf = new CharBuffer();
82   String JavaDoc _textFilename;
83   int _textLine;
84
85   char []_textBuffer = new char[1024];
86   int _textLength;
87   int _textCapacity = _textBuffer.length;
88   boolean _isIgnorableWhitespace;
89   boolean _isJspText;
90   
91   CharBuffer _name = new CharBuffer();
92   CharBuffer _nameBuffer = new CharBuffer();
93   
94   MacroReader _macro = new MacroReader();
95   int _macroIndex = 0;
96   int _macroLength = 0;
97   char []_macroBuffer;
98
99   QName []_elementNames = new QName[64];
100   NamespaceMap []_namespaces = new NamespaceMap[64];
101   int []_elementLines = new int[64];
102   int _elementTop;
103
104   NamespaceMap _namespaceMap;
105
106   ArrayList JavaDoc<String JavaDoc> _attrNames = new ArrayList JavaDoc<String JavaDoc>();
107   ArrayList JavaDoc<String JavaDoc> _attrValues = new ArrayList JavaDoc<String JavaDoc>();
108
109   ReadStream _is;
110   XmlReader _reader;
111   
112   String JavaDoc _extPublicId;
113   String JavaDoc _extSystemId;
114   
115   QName _activeNode;
116   QName _topNamespaceNode;
117   boolean _isTagStart;
118   boolean _stopOnIncludeEnd;
119   boolean _hasTopElement;
120   boolean _hasDoctype;
121   boolean _isHtml;
122   Locator JavaDoc _locator = new LocatorImpl(this);
123
124   public XmlParser()
125   {
126     clear();
127   }
128
129   /**
130    * Creates a new parser with a given parsing policy and dtd.
131    *
132    * @param policy the parsing policy, handling optional tags.
133    * @param dtd the parser's dtd.
134    */

135   XmlParser(Policy policy, QDocumentType dtd)
136   {
137     super(policy, dtd);
138     
139     clear();
140   }
141
142   /**
143    * Initialize the parser.
144    */

145   void init()
146   {
147     super.init();
148     
149     _attributes = new QAttributes();
150     _nullAttributes = new QAttributes();
151     _eltName = new CharBuffer();
152     _text = new CharBuffer();
153
154     _isHtml = _policy instanceof HtmlPolicy;
155
156     // jsp/193b
157
// _namespaceMap = null;
158

159     _textLength = 0;
160     _isIgnorableWhitespace = true;
161     _elementTop = 0;
162     _elementLines[0] = 1;
163
164     _line = 1;
165
166     _dtd = null;
167     _inDtd = false;
168     _isTagStart = false;
169     _stopOnIncludeEnd = false;
170
171     _extPublicId = null;
172     _extSystemId = null;
173
174     _filename = null;
175     _publicId = null;
176     _systemId = null;
177     
178     _hasTopElement = false;
179     _hasDoctype = false;
180
181     _macroIndex = 0;
182     _macroLength = 0;
183
184     _reader = null;
185
186     // _owner = null;
187

188     _policy.init();
189   }
190
191   /**
192    * Parse the document from a read stream.
193    *
194    * @param is read stream to parse from.
195    *
196    * @return The parsed document.
197    */

198   Document JavaDoc parseInt(ReadStream is)
199     throws IOException JavaDoc, SAXException JavaDoc
200   {
201     _is = is;
202
203     if (_filename == null && _systemId != null)
204       _filename = _systemId;
205     else if (_filename == null)
206       _filename = _is.getUserPath();
207
208     if (_systemId == null) {
209       _systemId = _is.getPath().getURL();
210       if ("null:".equals(_systemId) || "string:".equals(_systemId))
211     _systemId = "stream";
212     }
213
214     /* xsl/0401
215     if (_isNamespaceAware)
216       _namespaceMap = new NamespaceMap(null, "", "");
217     */

218     _policy.setNamespaceAware(_isNamespaceAware);
219     
220     if (_filename == null)
221       _filename = _systemId;
222
223     if (_filename == null)
224       _filename = "stream";
225
226     if (_dtd != null)
227       _dtd.setSystemId(_systemId);
228     
229     if (_builder != null) {
230       if (! "string:".equals(_systemId) && ! "stream".equals(_systemId))
231     _builder.setSystemId(_systemId);
232       _builder.setFilename(_is.getPath().getURL());
233     }
234
235     if (_contentHandler == null)
236       _contentHandler = new org.xml.sax.helpers.DefaultHandler JavaDoc();
237
238     _contentHandler.setDocumentLocator(_locator);
239
240     if (_owner == null)
241       _owner = new QDocument();
242     if (_defaultEncoding != null)
243       _owner.setAttribute("encoding", _defaultEncoding);
244     _owner.addDepend(is.getPath());
245     
246     _activeNode = DOC_NAME;
247     
248     _policy.setStream(is);
249     _policy.setNamespace(_namespaceMap);
250
251     _contentHandler.startDocument();
252     
253     int ch = parseXMLDeclaration(null);
254     
255     ch = skipWhitespace(ch);
256     parseNode(ch, false);
257
258     /*
259     if (dbg.canWrite()) {
260       printDebugNode(dbg, doc, 0);
261       dbg.flush();
262     }
263     */

264
265     if (_strictXml && ! _hasTopElement)
266       throw error(L.l("XML file has no top-element. All well-formed XML files have a single top-level element."));
267
268     if (_contentHandler != null)
269       _contentHandler.endDocument();
270
271     QDocument owner = _owner;
272     _owner = null;
273       
274     return owner;
275   }
276
277   /**
278    * The main dispatch loop.
279    *
280    * @param node the current node
281    * @param ch the next character
282    * @param special true for the short form, &lt;foo/bar/>
283    */

284   private void parseNode(int ch, boolean special)
285     throws IOException JavaDoc, SAXException JavaDoc
286   {
287     //boolean isTop = node instanceof QDocument;
288

289     _text.clear();
290
291   loop:
292     while (true) {
293       if (_textLength == 0) {
294         _textFilename = getFilename();
295         _textLine = getLine();
296       }
297
298       switch (ch) {
299       case -1:
300     if (_textLength != 0)
301       appendText();
302         if (! _stopOnIncludeEnd && _reader.getNext() != null) {
303           popInclude();
304           if (_reader != null)
305             parseNode(_reader.read(), special);
306           return;
307         }
308     closeTag("");
309     return;
310
311       case ' ': case '\t': case '\n': case '\r':
312     if (! _normalizeWhitespace)
313       addText((char) ch);
314     else if (_textLength == 0) {
315       if (! _isTagStart)
316         addText(' ');
317     }
318     else if (_textBuffer[_textLength - 1] != ' ') {
319       addText(' ');
320     }
321     ch = _reader.read();
322     break;
323
324       case 0xffff:
325     // marker for end of text for serialization
326
return;
327
328       default:
329     addText((char) ch);
330     ch = _reader.read();
331     break;
332
333       case '/':
334     if (! special) {
335       addText((char) ch);
336       ch = _reader.read();
337       continue;
338     }
339     ch = _reader.read();
340     if (ch == '>' || ch == -1) {
341           appendText();
342           popNode();
343       return;
344     }
345     addText('/');
346     break;
347
348       case '&':
349         ch = parseEntityReference();
350     break;
351
352       case '<':
353     boolean endTag = false;
354     ch = _reader.read();
355
356     if (ch == '/' && ! special) {
357       if (_normalizeWhitespace &&
358           _textLength > 0 && _textBuffer[_textLength - 1] == ' ') {
359         _textLength--;
360       }
361       appendText();
362
363       ch = _reader.parseName(_name, _reader.read());
364
365           if (ch != '>') {
366             // XXX: Hack for Java PetStore
367
while (XmlChar.isWhitespace(ch))
368               ch = _reader.read();
369
370             if (ch != '>')
371               throw error(L.l("`</{0}>' expected `>' at {1}. Closing tags must close immediately after the tag name.", _name, badChar(ch)));
372           }
373
374       closeTag(_policy.getName(_name).getName());
375       ch = _reader.read();
376     }
377     // element: <tag attr=value ... attr=value> ...
378
else if (XmlChar.isNameStart(ch)) {
379       appendText();
380       
381       parseElement(ch);
382       ch = _reader.read();
383     }
384     // <! ...
385
else if (ch == '!') {
386       // <![CDATA[ ... ]]>
387
if ((ch = _reader.read()) == '[') {
388         parseCdata();
389         ch = _reader.read();
390       }
391       // <!-- ... -->
392
else if (ch == '-') {
393         parseComment();
394
395         ch = _reader.read();
396       }
397       else if (XmlChar.isNameStart(ch)) {
398         appendText();
399         ch = _reader.parseName(_name, ch);
400         String JavaDoc declName = _name.toString();
401         if (declName.equals("DOCTYPE")) {
402           parseDoctype(ch);
403               if (_contentHandler instanceof DOMBuilder)
404                 ((DOMBuilder) _contentHandler).dtd(_dtd);
405
406           ch = _reader.read();
407         } else if (_forgiving && declName.equalsIgnoreCase("doctype")) {
408           parseDoctype(ch);
409               if (_contentHandler instanceof DOMBuilder)
410                 ((DOMBuilder) _contentHandler).dtd(_dtd);
411               
412           ch = _reader.read();
413         } else
414           throw error(L.l("expected `<!DOCTYPE' declaration at {0}", declName));
415       } else if (_forgiving) {
416         addText("<!");
417       } else
418         throw error(L.l("expected `<!DOCTYPE' declaration at {0}", badChar(ch)));
419     }
420     // PI: <?tag attr=value ... attr=value?>
421
else if (ch == '?') {
422       ch = parsePI();
423     }
424     else if (_strictXml) {
425       throw error(L.l("expected tag name after `<' at {0}. Open tag names must immediately follow the open brace like `<foo ...>'", badChar(ch)));
426         }
427         // implicit <![CDATA[ for <% ... %>
428
else if (_isJsp && ch == '%') {
429           ch = _reader.read();
430
431           appendText();
432           _isJspText = ch != '=';
433           
434           addText("<%");
435
436           while (ch >= 0) {
437             if (ch == '%') {
438               ch = _reader.read();
439               if (ch == '>') {
440                 addText("%>");
441                 ch = _reader.read();
442                 break;
443               }
444               else
445                 addText('%');
446             }
447             else {
448               addText((char) ch);
449               ch = _reader.read();
450             }
451           }
452
453           appendText();
454           _isJspText = false;
455     }
456     else {
457       addText('<');
458     }
459       }
460     }
461   }
462
463   /**
464    * Parses the &lt;!DOCTYPE> declaration.
465    */

466   private void parseDoctype(int ch)
467     throws IOException JavaDoc, SAXException JavaDoc
468   {
469     if (_activeNode != DOC_NAME)
470       throw error(L.l("<!DOCTYPE immediately follow the <?xml ...?> declaration."));
471     
472     _inDtd = true;
473
474     ch = skipWhitespace(ch);
475     ch = _reader.parseName(_nameBuffer, ch);
476     String JavaDoc name = _nameBuffer.toString();
477     ch = skipWhitespace(ch);
478
479     if (_dtd == null)
480       _dtd = new QDocumentType(name);
481
482     _dtd.setName(name);
483
484     if (XmlChar.isNameStart(ch)) {
485       ch = parseExternalID(ch);
486       ch = skipWhitespace(ch);
487
488       _dtd._publicId = _extPublicId;
489       _dtd._systemId = _extSystemId;
490     }
491
492     if (_dtd._systemId != null && ! _dtd._systemId.equals("")) {
493       InputStream JavaDoc is = null;
494
495       unread(ch);
496       
497       XmlReader oldReader = _reader;
498       boolean hasInclude = false;
499
500       try {
501         pushInclude(_extPublicId, _extSystemId);
502         hasInclude = true;
503       } catch (Exception JavaDoc e) {
504     if (log.isLoggable(Level.FINEST))
505       log.log(Level.FINER, e.toString(), e);
506     else
507       log.finer(e.toString());
508       }
509
510       if (hasInclude) {
511         _stopOnIncludeEnd = true;
512     try {
513       ch = parseDoctypeDecl(_dtd);
514     } catch (XmlParseException e) {
515       if (_extSystemId != null &&
516           _extSystemId.startsWith("http")) {
517         log.log(Level.FINE, e.toString(), e);
518       }
519       else
520         throw e;
521     }
522         _stopOnIncludeEnd = false;
523
524         while (_reader != null && _reader != oldReader)
525           popInclude();
526       }
527
528       if (_reader != null)
529         ch = skipWhitespace(read());
530     }
531     
532     if (ch == '[')
533       ch = parseDoctypeDecl(_dtd);
534
535     ch = skipWhitespace(ch);
536
537     _inDtd = false;
538
539     if (ch != '>')
540       throw error(L.l("expected `>' in <!DOCTYPE at {0}",
541                       badChar(ch)));
542   }
543
544   /**
545    * Parses the DTD.
546    *
547    * <pre>
548    * dtd-item ::= &lt!ELEMENT ... |
549    * &lt!ATTLIST ... |
550    * &lt!NOTATION ... |
551    * &lt!ENTITY ... |
552    * &lt!-- comment |
553    * &lt? pi |
554    * %pe-ref;
555    * </pre>
556    *
557    * @return the next character.
558    */

559   private int parseDoctypeDecl(QDocumentType doctype)
560     throws IOException JavaDoc, SAXException JavaDoc
561   {
562     _hasDoctype = true;
563     int ch = 0;
564
565     for (ch = skipWhitespace(read());
566      ch >= 0 && ch != ']';
567      ch = skipWhitespace(read())) {
568       if (ch == '<') {
569     if ((ch = read()) == '!') {
570       if (XmlChar.isNameStart(ch = read())) {
571         ch = _reader.parseName(_text, ch);
572         String JavaDoc name = _text.toString();
573
574         if (name.equals("ELEMENT"))
575           parseElementDecl(doctype);
576         else if (name.equals("ATTLIST"))
577           parseAttlistDecl(doctype);
578         else if (name.equals("NOTATION"))
579           parseNotationDecl(doctype);
580         else if (name.equals("ENTITY"))
581           parseEntityDecl(doctype);
582         else
583           throw error("unknown declaration `" + name + "'");
584       }
585       else if (ch == '-')
586         parseComment();
587       else if (ch == '[') {
588         ch = _reader.parseName(_text, read());
589         String JavaDoc name = _text.toString();
590
591         if (name.equals("IGNORE")) {
592           parseIgnore();
593         }
594         else if (name.equals("INCLUDE")) {
595           parseIgnore();
596         }
597         else
598           throw error("unknown declaration `" + name + "'");
599       }
600     }
601     else if (ch == '?') {
602       parsePI();
603     }
604     else
605       throw error(L.l("expected markup at {0}", badChar(ch)));
606       }
607       else if (ch == '%') {
608     ch = _reader.parseName(_buf, read());
609
610     if (ch != ';')
611       throw error(L.l("`%{0};' expects `;' at {1}. Parameter entities have a `%name;' syntax.", _buf, badChar(ch)));
612
613     addPEReference(_text, _buf.toString());
614       }
615       else {
616     throw error(L.l("expected '<' at {0}", badChar(ch)));
617       }
618
619       _text.clear();
620     }
621     _text.clear();
622
623     return read();
624   }
625
626   /**
627    * Parses an element.
628    *
629    * @param ch the current character
630    */

631   private void parseElement(int ch)
632     throws IOException JavaDoc, SAXException JavaDoc
633   {
634     ch = _reader.parseName(_eltName, ch);
635
636     NamespaceMap oldNamespace = _namespaceMap;
637     
638     if (ch != '>' && ch != '/')
639       ch = parseAttributes(ch, true);
640     else
641       _attributes.clear();
642
643     QName qname = _policy.getName(_eltName);
644
645     if (_isValidating && _dtd != null) {
646       QElementDef elementDef = _dtd.getElement(qname.getName());
647       
648       if (elementDef != null)
649         elementDef.fillDefaults(_attributes);
650     }
651
652     if (ch == '/') {
653       // empty tag: <foo/>
654
if ((ch = _reader.read()) == '>') {
655     addElement(qname, true, _attributes, oldNamespace);
656       }
657       // short tag: </foo/some text here/>
658
else {
659     addElement(qname, false, _attributes, oldNamespace);
660     parseNode(ch, true);
661       }
662     } else if (ch == '>') {
663       addElement(qname, false, _attributes, oldNamespace);
664     } else
665       throw error(L.l("unexpected character {0} while parsing `{1}' attributes. Expected an attribute name or `>' or `/>'. XML element syntax is:\n <name attr-1=\"value-1\" ... attr-n=\"value-n\">",
666                       badChar(ch), qname.getName()));
667   }
668
669   /**
670    * Parses the attributes in an element.
671    *
672    * @param ch the next character to reader.read.
673    *
674    * @return the next character to read.
675    */

676   private int parseAttributes(int ch, boolean isElement)
677     throws IOException JavaDoc, SAXException JavaDoc
678   {
679     ch = skipWhitespace(ch);
680     _attributes.clear();
681
682     _attrNames.clear();
683     _attrValues.clear();
684
685     boolean hasWhitespace = true;
686
687     while (ch != -1) {
688       if (! XmlChar.isNameStart(ch)) {
689         if (! _isJsp || ch != '<')
690           break;
691
692         ch = parseJspAttribute(isElement);
693         continue;
694       }
695
696       if (! hasWhitespace)
697     throw error(L.l("attributes must be separated by whitespace"));
698
699       hasWhitespace = false;
700       
701       ch = _reader.parseName(_text, ch);
702
703       if (! _text.startsWith("xmlns")) {
704       }
705       else {
706         QName name;
707
708     if (_isNamespaceAware && _contentHandler instanceof DOMBuilder)
709       name = _policy.getNamespaceName(_text);
710     else
711       name = new QName(_text.toString(), null);
712
713     String JavaDoc prefix;
714
715     if (_text.length() > 5) {
716       prefix = _text.substring(6);
717
718       if (prefix.equals(""))
719         throw error(L.l("'{0}' is an illegal namespace declaration.",
720                 _text));
721     }
722     else {
723       prefix = "";
724     }
725     
726     _text.clear();
727     ch = skipWhitespace(ch);
728     if (ch != '=')
729       throw error(L.l("xmlns: needs value at {0}", badChar(ch)));
730     ch = skipWhitespace(_reader.read());
731     ch = parseValue(_text, ch, true);
732     
733     hasWhitespace = isWhitespace(ch);
734     
735     ch = skipWhitespace(ch);
736
737     // topNamespaceNode = element;
738
String JavaDoc uri = _text.toString();
739
740         if (_isXmlnsPrefix) {
741           _namespaceMap = new NamespaceMap(_namespaceMap, prefix, uri);
742           _policy.setNamespace(_namespaceMap);
743
744           _contentHandler.startPrefixMapping(prefix, uri);
745     }
746
747     // needed for xml/032e
748
if (isElement && _isXmlnsAttribute) {
749           _attributes.add(name, uri);
750     }
751
752     continue;
753       }
754
755       String JavaDoc attrName = _text.toString();
756       _attrNames.add(attrName);
757
758       _text.clear();
759       ch = skipWhitespace(ch);
760
761       String JavaDoc value = null;
762
763       if (ch == '=') {
764     ch = skipWhitespace(_reader.read());
765     ch = parseValue(_text, ch, true);
766
767     hasWhitespace = isWhitespace(ch);
768     
769     ch = skipWhitespace(ch);
770
771     value = _text.toString();
772       }
773       else if (_strictAttributes) {
774     throw error(L.l("attribute `{0}' expects value at {1}. XML requires attributes to have explicit values.",
775                         attrName, badChar(ch)));
776       }
777       else {
778     value = attrName; // xxx: conflict xsl/0432
779
hasWhitespace = true;
780       }
781
782       _attrValues.add(value);
783     }
784
785     int len = _attrNames.size();
786     for (int i = 0; i < len; i++) {
787       String JavaDoc attrName = _attrNames.get(i);
788       String JavaDoc value = _attrValues.get(i);
789
790       _text.clear();
791       _text.append(attrName);
792       QName name;
793
794       if (_contentHandler instanceof DOMBuilder)
795     name = _policy.getAttributeName(_eltName, _text, true);
796       else
797     name = _policy.getAttributeName(_eltName, _text);
798
799       _attributes.add(name, value);
800     }
801     
802     return ch;
803   }
804
805   /**
806    * Special parser to handle the use of &lt;%= as an attribute in JSP
807    * files. Covers cases like the following:
808    *
809    * <pre>
810    * &lt;options>
811    * &lt;option name="foo" &lt;%= test.isSelected("foo") %>/>
812    * &lt;/options>
813    * </pre>
814    *
815    * @param element the parent element
816    *
817    * @return the next character to read.
818    */

819   private int parseJspAttribute(boolean isElement)
820     throws IOException JavaDoc, XmlParseException
821   {
822     int ch = _reader.read();
823
824     if (ch != '%')
825       throw error(L.l("unexpected char `{0}' in element", "%"));
826
827     ch = _reader.read();
828     if (ch != '=')
829       throw error(L.l("unexpected char `{0}' in element", "="));
830
831     _text.clear();
832     ch = _reader.read();
833     while (ch >= 0) {
834       if (ch == '%') {
835         ch = _reader.read();
836         if (ch == '>') {
837           ch = _reader.read();
838           break;
839         }
840         _text.append((char) ch);
841       }
842       else {
843         _text.append((char) ch);
844         ch = _reader.read();
845       }
846     }
847
848     String JavaDoc value = _text.toString();
849
850     if (isElement)
851       _attributes.add(JSP_ATTRIBUTE_NAME, value);
852
853     return ch;
854   }
855
856   /**
857    * Handle processing at a close tag. For strict XML, this will normally
858    * just change the current node to its parent, but HTML has a more
859    * complicated policy.
860    */

861   private void closeTag(String JavaDoc endTagName)
862     throws IOException JavaDoc, SAXException JavaDoc
863   {
864     while (_activeNode != null && _activeNode != DOC_NAME) {
865       switch (_policy.elementCloseAction(this, _activeNode, endTagName)) {
866       case Policy.POP:
867     //if (dbg.canWrite())
868
// dbg.println("</" + activeNode.getNodeName() + ">");
869

870         popNode();
871     return;
872
873       case Policy.POP_AND_LOOP:
874     //if (dbg.canWrite())
875
// dbg.println("</" + activeNode.getNodeName() + ">");
876

877         popNode();
878     break;
879     
880       case Policy.IGNORE:
881     return;
882
883       default:
884     throw new RuntimeException JavaDoc();
885       }
886     }
887
888     if (! _extraForgiving && endTagName != null && ! endTagName.equals(""))
889       throw error(L.l("Unexpected end tag `</{0}>' at top-level. All open tags have already been closed.",
890                     endTagName));
891   }
892
893   /**
894    * Handles processing of the resin:include tag.
895    */

896   private void handleResinInclude()
897     throws IOException JavaDoc, SAXException JavaDoc
898   {
899     String JavaDoc filename = _attributes.getValue("path");
900     
901     if (filename == null || filename.equals(""))
902       filename = _attributes.getValue("href");
903
904     if (filename.equals(""))
905       throw error(L.l("<resin:include> expects a `path' attribute."));
906
907     pushInclude(filename);
908   }
909
910   /**
911    * Handles processing of the resin:include tag.
912    */

913   private void handleResinIncludeDirectory()
914     throws IOException JavaDoc, SAXException JavaDoc
915   {
916     String JavaDoc filename = _attributes.getValue("path");
917
918     if (filename == null || filename.equals(""))
919       filename = _attributes.getValue("href");
920     
921     String JavaDoc extension = _attributes.getValue("extension");
922
923     if (filename.equals(""))
924       throw error(L.l("<resin:include> expects a `path' attribute."));
925
926     Path pwd;
927     if (_searchPath != null)
928       pwd = _searchPath;
929     else
930       pwd = Vfs.lookup(_systemId).getParent();
931
932     Path dir = pwd.lookup(filename);
933     if (! dir.isDirectory())
934       throw error(L.l("`{0}' is not a directory for resin:include-directory. The href for resin:include-directory must refer to a directory.",
935                       dir.getNativePath()));
936
937     String JavaDoc []list = dir.list();
938     Arrays.sort(list);
939     for (int i = list.length - 1; i >= 0; i--) {
940       if (list[i].startsWith(".") ||
941           extension != null && ! list[i].endsWith(extension))
942         continue;
943
944       pushInclude(dir.lookup(list[i]).getPath());
945     }
946   }
947
948   private int parseNameToken(CharBuffer name, int ch)
949     throws IOException JavaDoc, SAXException JavaDoc
950   {
951     name.clear();
952
953     if (! XmlChar.isNameChar(ch))
954       throw error(L.l("expected name at {0}", badChar(ch)));
955
956     for (; XmlChar.isNameChar(ch); ch = _reader.read())
957       name.append((char) ch);
958
959     return ch;
960   }
961
962   /**
963    * Pop the top-level node
964    */

965   private void popNode()
966     throws SAXException JavaDoc
967   {
968     QName node = _activeNode;
969
970     if (_activeNode != DOC_NAME) {
971       String JavaDoc uri = _activeNode.getNamespaceURI();
972       String JavaDoc localName = _activeNode.getLocalName();
973       
974       if (uri == null) {
975     uri = "";
976
977     if (_isNamespaceAware)
978       localName = _activeNode.getName();
979     else
980       localName = "";
981       }
982
983       _contentHandler.endElement(uri,
984                  localName,
985                  _activeNode.getName());
986     }
987
988     if (_elementTop > 0) {
989       _elementTop--;
990       NamespaceMap oldMap = _namespaces[_elementTop];
991
992       popNamespaces(oldMap);
993       
994       _activeNode = _elementNames[_elementTop];
995     }
996     
997     if (_elementTop == 0)
998       _activeNode = DOC_NAME;
999   }
1000
1001  public void pushNamespace(String JavaDoc prefix, String JavaDoc uri)
1002  {
1003    _namespaceMap = new NamespaceMap(_namespaceMap, prefix, uri);
1004
1005    _policy.setNamespace(_namespaceMap);
1006  }
1007
1008  private void popNamespaces(NamespaceMap oldMap)
1009    throws SAXException JavaDoc
1010  {
1011    for (;
1012         _namespaceMap != null && _namespaceMap != oldMap;
1013         _namespaceMap = _namespaceMap.next) {
1014      _contentHandler.endPrefixMapping(_namespaceMap.prefix);
1015    }
1016    _namespaceMap = oldMap;
1017    _policy.setNamespace(_namespaceMap);
1018  }
1019
1020  private void appendText(String JavaDoc s)
1021  {
1022    if (_text.length() == 0) {
1023      _textFilename = getFilename();
1024      _textLine = getLine();
1025    }
1026
1027    _text.append(s);
1028  }
1029
1030  /**
1031   * Parses an entity reference:
1032   *
1033   * <pre>
1034   * er ::= &#d+;
1035   * ::= &name;
1036   * </pre>
1037   */

1038  private int parseEntityReference()
1039    throws IOException JavaDoc, SAXException JavaDoc
1040  {
1041    int ch;
1042
1043    ch = _reader.read();
1044
1045    // character reference
1046
if (ch == '#') {
1047      addText((char) parseCharacterReference());
1048
1049      return _reader.read();
1050    }
1051    // entity reference
1052
else if (XmlChar.isNameStart(ch)) {
1053      ch = _reader.parseName(_buf, ch);
1054
1055      if (ch != ';' && _strictXml)
1056    throw error(L.l("`&{0};' expected `;' at {0}. Entity references have a `&name;' syntax.", _buf, badChar(ch)));
1057      else if (ch != ';') {
1058    addText('&');
1059    addText(_buf.toString());
1060    return ch;
1061      }
1062
1063      addEntityReference(_buf.toString());
1064
1065      ch = _reader.read();
1066
1067      return ch;
1068    } else if (_strictXml) {
1069      throw error(L.l("expected name at {0}", badChar(ch)));
1070    } else {
1071      addText('&');
1072      return ch;
1073    }
1074  }
1075
1076  private int parseCharacterReference()
1077    throws IOException JavaDoc, SAXException JavaDoc
1078  {
1079    int ch = _reader.read();
1080
1081    int radix = 10;
1082    if (ch == 'x') {
1083      radix = 16;
1084      ch = _reader.read();
1085    }
1086
1087    int value = 0;
1088    for (; ch != ';'; ch = _reader.read()) {
1089      if (ch >= '0' && ch <= '9')
1090    value = radix * value + ch - '0';
1091      else if (radix == 16 && ch >= 'a' && ch <= 'f')
1092    value = radix * value + ch - 'a' + 10;
1093      else if (radix == 16 && ch >= 'A' && ch <= 'F')
1094    value = radix * value + ch - 'A' + 10;
1095      else
1096    throw error(L.l("malformed entity ref at {0}", badChar(ch)));
1097    }
1098
1099    if (value > 0xffff)
1100      throw error(L.l("malformed entity ref at {0}", "" + value));
1101
1102    // xml/0072
1103
if (_strictCharacters && ! isChar(value))
1104      throw error(L.l("illegal character ref at {0}", badChar(value)));
1105
1106    return value;
1107  }
1108
1109  /**
1110   * Looks up a named entity reference, filling the text.
1111   */

1112  private void addEntityReference(String JavaDoc name)
1113    throws IOException JavaDoc, SAXException JavaDoc
1114  {
1115    boolean expand = ! _entitiesAsText || _hasDoctype || ! _switchToXml;
1116    // XXX: not quite the right logic. There should be a soft expandEntities
1117

1118    if (! expand) {
1119      addText("&" + name + ";");
1120      return;
1121    }
1122    
1123    int ch = _entities.getEntity(name);
1124    if (ch >= 0 && ch <= 0xffff) {
1125      addText((char) ch);
1126      return;
1127    }
1128
1129    QEntity entity = _dtd == null ? null : _dtd.getEntity(name);
1130
1131    if (! _expandEntities) {
1132      addText("&" + name + ";");
1133      return;
1134    }
1135
1136    if (entity == null && (_dtd == null || _dtd.getName() == null ||
1137                           ! _dtd.isExternal())) {
1138      if (_strictXml)
1139    throw error(L.l("`&{0};' is an unknown entity. XML predefines only `&lt;', `&amp;', `&gt;', `&apos;' and `&quot;'. All other entities must be defined in an &lt;!ENTITY> definition in the DTD.", name));
1140      else {
1141        if (expand && _contentHandler instanceof DOMBuilder) {
1142          appendText();
1143          ((DOMBuilder) _contentHandler).entityReference(name);
1144        }
1145        else
1146          addText("&" + name + ";");
1147      }
1148    }
1149    else if (entity != null) {
1150      if (expand && entity._isSpecial && entity._value != null)
1151    addText(entity._value);
1152      else if (entity.getSystemId() != null) {
1153        if (pushSystemEntity(entity)) {
1154        }
1155        /* XXX:??
1156        else if (strictXml) {
1157          throw error(L.l("can't open external entity at `&{0};'", name));
1158        }
1159        */

1160        else if (_contentHandler instanceof DOMBuilder) {
1161          appendText();
1162          ((DOMBuilder) _contentHandler).entityReference(name);
1163        }
1164        else
1165          addText("&" + name + ";");
1166      }
1167      else if (expand && entity._value != null)
1168    setMacro(entity._value);
1169      else
1170        addText("&" + name + ";");
1171    }
1172    else {
1173      if (expand && _contentHandler instanceof DOMBuilder) {
1174        appendText();
1175        ((DOMBuilder) _contentHandler).entityReference(name);
1176      }
1177      else // XXX: error?
1178
addText("&" + name + ";");
1179    }
1180  }
1181
1182  private boolean pushSystemEntity(QEntity entity)
1183    throws IOException JavaDoc, SAXException JavaDoc
1184  {
1185    String JavaDoc publicId = entity.getPublicId();
1186    String JavaDoc systemId = entity.getSystemId();
1187    String JavaDoc value = null;
1188    InputSource JavaDoc source = null;
1189    ReadStream is = null;
1190
1191    if (_entityResolver != null)
1192      source = _entityResolver.resolveEntity(publicId, systemId);
1193
1194    if (source != null && source.getByteStream() != null)
1195      is = Vfs.openRead(source.getByteStream());
1196    else if (source != null && source.getCharacterStream() != null)
1197      is = Vfs.openRead(source.getCharacterStream());
1198    else if (source != null && source.getSystemId() != null &&
1199             _searchPath.lookup(source.getSystemId()).isFile()) {
1200      _owner.addDepend(_searchPath.lookup(source.getSystemId()));
1201      is = _searchPath.lookup(source.getSystemId()).openRead();
1202    }
1203    else if (systemId != null && ! systemId.equals("")) {
1204      String JavaDoc path = systemId;
1205      if (path.startsWith("file:"))
1206        path = path.substring(5);
1207      if (_searchPath.lookup(path).isFile()) {
1208        _owner.addDepend(_searchPath.lookup(path));
1209        is = _searchPath.lookup(path).openRead();
1210      }
1211    }
1212
1213    if (is == null)
1214      return false;
1215
1216    _filename = systemId;
1217    _systemId = systemId;
1218
1219    Path oldSearchPath = _searchPath;
1220    Path path = is.getPath();
1221    if (path != null) {
1222      _owner.addDepend(path);
1223      
1224      if (_searchPath != null) {
1225        _searchPath = path.getParent();
1226        _reader.setSearchPath(oldSearchPath);
1227      }
1228    }
1229
1230    _is = is;
1231    _line = 1;
1232    
1233    XmlReader oldReader = _reader;
1234    _reader = null;
1235
1236    int ch = parseXMLDeclaration(oldReader);
1237    unread(ch);
1238
1239    return true;
1240  }
1241
1242  /**
1243   * Parses an attribute value.
1244   *
1245   * <pre>
1246   * value ::= '[^']*'
1247   * ::= "[^"]*"
1248   * ::= [^ />]*
1249   * </pre>
1250   *
1251   * @param value the CharBuffer which will contain the value.
1252   * @param ch the next character from the input stream.
1253   * @param isGeneral true if general entities are allowed.
1254   *
1255   * @return the following character from the input stream
1256   */

1257  private int parseValue(CharBuffer value, int ch, boolean isGeneral)
1258    throws IOException JavaDoc, SAXException JavaDoc
1259  {
1260    int end = ch;
1261
1262    value.clear();
1263
1264    if (end == '\'' || end == '"')
1265      ch = _reader.read();
1266    else if (_strictAttributes) {
1267      value.append((char) end);
1268      for (ch = _reader.read();
1269           ch >= 0 && XmlChar.isNameChar(ch);
1270           ch = _reader.read())
1271        value.append((char) ch);
1272      
1273      throw error(L.l("XML attribute value must be quoted at `{0}'. XML attribute syntax is either attr=\"value\" or attr='value'.",
1274                      value));
1275    }
1276    else
1277      end = 0;
1278
1279    while (ch != -1 && (end != 0 && ch != end ||
1280            end == 0 && isAttributeChar(ch))) {
1281      if (end == 0 && ch == '/') {
1282    ch = _reader.read();
1283    if (! isWhitespace(ch) && ch != '>') {
1284      value.append('/');
1285      value.append((char) ch);
1286    }
1287    else {
1288          unread(ch);
1289      return '/';
1290    }
1291      }
1292      else if (ch == '&' && ! _entitiesAsText) {
1293    if ((ch = _reader.read()) == '#')
1294      value.append((char) parseCharacterReference());
1295    else if (! isGeneral) {
1296      value.append('&');
1297      value.append((char) ch);
1298    }
1299        else if (XmlChar.isNameStart(ch)) {
1300      ch = _reader.parseName(_buf, ch);
1301      String JavaDoc name = _buf.toString();
1302
1303      if (ch != ';' && _strictXml)
1304        throw error(L.l("expected `{0}' at {1}", ";", badChar(ch)));
1305      else if (ch != ';') {
1306        value.append('&');
1307        value.append(name);
1308        continue;
1309      } else {
1310            int lookup = _entities.getEntity(name);
1311
1312            if (lookup >= 0 && lookup <= 0xffff) {
1313              ch = _reader.read();
1314              value.append((char) lookup);
1315              continue;
1316            }
1317            
1318        QEntity entity = _dtd == null ? null : _dtd.getEntity(name);
1319        if (entity != null && entity._value != null)
1320              setMacroAttr(entity._value);
1321        else if (_strictXml)
1322          throw error(L.l("expected local reference at `&{0};'", name));
1323        else {
1324          value.append('&');
1325          value.append(name);
1326          value.append(';');
1327        }
1328      }
1329    }
1330      }
1331      else if (ch == '%' && ! isGeneral) {
1332        ch = _reader.read();
1333
1334        if (! XmlChar.isNameStart(ch)) {
1335          value.append('%');
1336          continue;
1337        }
1338        else {
1339          ch = _reader.parseName(_buf, ch);
1340
1341          if (ch != ';')
1342            throw error(L.l("expected `{0}' at {1}", ";", badChar(ch)));
1343          else
1344            addPEReference(value, _buf.toString());
1345        }
1346      }
1347      else if (ch == '<' && _isJsp) {
1348        value.append('<');
1349        
1350        ch = _reader.read();
1351
1352        if (ch != '%')
1353          continue;
1354
1355        value.append('%');
1356
1357        ch = _reader.read();
1358        while (ch >= 0) {
1359          if (ch == '%') {
1360            ch = _reader.read();
1361            if (ch == '>') {
1362              value.append("%>");
1363              break;
1364            }
1365            else
1366              value.append('%');
1367          }
1368          else {
1369            value.append((char) ch);
1370            ch = _reader.read();
1371          }
1372        }
1373      }
1374      else if (isGeneral) {
1375        if (ch == '\r') {
1376          ch = _reader.read();
1377          if (ch != '\n') {
1378            value.append('\n');
1379            continue;
1380          }
1381        }
1382        value.append((char) ch);
1383      }
1384      else if (ch == '\r') {
1385    value.append(' ');
1386        
1387        if ((ch = _reader.read()) != '\n')
1388          continue;
1389      }
1390      else if (ch == '\n')
1391    value.append(' ');
1392      else
1393    value.append((char) ch);
1394
1395      ch = _reader.read();
1396    }
1397
1398    if (end != 0)
1399      ch = _reader.read();
1400
1401    return ch;
1402  }
1403
1404  private boolean isAttributeChar(int ch)
1405  {
1406    switch (ch) {
1407    case ' ': case '\t': case '\n': case '\r':
1408      return false;
1409    case '<': case '>': case '\'':case '"': case '=':
1410      return false;
1411    default:
1412      return true;
1413    }
1414  }
1415
1416  private void parsePcdata(QNode node) throws IOException JavaDoc, SAXException JavaDoc
1417  {
1418    int ch;
1419    String JavaDoc tail = "</" + node.getNodeName() + ">";
1420
1421    _text.clear();
1422    ch = _reader.read();
1423    if (ch == '\n')
1424      ch = _reader.read();
1425
1426    for (; ch != -1; ch = _reader.read()) {
1427      addText((char) ch);
1428
1429      if (_text.endsWith(tail)) {
1430    _text.setLength(_text.length() - tail.length());
1431    if (_text.length() > 1 && _text.charAt(_text.length() - 1) == '\n')
1432      _text.setLength(_text.length() - 1);
1433    appendText();
1434    return;
1435      }
1436    }
1437    
1438    throw error("bad pcdata");
1439  }
1440
1441  private int parseXMLDeclaration(XmlReader oldReader)
1442    throws IOException JavaDoc, SAXException JavaDoc
1443  {
1444    int startOffset = _is.getOffset();
1445    boolean isEBCDIC = false;
1446    int ch = _is.read();
1447
1448    XmlReader reader = null;
1449    
1450    // utf-16 starts with \xfe \xff
1451
if (ch == 0xfe) {
1452      ch = _is.read();
1453      if (ch == 0xff) {
1454    _owner.setAttribute("encoding", "UTF-16");
1455    _is.setEncoding("utf-16");
1456
1457        reader = new Utf16Reader(this, _is);
1458        
1459        ch = reader.read();
1460      }
1461    }
1462    // utf-16 rev starts with \xff \xfe
1463
else if (ch == 0xff) {
1464      ch = _is.read();
1465      if (ch == 0xfe) {
1466    _owner.setAttribute("encoding", "UTF-16");
1467    _is.setEncoding("utf-16");
1468
1469        reader = new Utf16Reader(this, _is);
1470        ((Utf16Reader) reader).setReverse(true);
1471        
1472        ch = reader.read();
1473      }
1474    }
1475    // utf-16 can also start with \x00 <
1476
else if (ch == 0x00) {
1477      ch = _is.read();
1478      _owner.setAttribute("encoding", "UTF-16");
1479      _is.setEncoding("utf-16");
1480      
1481      reader = new Utf16Reader(this, _is);
1482    }
1483    // utf-8 BOM is \xef \xbb \xbf
1484
else if (ch == 0xef) {
1485      ch = _is.read();
1486      if (ch == 0xbb) {
1487        ch = _is.read();
1488
1489        if (ch == 0xbf) {
1490          ch = _is.read();
1491
1492          _owner.setAttribute("encoding", "UTF-8");
1493          _is.setEncoding("utf-8");
1494      
1495          reader = new Utf8Reader(this, _is);
1496        }
1497      }
1498    }
1499    else if (ch == 0x4c) {
1500      // ebcdic
1501
// xml/00l1
1502
_is.unread();
1503      // _is.setEncoding("cp037");
1504
_is.setEncoding("cp500");
1505
1506      isEBCDIC = true;
1507
1508      reader = new XmlReader(this, _is);
1509
1510      ch = reader.read();
1511    }
1512    else {
1513      int ch2 = _is.read();
1514
1515      if (ch2 == 0x00) {
1516    _owner.setAttribute("encoding", "UTF-16LE");
1517    _is.setEncoding("utf-16le");
1518
1519        reader = new Utf16Reader(this, _is);
1520        ((Utf16Reader) reader).setReverse(true);
1521      }
1522      else if (ch2 > 0)
1523    _is.unread();
1524    }
1525
1526    if (reader != null && reader != oldReader) {
1527    }
1528    else if (_policy instanceof HtmlPolicy ||
1529             _is.getSource() instanceof ReaderWriterStream) {
1530      reader = new XmlReader(this, _is);
1531    }
1532    else {
1533      reader = new Utf8Reader(this, _is);
1534    }
1535
1536    if (ch == '\n')
1537      reader.setLine(2);
1538
1539    reader.setSystemId(_systemId);
1540    if (_systemId == null)
1541      reader.setSystemId(_filename);
1542    reader.setFilename(_filename);
1543    reader.setPublicId(_publicId);
1544
1545    reader.setNext(oldReader);
1546
1547    _reader = reader;
1548
1549    /* XXX: this might be too strict. */
1550    /*
1551    if (! strictXml) {
1552      for (; XmlChar.isWhitespace(ch); ch = reader.read()) {
1553      }
1554    }
1555    */

1556
1557    if (ch != '<')
1558      return ch;
1559
1560    if (parseXMLDecl(_reader) && isEBCDIC) {
1561      // EBCDIC requires a re-read
1562
_is.setOffset(startOffset);
1563
1564      ch = _reader.read();
1565      if (ch != '<')
1566    throw new IllegalStateException JavaDoc();
1567      
1568      parseXMLDecl(_reader);
1569    }
1570
1571    return _reader.read();
1572  }
1573
1574  private boolean parseXMLDecl(XmlReader reader)
1575    throws IOException JavaDoc, SAXException JavaDoc
1576  {
1577    int ch = reader.read();
1578    if (ch != '?') {
1579      unread((char) ch);
1580      unread('<');
1581      return false;
1582    }
1583
1584    ch = _reader.read();
1585    if (! XmlChar.isNameStart(ch))
1586      throw error(L.l("expected name after '<?' at {0}. Processing instructions expect a name like <?foo ... ?>", badChar(ch)));
1587    ch = _reader.parseName(_text, ch);
1588
1589    String JavaDoc piName = _text.toString();
1590    if (! piName.equals("xml")) {
1591      ch = parsePITail(piName, ch);
1592      unread(ch);
1593      return false;
1594    }
1595          
1596    if (_switchToXml && _activeNode == DOC_NAME && ! _inDtd) {
1597      _policy = new XmlPolicy();
1598    }
1599
1600    ch = parseAttributes(ch, false);
1601      
1602    if (ch != '?')
1603      throw error(L.l("expected `?' at {0}. Processing instructions end with `?>' like <?foo ... ?>", badChar(ch)));
1604    if ((ch = _reader.read()) != '>')
1605      throw error(L.l("expected `>' at {0}. Processing instructions end with `?>' like <?foo ... ?>", ">", badChar(ch)));
1606
1607    for (int i = 0; i < _attributes.getLength(); i++) {
1608      QName name = _attributes.getName(i);
1609      String JavaDoc value = _attributes.getValue(i);
1610
1611      if (_owner != null)
1612        _owner.setAttribute(name.getName(), value);
1613
1614      if (name.getName().equals("encoding")) { // xml/00hb // && ! _inDtd) {
1615
String JavaDoc encoding = value;
1616
1617        if (! _isStaticEncoding &&
1618            ! encoding.equalsIgnoreCase("UTF-8") &&
1619            ! encoding.equalsIgnoreCase("UTF-16") &&
1620            ! (_is.getSource() instanceof ReaderWriterStream)) {
1621      _is.setEncoding(encoding);
1622
1623      XmlReader oldReader = _reader;
1624      
1625      _reader = new XmlReader(this, _is);
1626      // _reader.setNext(oldReader);
1627

1628      _reader.setLine(oldReader.getLine());
1629
1630          _reader.setSystemId(_filename);
1631          _reader.setPublicId(null);
1632        }
1633      }
1634    }
1635
1636    return true;
1637  }
1638
1639  private int parsePI()
1640    throws IOException JavaDoc, SAXException JavaDoc
1641  {
1642    int ch;
1643
1644    appendText();
1645    ch = _reader.read();
1646    if (! XmlChar.isNameStart(ch))
1647      throw error(L.l("expected name after '<?' at {0}. Processing instructions expect a name like <?foo ... ?>", badChar(ch)));
1648    ch = _reader.parseName(_text, ch);
1649
1650    String JavaDoc piName = _text.toString();
1651    if (! piName.equals("xml"))
1652      return parsePITail(piName, ch);
1653    else if (_switchToXml && _activeNode == DOC_NAME && ! _inDtd) {
1654      _policy = new XmlPolicy();
1655      return parsePITail(piName, ch);
1656    }
1657    else {
1658      throw error(L.l("<?xml ... ?> occurs after content. The <?xml ... ?> prolog must be at the document start."));
1659
1660    }
1661  }
1662
1663  private int parsePITail(String JavaDoc piName, int ch)
1664    throws IOException JavaDoc, SAXException JavaDoc
1665  {
1666    ch = skipWhitespace(ch);
1667
1668    _text.clear();
1669    while (ch != -1) {
1670      if (ch == '?') {
1671        if ((ch = _reader.read()) == '>')
1672          break;
1673        else
1674          _text.append('?');
1675      } else {
1676        _text.append((char) ch);
1677        ch = _reader.read();
1678      }
1679    }
1680
1681    if (_inDtd) {
1682      QProcessingInstruction pi;
1683      pi = new QProcessingInstruction(piName, _text.toString());
1684      pi._owner = _dtd._owner;
1685      _dtd.appendChild(pi);
1686    }
1687    else
1688      _contentHandler.processingInstruction(piName, _text.toString());
1689
1690    return _reader.read();
1691  }
1692
1693  /**
1694   * Parses a comment. The "&lt;!--" has already been read.
1695   */

1696  private void parseComment()
1697    throws IOException JavaDoc, SAXException JavaDoc
1698  {
1699    if (! _skipComments)
1700      appendText();
1701
1702    int ch = _reader.read();
1703
1704    if (ch != '-')
1705      throw error(L.l("expected comment at {0}", badChar(ch)));
1706
1707    ch = _reader.read();
1708
1709    if (! _skipComments)
1710      _buf.clear();
1711          
1712  comment:
1713    while (ch != -1) {
1714      if (ch == '-') {
1715    ch = _reader.read();
1716
1717    while (ch == '-') {
1718      if ((ch = _reader.read()) == '>')
1719        break comment;
1720      else if (_strictComments)
1721        throw error(L.l("XML forbids `--' in comments"));
1722      else if (ch == '-') {
1723            if (! _skipComments)
1724              _buf.append('-');
1725          }
1726      else {
1727            if (! _skipComments)
1728              _buf.append("--");
1729        break;
1730          }
1731    }
1732
1733        _buf.append('-');
1734      } else if (! XmlChar.isChar(ch)) {
1735        throw error(L.l("bad character {0}", hex(ch)));
1736      } else {
1737    _buf.append((char) ch);
1738    ch = _reader.read();
1739      }
1740    }
1741
1742    if (_inDtd) {
1743      QComment comment = new QComment(_buf.toString());
1744      comment._owner = _dtd._owner;
1745      _dtd.appendChild(comment);
1746    }
1747    else if (_skipComments) {
1748    }
1749    else if (_contentHandler instanceof XMLWriter && ! _skipComments) {
1750      ((XMLWriter) _contentHandler).comment(_buf.toString());
1751      _isIgnorableWhitespace = true;
1752    }
1753    else if (_lexicalHandler != null) {
1754      _lexicalHandler.comment(_buf.getBuffer(), 0, _buf.getLength());
1755      _isIgnorableWhitespace = true;
1756    }
1757  }
1758
1759  /**
1760   * Parses the contents of a cdata section.
1761   *
1762   * <pre>
1763   * cdata ::= &lt;![CDATA[ ... ]]>
1764   * </pre>
1765   */

1766  private void parseCdata()
1767    throws IOException JavaDoc, SAXException JavaDoc
1768  {
1769    int ch;
1770
1771    if (_forgiving) {
1772      if ((ch = _reader.read()) != 'C') {
1773    appendText("<![" + (char) ch);
1774    return;
1775      }
1776      else if ((ch = _reader.read()) != 'D') {
1777    appendText("<![C" + (char) ch);
1778    return;
1779      }
1780      else if ((ch = _reader.read()) != 'A') {
1781    appendText("<![CD" + (char) ch);
1782    return;
1783      }
1784      else if ((ch = _reader.read()) != 'T') {
1785    appendText("<![CDA" + (char) ch);
1786    return;
1787      }
1788      else if ((ch = _reader.read()) != 'A') {
1789    appendText("<![CDAT" + (char) ch);
1790    return;
1791      }
1792      else if ((ch = _reader.read()) != '[') {
1793    appendText("<![CDATA" + (char) ch);
1794    return;
1795      }
1796    }
1797    else if ((ch = _reader.read()) != 'C' ||
1798             (ch = _reader.read()) != 'D' ||
1799             (ch = _reader.read()) != 'A' ||
1800             (ch = _reader.read()) != 'T' ||
1801             (ch = _reader.read()) != 'A' ||
1802             (ch = _reader.read()) != '[') {
1803      throw error(L.l("expected `<![CDATA[' at {0}", badChar(ch)));
1804    }
1805
1806    ch = _reader.read();
1807
1808    if (_lexicalHandler != null) {
1809      _lexicalHandler.startCDATA();
1810      appendText();
1811    }
1812    else if (! _isCoalescing)
1813      appendText();
1814
1815  cdata:
1816    while (ch != -1) {
1817      if (ch == ']') {
1818    ch = _reader.read();
1819
1820    while (ch == ']') {
1821      if ((ch = _reader.read()) == '>')
1822        break cdata;
1823      else if (ch == ']')
1824        addText(']');
1825      else {
1826        addText(']');
1827        break;
1828      }
1829    }
1830
1831    addText(']');
1832      } else if (_strictCharacters && ! isChar(ch)) {
1833    throw error(L.l("expected character in cdata at {0}", badChar(ch)));
1834      } else {
1835    addText((char) ch);
1836    ch = _reader.read();
1837      }
1838    }
1839    
1840    if (_lexicalHandler != null) {
1841      appendText();
1842      _lexicalHandler.endCDATA();
1843    }
1844    else if (! _isCoalescing)
1845      appendText();
1846  }
1847
1848  /**
1849   * Ignores content to the ']]>'
1850   */

1851  private void parseIgnore()
1852    throws IOException JavaDoc, SAXException JavaDoc
1853  {
1854    int ch = read();
1855
1856    while (ch >= 0) {
1857      if (ch != ']') {
1858    ch = read();
1859      }
1860      else if ((ch = read()) != ']') {
1861      }
1862      else if ((ch = read()) == '>')
1863    return;
1864    }
1865  }
1866
1867  private int parseContentSpec(QElementDef def, int ch)
1868    throws IOException JavaDoc, SAXException JavaDoc
1869  {
1870    ch = expandPE(ch);
1871    
1872    if (XmlChar.isNameStart(ch)) {
1873      ch = _reader.parseName(_text, ch);
1874      String JavaDoc name = _text.toString();
1875
1876      if (name.equals("EMPTY")) {
1877    def._content = "EMPTY";
1878    return ch;
1879      }
1880      else if (name.equals("ANY")) {
1881    def._content = "ANY";
1882    return ch;
1883      }
1884      else
1885    throw error(L.l("expected EMPTY or ANY at `{0}'", name));
1886    }
1887    else if (ch != '(') {
1888      throw error(L.l("expected grammar definition starting with '(' at {0}. <!ELEMENT> definitions have the syntax <!ELEMENT name - - (grammar)>", badChar(ch)));
1889    }
1890    else {
1891      QContentParticle cp = new QContentParticle();
1892      def._content = cp;
1893
1894      return parseContentParticle(cp, true);
1895    }
1896  }
1897
1898  /**
1899   * Parses a content-particle, i.e. a grammer particle in the DTD
1900   * regexp.
1901   */

1902  private int parseContentParticle(QContentParticle cp, boolean isTop)
1903    throws IOException JavaDoc, SAXException JavaDoc
1904  {
1905    boolean hasCdata = false;
1906    cp._separator = 0;
1907    cp._repeat = 0;
1908    int ch;
1909
1910    ch = expandPE(_reader.read());
1911    
1912    for (; ch != -1; ch = expandPE(ch)) {
1913      if (ch == '(') {
1914    QContentParticle child = new QContentParticle();
1915    cp.addChild(child);
1916    
1917    ch = parseContentParticle(child, false);
1918      }
1919      else if (XmlChar.isNameStart(ch)) {
1920    ch = _reader.parseName(_text, ch);
1921    cp.addChild(_text.toString());
1922      }
1923      else if (ch == '#') {
1924    ch = _reader.parseName(_text, _reader.read());
1925    String JavaDoc name = _text.toString();
1926
1927    if (_strictXml && cp._children.size() != 0)
1928      throw error(L.l("`#{0}' must occur first", name));
1929    if (_strictXml && ! isTop)
1930      throw error(L.l("`#{0}' may only occur at top level", name));
1931
1932    if (name.equals("PCDATA"))
1933      cp.addChild("#PCDATA");
1934    else
1935      throw error(L.l("illegal content particle at `#{0}'", name));
1936
1937    hasCdata = true;
1938      }
1939      else
1940    throw error(L.l("expected content particle at {0}", badChar(ch)));
1941
1942      ch = expandPE(ch);
1943
1944      if (ch == '?' || ch == '*' || ch == '+') {
1945        Object JavaDoc child = cp.getChild(cp.getChildSize() - 1);
1946        if (child instanceof QContentParticle) {
1947          QContentParticle cpChild = (QContentParticle) child;
1948          cpChild._repeat = ch;
1949        }
1950        else {
1951          QContentParticle cpChild = new QContentParticle();
1952          cpChild.addChild(child);
1953          cpChild._repeat = ch;
1954          cp.setChild(cp.getChildSize() - 1, cpChild);
1955        }
1956
1957        ch = expandPE(_reader.read());
1958      }
1959
1960      if (ch == ')')
1961    break;
1962      else if (cp._separator == 0) {
1963    if (ch == '|')
1964      cp._separator = ch;
1965    else if (hasCdata && _strictXml)
1966      throw error(L.l("#PCDATA must be separated by `|' at {0}",
1967                          badChar(ch)));
1968    else if (ch == ',')
1969      cp._separator = ch;
1970    else if (! _strictXml && ch =='&')
1971      cp._separator = ch;
1972    else
1973      throw error(L.l("expected separator at {0}", badChar(ch)));
1974
1975    ch = _reader.read();
1976      } else if (ch != cp._separator)
1977    throw error(L.l("expected `{0}' at {1}",
1978                        "" + (char) cp._separator, badChar(ch)));
1979      else
1980    ch = _reader.read();
1981    }
1982
1983    ch = expandPE(_reader.read());
1984
1985    if (_strictXml && hasCdata && (ch == '+' || ch == '?'))
1986      throw error(L.l("pcdata clause can not have {0}", badChar(ch)));
1987    else if (ch == '*' || ch == '+' || ch == '?') {
1988      cp._repeat = ch;
1989      return _reader.read();
1990    }
1991    else
1992      return ch;
1993  }
1994
1995  private int expandPE(int ch)
1996    throws IOException JavaDoc, SAXException JavaDoc
1997  {
1998    ch = skipWhitespace(ch);
1999    
2000    while (ch == '%') {
2001      parsePEReference();
2002      ch = skipWhitespace(_reader.read());
2003    }
2004
2005    return ch;
2006  }
2007
2008  /**
2009   * Parses a PE reference %foo; and inserts the macro text to the input
2010   * stream.
2011   */

2012  private void parsePEReference()
2013    throws IOException JavaDoc, SAXException JavaDoc
2014  {
2015    int ch = _reader.parseName(_buf, _reader.read());
2016
2017    if (ch != ';')
2018      throw error(L.l("`%{0};' expects `;' at {1}. Parameter entities have a `%name;' syntax.", _buf, badChar(ch)));
2019
2020    addPEReference(_text, _buf.toString());
2021  }
2022
2023  /**
2024   * Expands the macro value of a PE reference.
2025   */

2026  private void addPEReference(CharBuffer value, String JavaDoc name)
2027    throws IOException JavaDoc, SAXException JavaDoc
2028  {
2029    QEntity entity = _dtd.getParameterEntity(name);
2030
2031    if (entity == null && ! _dtd.isExternal())
2032      throw error(L.l("`%{0};' is an unknown parameter entity. Parameter entities must be defined in an <!ENTITY> declaration before use.", name));
2033    else if (entity != null && entity._value != null) {
2034      setMacro(entity._value);
2035    }
2036    else if (entity != null && entity.getSystemId() != null) {
2037      pushInclude(entity.getPublicId(), entity.getSystemId());
2038    }
2039    else {
2040      value.append("%");
2041      value.append(name);
2042      value.append(";");
2043    }
2044  }
2045
2046  /**
2047   * <!ELEMENT name contentspec>
2048   */

2049  private void parseElementDecl(QDocumentType doctype)
2050    throws IOException JavaDoc, SAXException JavaDoc
2051  {
2052    int ch = skipWhitespace(_reader.read());
2053
2054    ch = _reader.parseName(_text, ch);
2055    String JavaDoc name = _text.toString();
2056
2057    ch = skipWhitespace(ch);
2058
2059    QElementDef def = _dtd.addElement(name);
2060    def.setLocation(getSystemId(), getFilename(), getLine(), getColumn());
2061
2062    boolean needsStartTag = true;
2063    boolean needsEndTag = true;
2064
2065    if (_optionalTags && (ch == 'O' || ch == '-')) {
2066      needsStartTag = ch == '-';
2067
2068      ch = skipWhitespace(ch);
2069
2070      if (ch == '0')
2071    needsEndTag = false;
2072      else if (ch == '-')
2073    needsEndTag = true;
2074      else
2075    throw error(L.l("unknown short tag"));
2076    }
2077
2078    ch = parseContentSpec(def, ch);
2079
2080    ch = skipWhitespace(ch);
2081
2082    if (ch != '>')
2083      throw error(L.l("`<!ELEMENT' must close with `>' at {0}", badChar(ch)));
2084  }
2085
2086  private static String JavaDoc toAttrDefault(CharBuffer text)
2087  {
2088    for (int i = 0; i < text.length(); i++) {
2089      int ch = text.charAt(i);
2090
2091      if (ch == '"') {
2092    text.delete(i, i + 1);
2093    text.insert(i, "&#34;");
2094    i--;
2095      } else if (ch == '\'') {
2096    text.delete(i, i + 1);
2097    text.insert(i, "&#39;");
2098    i--;
2099      }
2100    }
2101
2102    return text.toString();
2103  }
2104
2105  /**
2106   * <!ATTLIST name (attr type def)*>
2107   */

2108  private void parseAttlistDecl(QDocumentType doctype)
2109    throws IOException JavaDoc, SAXException JavaDoc
2110  {
2111    int ch = skipWhitespace(_reader.read());
2112
2113    ch = _reader.parseName(_text, ch);
2114    String JavaDoc name = _text.toString();
2115
2116    ch = skipWhitespace(ch);
2117
2118    QElementDef def = _dtd.addElement(name);
2119
2120    while (XmlChar.isNameStart((ch = expandPE(ch)))) {
2121      ch = _reader.parseName(_text, ch);
2122      String JavaDoc attrName = _text.toString();
2123
2124      String JavaDoc attrType = null;
2125      ArrayList JavaDoc<String JavaDoc> enumeration = null;
2126      ch = expandPE(ch);
2127      if (ch == '(') {
2128    attrType = "#ENUM";
2129    enumeration = new ArrayList JavaDoc<String JavaDoc>();
2130    do {
2131      ch = expandPE(_reader.read());
2132
2133      ch = parseNameToken(_text, ch);
2134      enumeration.add(_text.toString());
2135
2136      ch = expandPE(ch);
2137    } while (ch == '|');
2138
2139    if (ch != ')')
2140      throw error(L.l("expected `{0}' at {1}. <!ATTRLIST> enumerations definitions are enclosed in '(' ... ')'.", ")", badChar(ch)));
2141    ch = _reader.read();
2142      }
2143      else {
2144    ch = _reader.parseName(_text, ch);
2145    attrType = _text.toString();
2146
2147    if (attrType.equals("NOTATION")) {
2148      enumeration = new ArrayList JavaDoc<String JavaDoc>();
2149      ch = expandPE(ch);
2150      if (ch != '(')
2151            throw error(L.l("expected `{0}' at {1}", "(", badChar(ch)));
2152
2153      do {
2154        ch = expandPE(_reader.read());
2155
2156        ch = _reader.parseName(_text, ch);
2157        enumeration.add(_text.toString());
2158
2159        ch = expandPE(ch);
2160      } while (ch == '|');
2161
2162      if (ch != ')')
2163        throw error(L.l("expected `{0}' at {1}", ")", badChar(ch)));
2164      ch = _reader.read();
2165    }
2166    else if (_attrTypes.get(attrType) != null) {
2167    }
2168        else
2169      throw error(L.l("expected attribute type at `{0}'", attrType));
2170      }
2171
2172      ch = skipWhitespace(ch);
2173      String JavaDoc qualifier = null;
2174      String JavaDoc attrDefault = null;
2175      if (ch == '#') {
2176    ch = _reader.parseName(_text, _reader.read());
2177    qualifier = "#" + _text.toString();
2178
2179    if (qualifier.equals("#IMPLIED")) {
2180    }
2181    else if (qualifier.equals("#REQUIRED")) {
2182    }
2183    else if (qualifier.equals("#FIXED")) {
2184      ch = skipWhitespace(ch);
2185      ch = parseValue(_text, ch, false);
2186      attrDefault = _text.toString();
2187    } else
2188      throw error(L.l("expected attribute default at `{0}'",
2189                      qualifier));
2190      }
2191      else if (ch != '>') {
2192    ch = parseValue(_text, ch, false);
2193    attrDefault = _text.toString();
2194      }
2195
2196      def.addAttribute(attrName, attrType, enumeration,
2197                       qualifier, attrDefault);
2198      if (attrType != null && attrType.equals("ID"))
2199    doctype.setElementId(name, attrName);
2200
2201      ch = skipWhitespace(ch);
2202    }
2203
2204    if (ch != '>')
2205      throw error(L.l("expected `{0}' at {1}", ">", badChar(ch)));
2206  }
2207
2208  /**
2209   * <!NOTATION name systemId publicId>
2210   */

2211  private void parseNotationDecl(QDocumentType doctype)
2212    throws IOException JavaDoc, SAXException JavaDoc
2213  {
2214    int ch = skipWhitespace(_reader.read());
2215
2216    ch = _reader.parseName(_text, ch);
2217    String JavaDoc name = _text.toString();
2218
2219    ch = skipWhitespace(ch);
2220    ch = _reader.parseName(_text, ch);
2221    String JavaDoc key = _text.toString();
2222
2223    ch = skipWhitespace(ch);
2224    ch = parseValue(_text, ch, false);
2225    String JavaDoc id = _text.toString();
2226
2227    ch = skipWhitespace(ch);
2228
2229    QNotation notation;
2230
2231    if (key.equals("PUBLIC")) {
2232      String JavaDoc systemId = null;
2233
2234      if (ch == '"' || ch == '\'') {
2235    ch = parseValue(_text, ch, false);
2236    ch = skipWhitespace(ch);
2237    systemId = _text.toString();
2238      }
2239
2240      notation = new QNotation(name, id, systemId);
2241      notation._owner = doctype._owner;
2242      notation.setLocation(getSystemId(), getFilename(), getLine(), getColumn());
2243    }
2244    else if (key.equals("SYSTEM")) {
2245      notation = new QNotation(name, null, id);
2246      notation._owner = doctype._owner;
2247      notation.setLocation(getSystemId(), getFilename(), getLine(), getColumn());
2248    }
2249    else
2250      throw error(L.l("expected PUBLIC or SYSTEM at `{0}'", key));
2251    
2252    doctype.addNotation(notation);
2253    doctype.appendChild(notation);
2254
2255    if (ch != '>')
2256      throw error(L.l("expected `{0}' at {1}", ">", badChar(ch)));
2257  }
2258
2259  /**
2260   * externalID ::= PUBLIC publicId systemId
2261   * ::= SYSTEM systemId
2262   */

2263  private int parseExternalID(int ch)
2264    throws IOException JavaDoc, SAXException JavaDoc
2265  {
2266    ch = _reader.parseName(_text, ch);
2267    String JavaDoc key = _text.toString();
2268    ch = skipWhitespace(ch);
2269
2270    _extSystemId = null;
2271    _extPublicId = null;
2272    if (key.equals("PUBLIC") || _forgiving && key.equalsIgnoreCase("public")) {
2273      ch = parseValue(_text, ch, false);
2274      _extPublicId = _text.toString();
2275      ch = skipWhitespace(ch);
2276
2277      if (_extPublicId.indexOf('&') > 0)
2278    throw error(L.l("Illegal character '&' in PUBLIC identifier '{0}'",
2279            _extPublicId));
2280
2281      ch = parseValue(_text, ch, false);
2282      ch = skipWhitespace(ch);
2283      _extSystemId = _text.toString();
2284    }
2285    else if (key.equals("SYSTEM") ||
2286         _forgiving && key.equalsIgnoreCase("system")) {
2287      ch = parseValue(_text, ch, false);
2288      _extSystemId = _text.toString();
2289    }
2290    else
2291      throw error(L.l("expected PUBLIC or SYSTEM at `{0}'", key));
2292
2293    return ch;
2294  }
2295
2296  /**
2297   * <!ENTITY name systemId publicId>
2298   */

2299  private void parseEntityDecl(QDocumentType doctype)
2300    throws IOException JavaDoc, SAXException JavaDoc
2301  {
2302    int ch = skipWhitespace(_reader.read());
2303
2304    boolean isPe = ch == '%';
2305
2306    if (isPe)
2307      ch = skipWhitespace(_reader.read());
2308
2309    ch = _reader.parseName(_text, ch);
2310    String JavaDoc name = _text.toString();
2311
2312    ch = skipWhitespace(ch);
2313
2314    QEntity entity;
2315    if (ch == '"' || ch == '\'') {
2316      ch = parseValue(_text, ch, false);
2317      
2318      entity = new QEntity(name, _text.toString(), null, null);
2319      entity._owner = doctype._owner;
2320      entity.setLocation(getSystemId(), getFilename(), getLine(), getColumn());
2321    }
2322    else {
2323      ch = parseExternalID(ch);
2324
2325      entity = new QEntity(name, null, _extPublicId, _extSystemId);
2326      entity._owner = doctype._owner;
2327      entity.setLocation(getSystemId(), getFilename(), getLine(), getColumn());
2328
2329      ch = skipWhitespace(ch);
2330      if (! isPe && XmlChar.isNameStart(ch)) {
2331    ch = _reader.parseName(_text, ch);
2332    String JavaDoc key = _text.toString();
2333    if (key.equals("NDATA")) {
2334      ch = skipWhitespace(ch);
2335      ch = _reader.parseName(_text, ch);
2336
2337      String JavaDoc ndata = _text.toString();
2338
2339      entity._ndata = ndata;
2340    } else
2341      throw error(L.l("expected `NDATA' at `{0}'", key));
2342      }
2343    }
2344      
2345    entity._isPe = isPe;
2346
2347    if (isPe)
2348      doctype.addParameterEntity(entity);
2349    else
2350      doctype.addEntity(entity);
2351
2352    doctype.appendChild(entity);
2353
2354    ch = skipWhitespace(ch);
2355
2356    if (ch != '>')
2357      throw error(L.l("expected `>' at {0}", badChar(ch)));
2358  }
2359
2360  private boolean isWhitespace(int ch)
2361  {
2362    return ch <= 0x20 && (ch == 0x20 || ch == 0x9 || ch == 0xa || ch == 0xd);
2363  }
2364
2365  private boolean isChar(int ch)
2366  {
2367    return (ch >= 0x20 && ch <= 0xd7ff ||
2368        ch == 0x9 ||
2369        ch == 0xa ||
2370        ch == 0xd ||
2371        ch >= 0xe000 && ch <= 0xfffd);
2372  }
2373
2374  /**
2375   * Returns the hex representation of a byte.
2376   */

2377  private static String JavaDoc hex(int value)
2378  {
2379    CharBuffer cb = CharBuffer.allocate();
2380
2381    for (int b = 3; b >= 0; b--) {
2382      int v = (value >> (4 * b)) & 0xf;
2383      if (v < 10)
2384    cb.append((char) (v + '0'));
2385      else
2386    cb.append((char) (v - 10 + 'a'));
2387    }
2388
2389    return cb.close();
2390  }
2391
2392  /**
2393   * Returns the current filename.
2394   */

2395  public String JavaDoc getFilename()
2396  {
2397    return _filename;
2398  }
2399
2400  /**
2401   * Returns the current line.
2402   */

2403  public int getLine()
2404  {
2405    return _line;
2406  }
2407  
2408  /**
2409   * Returns the current column.
2410   */

2411  private int getColumn()
2412  {
2413    return 0;
2414  }
2415
2416  /**
2417   * Returns the opening line of the current node.
2418   */

2419  int getNodeLine()
2420  {
2421    if (_elementTop > 0)
2422      return _elementLines[_elementTop - 1];
2423    else
2424      return 1;
2425  }
2426
2427  /**
2428   * Returns the current public id being read.
2429   */

2430  public String JavaDoc getPublicId()
2431  {
2432    if (_reader != null)
2433      return _reader.getPublicId();
2434    else
2435      return _publicId;
2436  }
2437  
2438  /**
2439   * Returns the current system id being read.
2440   */

2441  public String JavaDoc getSystemId()
2442  {
2443    if (_reader != null)
2444      return _reader.getSystemId();
2445    else if (_systemId != null)
2446      return _systemId;
2447    else
2448      return _filename;
2449  }
2450
2451  public void setLine(int line)
2452  {
2453    _line = line;
2454  }
2455  
2456  public int getLineNumber() { return getLine(); }
2457  public int getColumnNumber() { return getColumn(); }
2458
2459  /**
2460   * Adds a string to the current text buffer.
2461   */

2462  private void addText(String JavaDoc s)
2463    throws IOException JavaDoc, SAXException JavaDoc
2464  {
2465    int len = s.length();
2466    
2467    for (int i = 0; i < len; i++)
2468      addText(s.charAt(i));
2469  }
2470  
2471  /**
2472   * Adds a character to the current text buffer.
2473   */

2474  private void addText(char ch)
2475    throws IOException JavaDoc, SAXException JavaDoc
2476  {
2477    if (_textLength >= _textCapacity) {
2478      appendText();
2479    }
2480
2481    if (_textLength > 0 && _textBuffer[_textLength - 1] == '\r') {
2482      _textBuffer[_textLength - 1] = '\n';
2483      if (ch == '\n')
2484        return;
2485    }
2486    
2487    if (_isIgnorableWhitespace && ! XmlChar.isWhitespace(ch))
2488      _isIgnorableWhitespace = false;
2489    
2490    _textBuffer[_textLength++] = ch;
2491  }
2492
2493  /**
2494   * Flushes the text buffer to the SAX callback.
2495   */

2496  private void appendText()
2497    throws IOException JavaDoc, SAXException JavaDoc
2498  {
2499    if (_textLength > 0) {
2500      if (_activeNode == DOC_NAME) {
2501        if (_isJspText) {
2502          _contentHandler.characters(_textBuffer, 0, _textLength);
2503        }
2504        else if (_isIgnorableWhitespace) {
2505        }
2506        else if (_strictXml)
2507          throw error(L.l("expected top element at `{0}'",
2508                          new String JavaDoc(_textBuffer, 0, _textLength)));
2509        else {
2510          addChild(TEXT_NAME);
2511          _contentHandler.characters(_textBuffer, 0, _textLength);
2512        }
2513      }
2514      else if (_isJspText) {
2515        _contentHandler.characters(_textBuffer, 0, _textLength);
2516      }
2517      else if (_isIgnorableWhitespace) {
2518        if (_isHtml)
2519          _contentHandler.characters(_textBuffer, 0, _textLength);
2520        else
2521          _contentHandler.ignorableWhitespace(_textBuffer, 0, _textLength);
2522      }
2523      else if (_strictXml && ! _isIgnorableWhitespace && _activeNode == DOC_NAME) {
2524      }
2525      else {
2526        if (_isJspText) {
2527        }
2528        else if (_isIgnorableWhitespace)
2529          addChild(WHITESPACE_NAME);
2530        else
2531          addChild(TEXT_NAME);
2532        _contentHandler.characters(_textBuffer, 0, _textLength);
2533      }
2534        
2535      _textLength = 0;
2536      _isIgnorableWhitespace = true;
2537    }
2538  }
2539
2540  private void addElement(String JavaDoc child, boolean isEmpty,
2541                          QAttributes attributes,
2542                          NamespaceMap oldNamespace)
2543    throws IOException JavaDoc, SAXException JavaDoc
2544  {
2545    _text.clear();
2546    _text.append(child);
2547    addElement(_policy.getName(_text), isEmpty, attributes, oldNamespace);
2548  }
2549  
2550  /**
2551   * Adds an element as a child of the current tree. Some
2552   * DTDs, like HTML, will push additional nodes to make
2553   * the tree work, e.g. the body tag.
2554   *
2555   * @param child the new child to be added.
2556   * @param isEmpty true if the tag is already closed.
2557   */

2558  private void addElement(QName child, boolean isEmpty,
2559                          QAttributes attributes, NamespaceMap oldNamespace)
2560    throws IOException JavaDoc, SAXException JavaDoc
2561  {
2562    if (! _doResinInclude) {
2563    }
2564    else if (child.getName() == "include" &&
2565         child.getNamespaceURI() == "http://caucho.com/ns/resin/core" ||
2566         child.getName() == "resin:include") {
2567      if (! isEmpty)
2568        throw error(L.l("resin:include must be an empty tag"));
2569      
2570      handleResinInclude();
2571      return;
2572    }
2573    else if (child.getName() == "include-directory" &&
2574         child.getNamespaceURI() == "http://caucho.com/ns/resin/core" ||
2575         child.getName() == "resin:include-directory") {
2576      if (! isEmpty)
2577        throw error(L.l("resin:include-directory must be an empty tag"));
2578      
2579      handleResinIncludeDirectory();
2580      return;
2581    }
2582
2583    if (_activeNode == DOC_NAME && _hasTopElement && _strictXml)
2584      throw error(L.l("expected a single top-level element at `{0}'",
2585                      child.getName()));
2586
2587    _hasTopElement = true;
2588    
2589    String JavaDoc childURI = child.getNamespaceURI();
2590    String JavaDoc childLocal = child.getLocalName();
2591
2592    if (childURI == null) {
2593      childURI = "";
2594
2595      if (_isNamespaceAware)
2596    childLocal = child.getName();
2597      else
2598    childLocal = "";
2599    }
2600
2601    while (true) {
2602      int action = _policy.openAction(this, _activeNode, child);
2603
2604      switch (action) {
2605      case Policy.IGNORE:
2606    return;
2607
2608      case Policy.PUSH:
2609    //if (dbg.canWrite())
2610
// dbg.println("<" + child.getNodeName() + ">");
2611

2612        if (_contentHandler instanceof DOMBuilder)
2613          ((DOMBuilder) _contentHandler).startElement(child, attributes);
2614    else {
2615      _contentHandler.startElement(childURI,
2616                       childLocal,
2617                       child.getName(),
2618                       attributes);
2619    }
2620        
2621        if (isEmpty) {
2622      _contentHandler.endElement(childURI,
2623                     childLocal,
2624                     child.getName());
2625
2626          popNamespaces(oldNamespace);
2627        }
2628        else {
2629          if (_elementTop == _elementNames.length) {
2630            int len = _elementNames.length;
2631            QName []names = new QName[2 * len];
2632            NamespaceMap []newNamespaces = new NamespaceMap[2 * len];
2633            int []lines = new int[2 * len];
2634            System.arraycopy(_elementNames, 0, names, 0, len);
2635            System.arraycopy(_elementLines, 0, lines, 0, len);
2636            System.arraycopy(_namespaces, 0, newNamespaces, 0, len);
2637            _elementNames = names;
2638            _elementLines = lines;
2639            _namespaces = newNamespaces;
2640          }
2641          _namespaces[_elementTop] = oldNamespace;
2642          _elementLines[_elementTop] = getLine();
2643          _elementNames[_elementTop] = _activeNode;
2644          _elementTop++;
2645      _activeNode = child;
2646      _isTagStart = true;
2647        }
2648    return;
2649
2650      case Policy.PUSH_EMPTY:
2651    //if (dbg.canWrite())
2652
// dbg.println("<" + child.getNodeName() + "/>");
2653

2654        if (_contentHandler instanceof DOMBuilder)
2655          ((DOMBuilder) _contentHandler).startElement(child, attributes);
2656        else {
2657          _contentHandler.startElement(childURI,
2658                       childLocal,
2659                       child.getName(),
2660                       attributes);
2661        }
2662    
2663        _contentHandler.endElement(childURI,
2664                   childLocal,
2665                   child.getName());
2666
2667        popNamespaces(oldNamespace);
2668    return;
2669
2670      case Policy.PUSH_OPT:
2671    addElement(_policy.getOpt(), false, _nullAttributes, oldNamespace);
2672        break;
2673        
2674      case Policy.PUSH_VERBATIM:
2675        if (_contentHandler instanceof DOMBuilder)
2676          ((DOMBuilder) _contentHandler).startElement(child, attributes);
2677        else
2678          _contentHandler.startElement(childURI,
2679                       childLocal,
2680                       child.getName(),
2681                       attributes);
2682
2683        scanVerbatim(child.getName());
2684        appendText();
2685        _contentHandler.endElement(childURI,
2686                   childLocal,
2687                   child.getName());
2688        return;
2689
2690      case Policy.POP:
2691    //if (dbg.canWrite())
2692
// dbg.println("</" + activeNode.getNodeName() + ">");
2693

2694        popNode();
2695
2696        if (_activeNode == null)
2697          return;
2698        break;
2699        
2700      default:
2701    throw error(L.l("can't add `{0}' to `{1}'",
2702                        child.getName(), _activeNode.getName()));
2703      }
2704    }
2705  }
2706
2707  /**
2708   * Adds a child node to the current node.
2709   */

2710  private void addChild(QName child)
2711    throws IOException JavaDoc, SAXException JavaDoc
2712  {
2713    while (_activeNode != null) {
2714      int action = _policy.openAction(this, _activeNode, child);
2715
2716      switch (action) {
2717      case Policy.IGNORE:
2718    return;
2719
2720      case Policy.PUSH:
2721    _isTagStart = true;
2722        
2723      case Policy.PUSH_EMPTY:
2724    //if (dbg.canWrite())
2725
// dbg.println("<" + child.getNodeName() + ">");
2726

2727        /*
2728        if (child.getNodeType() == child.TEXT_NODE) {
2729          String value = child.getNodeValue();
2730          contentHandler.characters(value.toCharArray(), 0, value.length());
2731        }
2732        */

2733    return;
2734
2735      case Policy.PUSH_OPT:
2736    addElement(_policy.getOpt(), false, _nullAttributes, _namespaceMap);
2737        break;
2738        
2739      case Policy.PUSH_VERBATIM:
2740        scanVerbatim(child.getName());
2741        return;
2742
2743      case Policy.POP:
2744    // if (dbg.canWrite())
2745
// dbg.println("</" + activeNode.getNodeName() + ">");
2746

2747        popNode();
2748    break;
2749      default:
2750    throw error(L.l("cannot add `{0}' to `{1}'",
2751                        child.getName(), _activeNode.getName()));
2752      }
2753    }
2754  }
2755
2756  private void scanVerbatim(String JavaDoc name)
2757    throws IOException JavaDoc, SAXException JavaDoc
2758  {
2759    int ch = _reader.read();
2760
2761    while (ch >= 0) {
2762      if (ch != '<') {
2763        addText((char) ch);
2764        ch = _reader.read();
2765      }
2766      else if ((ch = _reader.read()) != '/')
2767        addText('<');
2768      else {
2769        ch = _reader.parseName(_eltName, _reader.read());
2770
2771        if (! _eltName.matchesIgnoreCase(name)) {
2772          addText("</");
2773          addText(_eltName.toString());
2774        }
2775        else if (ch != '>') {
2776          addText("</");
2777          addText(_eltName.toString());
2778        }
2779        else {
2780          return;
2781        }
2782      }
2783    }
2784
2785    throw error(L.l("expected </{0}> at {1}", name,
2786                    badChar(ch)));
2787  }
2788  
2789  private int skipWhitespace(int ch)
2790    throws IOException JavaDoc, SAXException JavaDoc
2791  {
2792    while (ch <= 0x20 && (ch == 0x20 || ch == 0x9 || ch == 0xa || ch == 0xd)) {
2793      ch = read();
2794    }
2795
2796    return ch;
2797  }
2798
2799
2800  public void setReader(XmlReader reader)
2801  {
2802    _reader = reader;
2803  }
2804
2805  /**
2806   * Adds text to the macro, escaping attribute values.
2807   */

2808  private void setMacroAttr(String JavaDoc text)
2809    throws IOException JavaDoc, SAXException JavaDoc
2810  {
2811    if (_reader != _macro) {
2812      _macro.init(this, _reader);
2813      _reader = _macro;
2814    }
2815
2816    int j = _macroIndex;
2817    for (int i = 0; i < text.length(); i++) {
2818      int ch = text.charAt(i);
2819
2820      if (ch == '\'')
2821        _macro.add("&#39;");
2822      else if (ch == '"')
2823    _macro.add("&#34;");
2824      else
2825    _macro.add((char) ch);
2826    }
2827  }
2828
2829  private void pushInclude(String JavaDoc systemId)
2830    throws IOException JavaDoc, SAXException JavaDoc
2831  {
2832    pushInclude(null, systemId);
2833  }
2834  /**
2835   * Pushes the named file as a lexical include.
2836   *
2837   * @param systemId the name of the file to include.
2838   */

2839  private void pushInclude(String JavaDoc publicId, String JavaDoc systemId)
2840    throws IOException JavaDoc, SAXException JavaDoc
2841  {
2842    InputStream JavaDoc stream = openStream(systemId, publicId);
2843    if (stream == null)
2844      throw new FileNotFoundException JavaDoc(systemId);
2845    _is = Vfs.openRead(stream);
2846    Path oldSearchPath = _searchPath;
2847    Path path = _is.getPath();
2848    if (path != null) {
2849      _owner.addDepend(path);
2850      
2851      if (_searchPath != null) {
2852        _searchPath = path.getParent();
2853        _reader.setSearchPath(oldSearchPath);
2854      }
2855    }
2856
2857    _filename = systemId;
2858    /*
2859    XmlReader nextReader;
2860    if (_reader instanceof Utf8Reader)
2861      nextReader = new Utf8Reader(this, _is);
2862    else {
2863      _is.setEncoding(_reader.getReadStream().getEncoding());
2864      nextReader = new XmlReader(this, _is);
2865    }
2866    _reader = nextReader;
2867    */

2868
2869    XmlReader oldReader = _reader;
2870    _reader = null;
2871    
2872    _line = 1;
2873
2874    int ch = parseXMLDeclaration(oldReader);
2875
2876    XmlReader reader = _reader;
2877
2878    if (reader instanceof MacroReader)
2879      reader = reader.getNext();
2880    
2881    reader.setSystemId(systemId);
2882    reader.setFilename(systemId);
2883    reader.setPublicId(publicId);
2884    reader.setNext(oldReader);
2885
2886    unread(ch);
2887  }
2888
2889  private void popInclude()
2890    throws IOException JavaDoc, SAXException JavaDoc
2891  {
2892    XmlReader oldReader = _reader;
2893    _reader = _reader.getNext();
2894    oldReader.setNext(null);
2895    _filename = _reader.getFilename();
2896    _line = _reader.getLine();
2897    _is = _reader.getReadStream();
2898    if (_reader.getSearchPath() != null)
2899      _searchPath = _reader.getSearchPath();
2900  }
2901
2902  private void setMacro(String JavaDoc text)
2903    throws IOException JavaDoc, SAXException JavaDoc
2904  {
2905    if (_reader == _macro) {
2906    }
2907    else if (_macro.getNext() == null) {
2908      _macro.init(this, _reader);
2909      _reader = _macro;
2910    }
2911    else {
2912      _macro = new MacroReader();
2913      _macro.init(this, _reader);
2914      _reader = _macro;
2915    }
2916    
2917    _macro.add(text);
2918  }
2919
2920  private int read()
2921    throws IOException JavaDoc, SAXException JavaDoc
2922  {
2923    int ch = _reader.read();
2924    while (ch < 0 && _reader.getNext() != null) {
2925      if (_stopOnIncludeEnd)
2926    return -1;
2927      
2928      popInclude();
2929      ch = _reader.read();
2930    }
2931    
2932    return ch;
2933  }
2934    
2935
2936  public void unread(int ch)
2937  {
2938    if (ch < 0) {
2939      return;
2940    }
2941    else if (_reader == _macro) {
2942    }
2943    else if (_macro.getNext() == null) {
2944      _macro.init(this, _reader);
2945      _reader = _macro;
2946    }
2947    else {
2948      _macro = new MacroReader();
2949      _macro.init(this, _reader);
2950      _reader = _macro;
2951    }
2952    
2953    _macro.prepend((char) ch);
2954  }
2955
2956  /**
2957   * Returns an error including the current line.
2958   *
2959   * @param text the error message text.
2960   */

2961  XmlParseException error(String JavaDoc text)
2962  {
2963    if (_errorHandler != null) {
2964      SAXParseException JavaDoc e = new SAXParseException JavaDoc(text, _locator);
2965
2966      try {
2967        _errorHandler.fatalError(e);
2968      } catch (SAXException JavaDoc e1) {
2969      }
2970    }
2971    
2972    return new XmlParseException(_filename + ":" + _line + ": " + text);
2973  }
2974
2975  public void free()
2976  {
2977  }
2978
2979  /**
2980   * Returns a user-readable string for an error character.
2981   */

2982  static String JavaDoc badChar(int ch)
2983  {
2984    if (ch < 0 || ch == 0xffff)
2985      return L.l("end of file");
2986    else if (ch == '\n' || ch == '\r')
2987      return L.l("end of line");
2988    else if (ch >= 0x20 && ch <= 0x7f)
2989      return "`" + (char) ch + "'";
2990    else
2991      return "`" + (char) ch + "' (\\u" + hex(ch) + ")";
2992  }
2993
2994  private void printDebugNode(WriteStream s, Node JavaDoc node, int depth)
2995    throws IOException JavaDoc
2996  {
2997    if (node == null)
2998      return;
2999
3000    for (int i = 0; i < depth; i++)
3001      s.print(' ');
3002
3003    if (node.getFirstChild() != null) {
3004      s.println("<" + node.getNodeName() + ">");
3005      for (Node JavaDoc child = node.getFirstChild();
3006       child != null;
3007       child = child.getNextSibling()) {
3008    printDebugNode(s, child, depth + 2);
3009      }
3010      for (int i = 0; i < depth; i++)
3011    s.print(' ');
3012      s.println("</" + node.getNodeName() + ">");
3013    }
3014    else
3015      s.println("<" + node.getNodeName() + "/>");
3016  }
3017
3018  public static class LocatorImpl implements ExtendedLocator {
3019    XmlParser _parser;
3020
3021    LocatorImpl(XmlParser parser)
3022    {
3023      _parser = parser;
3024    }
3025    
3026    public String JavaDoc getSystemId()
3027    {
3028      if (_parser._reader != null && _parser._reader.getSystemId() != null)
3029        return _parser._reader.getSystemId();
3030      else if (_parser.getSystemId() != null)
3031        return _parser.getSystemId();
3032      else if (_parser._reader != null && _parser._reader.getFilename() != null)
3033        return _parser._reader.getFilename();
3034      else if (_parser.getFilename() != null)
3035        return _parser.getFilename();
3036      else
3037        return null;
3038    }
3039    
3040    public String JavaDoc getFilename()
3041    {
3042      if (_parser._reader != null && _parser._reader.getFilename() != null)
3043        return _parser._reader.getFilename();
3044      else if (_parser.getFilename() != null)
3045        return _parser.getFilename();
3046      else if (_parser._reader != null && _parser._reader.getSystemId() != null)
3047        return _parser._reader.getSystemId();
3048      else if (_parser.getSystemId() != null)
3049        return _parser.getSystemId();
3050      else
3051        return null;
3052    }
3053    
3054    public String JavaDoc getPublicId()
3055    {
3056      if (_parser._reader != null)
3057        return _parser._reader.getPublicId();
3058      else
3059        return _parser.getPublicId();
3060    }
3061
3062    public int getLineNumber()
3063    {
3064      if (_parser._reader != null)
3065        return _parser._reader.getLine();
3066      else
3067        return _parser.getLineNumber();
3068    }
3069
3070    public int getColumnNumber()
3071    {
3072      return _parser.getColumnNumber();
3073    }
3074  }
3075}
3076
Popular Tags