KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > xml2 > XmlPrinter


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.xml2;
30
31 import com.caucho.java.LineMap;
32 import com.caucho.log.Log;
33 import com.caucho.util.CharBuffer;
34 import com.caucho.util.IntMap;
35 import com.caucho.util.L10N;
36 import com.caucho.vfs.EnclosedWriteStream;
37 import com.caucho.vfs.Path;
38 import com.caucho.vfs.Vfs;
39 import com.caucho.vfs.WriteStream;
40
41 import org.w3c.dom.*;
42 import org.xml.sax.Locator JavaDoc;
43
44 import java.io.IOException JavaDoc;
45 import java.io.OutputStream JavaDoc;
46 import java.io.UnsupportedEncodingException JavaDoc;
47 import java.io.Writer JavaDoc;
48 import java.util.ArrayList JavaDoc;
49 import java.util.HashMap JavaDoc;
50 import java.util.logging.Logger JavaDoc;
51
52 /**
53  * Controls printing of XML documents.
54  *
55  * Typical use:
56  * <code><pre>
57  * Node node = ...;
58  *
59  * OutputStream os = Vfs.openWrite("test.xml");
60  * XmlPrinter printer = new XmlPrinter(os);
61  *
62  * printer.printXml(node);
63  * </pre></code>
64  */

65 public class XmlPrinter implements XMLWriter {
66   static final Logger JavaDoc log = Log.open(XmlPrinter.class);
67   static final L10N L = new L10N(XmlPrinter.class);
68   
69   private static final int NO_PRETTY = 0;
70   private static final int INLINE = 1;
71   private static final int NO_INDENT = 2;
72   private static final int PRE = 3;
73
74   private static final char OMITTED_SPACE = 0;
75   private static final char OMITTED_NEWLINE = 1;
76   private static final char OMITTED = 2;
77   private static final char NULL_SPACE = 3;
78   private static final char SPACE = 4;
79   private static final char NEWLINE = 5;
80   private static final char WHITESPACE = 6;
81
82   private static final int ALWAYS_EMPTY = 1;
83   private static final int EMPTY_IF_EMPTY = 2;
84   
85   private static IntMap _empties;
86   private static HashMap JavaDoc<String JavaDoc,String JavaDoc> _booleanAttrs;
87   private static HashMap JavaDoc<String JavaDoc,String JavaDoc> _verbatimTags;
88   private static IntMap _prettyMap;
89
90   private WriteStream _os;
91   private char []_buffer = new char[256];
92   private int _capacity = _buffer.length;
93   private int _length;
94
95   boolean _isAutomaticMethod = true;
96   boolean _isTop = true;
97   boolean _isJsp = false;
98   String JavaDoc _encoding;
99   String JavaDoc _method;
100   boolean _isText;
101   boolean _isHtml;
102   boolean _inHead;
103   String JavaDoc _version;
104
105   boolean _isAutomaticPretty = true;
106   boolean _isPretty;
107   int _indent;
108   int _preCount;
109   int _lastTextChar = NULL_SPACE;
110   boolean _hasMetaContentType = false;
111   boolean _includeContentType = true;
112
113   boolean _printDeclaration;
114   
115   String JavaDoc _standalone;
116   String JavaDoc _systemId;
117   String JavaDoc _publicId;
118   private ExtendedLocator _locator;
119   
120   boolean _escapeText = true;
121   boolean _inVerbatim = false;
122   
123   private HashMap JavaDoc<String JavaDoc,String JavaDoc> _namespace;
124   private HashMap JavaDoc<String JavaDoc,String JavaDoc> _cdataElements;
125   private Entities _entities;
126   private String JavaDoc _mimeType;
127
128   private ArrayList JavaDoc<String JavaDoc> _prefixList;
129   
130   private ArrayList JavaDoc<String JavaDoc> _attributeNames = new ArrayList JavaDoc<String JavaDoc>();
131   private ArrayList JavaDoc<String JavaDoc> _attributeValues = new ArrayList JavaDoc<String JavaDoc>();
132
133   private char []_cbuf = new char[256];
134   private char []_abuf = new char[256];
135   
136   private LineMap _lineMap;
137   private int _line;
138   private String JavaDoc _srcFilename;
139   private int _srcLine;
140
141   private String JavaDoc _currentElement;
142   private Document _currentDocument;
143
144   private boolean _isEnclosedStream;
145
146   /**
147    * Create an XmlPrinter. Using this API, you'll need to use
148    * printer.init(os) to assign an output stream.
149    */

150   public XmlPrinter()
151   {
152   }
153
154   /**
155    * Creates a new XmlPrinter writing to an output stream.
156    *
157    * @param os output stream serving as the destination
158    */

159   public XmlPrinter(OutputStream JavaDoc os)
160   {
161     if (os instanceof WriteStream)
162       init((WriteStream) os);
163     else if (os instanceof EnclosedWriteStream)
164       init(((EnclosedWriteStream) os).getWriteStream());
165     else {
166       _isEnclosedStream = true;
167       WriteStream ws = Vfs.openWrite(os);
168       try {
169         ws.setEncoding("UTF-8");
170       } catch (UnsupportedEncodingException JavaDoc e) {
171       }
172       init(ws);
173     }
174   }
175
176   /**
177    * Creates a new XmlPrinter writing to a writer.
178    *
179    * @param writer destination of the serialized node
180    */

181   public XmlPrinter(Writer JavaDoc writer)
182   {
183     if (writer instanceof EnclosedWriteStream)
184       init(((EnclosedWriteStream) writer).getWriteStream());
185     else {
186       _isEnclosedStream = true;
187       WriteStream ws = Vfs.openWrite(writer);
188       init(ws);
189     }
190   }
191
192   /**
193    * Initialize the XmlPrinter with the write stream.
194    *
195    * @param os WriteStream containing the results.
196    */

197   public void init(WriteStream os)
198   {
199     _os = os;
200     init();
201   }
202
203   /**
204    * Initialize the XmlWriter in preparation for serializing a new XML.
205    */

206   void init()
207   {
208     String JavaDoc encoding = null;
209
210     if (_os != null)
211       encoding = _os.getEncoding();
212
213     _length = 0;
214
215     if (encoding == null ||
216     encoding.equals("US-ASCII") || encoding.equals("ISO-8859-1"))
217       _entities = XmlLatin1Entities.create();
218     else
219       _entities = XmlEntities.create();
220     _encoding = encoding;
221     _namespace = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
222     _line = 1;
223     _isTop = true;
224     _hasMetaContentType = false;
225     _attributeNames.clear();
226     _attributeValues.clear();
227   }
228
229   /**
230    * Prints the node as XML. The destination stream has already been
231    * set using init() or in the constructor.
232    *
233    * @param node source DOM node
234    */

235   public static void print(Path path, Node node)
236     throws IOException JavaDoc
237   {
238     WriteStream os = path.openWrite();
239
240     try {
241       new XmlPrinter(os).printXml(node);
242     } finally {
243       os.close();
244     }
245   }
246
247   /**
248    * Prints the node as XML. The destination stream has already been
249    * set using init() or in the constructor.
250    *
251    * @param node source DOM node
252    */

253   public void printXml(Node node)
254     throws IOException JavaDoc
255   {
256     _isAutomaticMethod = false;
257
258     ((QAbstractNode) node).print(this);
259
260     flush();
261   }
262
263   /**
264    * Prints the node and children as HTML
265    *
266    * @param node the top node to print
267    */

268   public void printHtml(Node node)
269     throws IOException JavaDoc
270   {
271     setMethod("html");
272     setVersion("4.0");
273
274     ((QAbstractNode) node).print(this);
275
276     flush();
277   }
278
279   /**
280    * Prints the node and children as XML, automatically indending
281    *
282    * @param node the top node to print
283    */

284   public void printPrettyXml(Node node)
285     throws IOException JavaDoc
286   {
287     _isAutomaticMethod = false;
288     setPretty(true);
289
290     ((QAbstractNode) node).print(this);
291
292     flush();
293   }
294
295   /**
296    * Prints the node as XML to a string.
297    *
298    * @param node the source node
299    * @return a string containing the XML.
300    */

301   public String JavaDoc printString(Node node)
302     throws IOException JavaDoc
303   {
304     CharBuffer cb = CharBuffer.allocate();
305
306     _os = Vfs.openWrite(cb);
307     init(_os);
308     try {
309       ((QAbstractNode) node).print(this);
310     } finally {
311       flush();
312       _os.close();
313     }
314
315     return cb.close();
316   }
317
318   /**
319    * Sets to true if XML entities like &lt; should be escaped as &amp;lt;.
320    * The default is true.
321    *
322    * @param escapeText set to true if entities should be escaped.
323    */

324   public void setEscaping(boolean escapeText)
325   {
326     if (! _isText)
327       _escapeText = escapeText;
328   }
329   
330   /**
331    * Returns the current XML escaping. If true, entities like &lt;
332    * will be escaped as &amp;lt;.
333    *
334    * @return true if entities should be escaped.
335    */

336   public boolean getEscaping()
337   {
338     return _escapeText;
339   }
340
341   /**
342    * Sets the output methods, like the XSL &lt;xsl:output method='method'/>.
343    */

344   public void setMethod(String JavaDoc method)
345   {
346     _method = method;
347
348     if (method == null) {
349       _isAutomaticMethod = true;
350       _isHtml = false;
351     } else if (method.equals("html")) {
352       _isAutomaticMethod = false;
353       _isHtml = true;
354       if (_isAutomaticPretty)
355         _isPretty = true;
356     } else if (method.equals("text")) {
357       _isAutomaticMethod = false;
358       _isText = true;
359       _escapeText = false;
360     } else {
361       _isAutomaticMethod = false;
362       _isHtml = false;
363     }
364   }
365   
366   /**
367    * Sets the XML/HTML version of the output file.
368    *
369    * @param version the output version
370    */

371   public void setVersion(String JavaDoc version)
372   {
373     _version = version;
374   }
375   
376   /**
377    * Sets the character set encoding for the output file.
378    *
379    * @param encoding the mime name of the output encoding
380    */

381   public void setEncoding(String JavaDoc encoding)
382   {
383     _encoding = encoding;
384     try {
385       if (encoding != null) {
386         _os.setEncoding(encoding);
387
388     if (encoding.equals("US-ASCII") || encoding.equals("ISO-8859-1"))
389       _entities = XmlLatin1Entities.create();
390     else
391       _entities = XmlEntities.create();
392       }
393     } catch (Exception JavaDoc e) {
394     }
395   }
396
397   public void setMimeType(String JavaDoc mimeType)
398   {
399     _mimeType = mimeType;
400     if (_method == null && mimeType != null && mimeType.equals("text/html"))
401       setMethod("html");
402   }
403
404   /**
405    * Set true if this is JSP special cased.
406    */

407   public void setJSP(boolean isJsp)
408   {
409     _isJsp = isJsp;
410   }
411
412   /**
413    * True if this is JSP special cased.
414    */

415   public boolean isJSP()
416   {
417     return _isJsp;
418   }
419
420   /**
421    * Returns true if the printer is printing HTML.
422    */

423   boolean isHtml()
424   {
425     return _isHtml;
426   }
427
428   /**
429    * Set to true if the printer should add whitespace to 'pretty-print'
430    * the output.
431    *
432    * @param isPretty if true, add spaces for printing
433    */

434   public void setPretty(boolean isPretty)
435   {
436     _isPretty = isPretty;
437     _isAutomaticPretty = false;
438   }
439
440   /**
441    * Returns true if the printer is currently pretty-printing the output.
442    */

443   public boolean isPretty()
444   {
445     return _isPretty;
446   }
447   
448   public void setPrintDeclaration(boolean printDeclaration)
449   {
450     _printDeclaration = printDeclaration;
451   }
452   
453   boolean getPrintDeclaration()
454   {
455     return _printDeclaration;
456   }
457   
458   public void setStandalone(String JavaDoc standalone)
459   {
460     _standalone = standalone;
461   }
462   
463   String JavaDoc getStandalone()
464   {
465     return _standalone;
466   }
467   
468   public void setSystemId(String JavaDoc id)
469   {
470     _systemId = id;
471   }
472   
473   String JavaDoc getSystemId()
474   {
475     return _systemId;
476   }
477
478   /**
479    * Set true if the printer should automatically add the
480    * &lt;meta content-type> to HTML.
481    */

482   public void setIncludeContentType(boolean include)
483   {
484     _includeContentType = include;
485   }
486   
487   /**
488    * Return true if the printer should automatically add the
489    * &lt;meta content-type> to HTML.
490    */

491   public boolean getIncludeContentType()
492   {
493     return _includeContentType;
494   }
495   
496   public void setPublicId(String JavaDoc id)
497   {
498     _publicId = id;
499   }
500   
501   String JavaDoc getPublicId()
502   {
503     return _publicId;
504   }
505
506   public Path getPath()
507   {
508     if (_os instanceof WriteStream)
509       return ((WriteStream) _os).getPath();
510     else
511       return null;
512   }
513
514   /**
515    * Creates a new line map.
516    */

517   public void setLineMap(String JavaDoc filename)
518   {
519     _lineMap = new LineMap(filename);
520   }
521
522   public LineMap getLineMap()
523   {
524     return _lineMap;
525   }
526
527   public void addCdataElement(String JavaDoc elt)
528   {
529     if (_cdataElements == null)
530       _cdataElements = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
531     _cdataElements.put(elt, "");
532   }
533
534   public void print(Node node)
535     throws IOException JavaDoc
536   {
537     if (node instanceof QAbstractNode)
538       ((QAbstractNode) node).print(this);
539     else {
540       printNode(node);
541     }
542     
543     if (_isEnclosedStream)
544       _os.flush();
545   }
546
547   public void printNode(Node node)
548     throws IOException JavaDoc
549   {
550     if (node == null)
551       return;
552
553     switch (node.getNodeType()) {
554     case Node.DOCUMENT_NODE:
555       startDocument((Document) node);
556       for (Node child = node.getFirstChild();
557            child != null;
558            child = child.getNextSibling())
559         printNode(child);
560       endDocument();
561       break;
562       
563     case Node.ELEMENT_NODE: {
564       Element elt = (Element) node;
565       
566       startElement(elt.getNamespaceURI(),
567                    elt.getLocalName(),
568                    elt.getNodeName());
569
570       NamedNodeMap attrs = elt.getAttributes();
571       int len = attrs.getLength();
572       for (int i = 0; i < len; i++) {
573         Attr attr = (Attr) attrs.item(i);
574
575         attribute(attr.getNamespaceURI(),
576                   attr.getLocalName(),
577                   attr.getNodeName(),
578                   attr.getNodeValue());
579       }
580       
581       for (Node child = node.getFirstChild();
582            child != null;
583            child = child.getNextSibling()) {
584         printNode(child);
585       }
586       endElement(elt.getNamespaceURI(), elt.getLocalName(), elt.getNodeName());
587       break;
588     }
589     
590     case Node.TEXT_NODE:
591     case Node.CDATA_SECTION_NODE:
592     {
593       CharacterData text = (CharacterData) node;
594       text(text.getData());
595       break;
596     }
597     
598     case Node.COMMENT_NODE:
599     {
600       Comment comment = (Comment) node;
601       comment(comment.getData());
602       break;
603     }
604     
605     case Node.PROCESSING_INSTRUCTION_NODE:
606     {
607       ProcessingInstruction pi = (ProcessingInstruction) node;
608       processingInstruction(pi.getNodeName(), pi.getData());
609       break;
610     }
611     }
612   }
613
614   WriteStream getStream()
615   {
616     return _os;
617   }
618
619   public void startDocument(Document document)
620     throws IOException JavaDoc
621   {
622     _currentDocument = document;
623
624     startDocument();
625   }
626
627   /**
628    * Callback when the document starts printing.
629    */

630   public void startDocument()
631     throws IOException JavaDoc
632   {
633     _isTop = true;
634   }
635
636   /**
637    * Callback when the document completes
638    */

639   public void endDocument()
640     throws IOException JavaDoc
641   {
642     if (_isPretty && _lastTextChar < SPACE)
643       println();
644
645     flush();
646   }
647
648   /**
649    * Sets the locator.
650    */

651   public void setDocumentLocator(Locator JavaDoc locator)
652   {
653     _locator = (ExtendedLocator) locator;
654   }
655   
656   /**
657    * Sets the current location.
658    *
659    * @param filename the source filename
660    * @param line the source line
661    * @param column the source column
662    */

663   public void setLocation(String JavaDoc filename, int line, int column)
664   {
665     _srcFilename = filename;
666     _srcLine = line;
667   }
668
669   /**
670    * Called at the start of a new element.
671    *
672    * @param url the namespace url
673    * @param localName the local name
674    * @param qName the qualified name
675    */

676   public void startElement(String JavaDoc url, String JavaDoc localName, String JavaDoc qName)
677     throws IOException JavaDoc
678   {
679     if (_isText)
680       return;
681     
682     if (_isAutomaticMethod) {
683       _isHtml = (qName.equalsIgnoreCase("html") &&
684                  (url == null || url.equals("")));
685       
686       if (_isAutomaticPretty)
687         _isPretty = _isHtml;
688
689       _isAutomaticMethod = false;
690     }
691
692     if (_isTop)
693       printHeader(qName);
694
695     if (_currentElement != null)
696       completeOpenTag();
697
698     _attributeNames.clear();
699     _attributeValues.clear();
700
701     if (_isHtml && _verbatimTags.get(qName.toLowerCase()) != null)
702       _inVerbatim = true;
703
704     if (_isPretty && _preCount <= 0)
705       printPrettyStart(qName);
706
707     if (_lineMap == null) {
708     }
709     else if (_locator != null) {
710       _lineMap.add(_locator.getFilename(), _locator.getLineNumber(), _line);
711     }
712     else if (_srcFilename != null)
713       _lineMap.add(_srcFilename, _srcLine, _line);
714     
715     print('<');
716     print(qName);
717     _currentElement = qName;
718     _lastTextChar = NULL_SPACE;
719   }
720
721   /**
722    * Prints the header, if necessary.
723    *
724    * @param top name of the top element
725    */

726   public void printHeader(String JavaDoc top)
727     throws IOException JavaDoc
728   {
729     if (! _isTop)
730       return;
731     
732     _isTop = false;
733     
734     String JavaDoc encoding = _encoding;
735
736     if (encoding != null && encoding.equalsIgnoreCase("UTF-16"))
737       print('\ufeff');
738     
739     if (_isHtml) {
740       double dVersion = 4.0;
741       
742       if (_version == null || _version.compareTo("4.0") >= 0) {
743       }
744       else {
745         dVersion = 3.2;
746       }
747
748       if (_systemId != null || _publicId != null)
749         printDoctype("html");
750
751       if (encoding == null || encoding.equalsIgnoreCase("ISO-8859-1"))
752     // _entities = Latin1Entities.create(dVersion);
753
_entities = HtmlEntities.create(dVersion);
754       else if (encoding.equalsIgnoreCase("US-ASCII"))
755         _entities = HtmlEntities.create(dVersion);
756       else
757         _entities = OtherEntities.create(dVersion);
758     }
759     else {
760       if (_printDeclaration) {
761         String JavaDoc version = _version;
762
763         if (version == null)
764           version = "1.0";
765
766         print("<?xml version=\"");
767         print(version);
768         print("\"");
769
770     if (encoding == null ||
771         encoding.equals("") ||
772         encoding.equalsIgnoreCase("UTF-16") ||
773         encoding.equalsIgnoreCase("US-ASCII")) {
774     }
775     else
776           print(" encoding=\"" + encoding + "\"");
777           
778         if (_standalone != null &&
779             (_standalone.equals("true") || _standalone.equals("yes")))
780           print(" standalone=\"yes\"");
781
782         println("?>");
783       }
784
785       printDoctype(top);
786
787       if (encoding == null ||
788           encoding.equalsIgnoreCase("US-ASCII") ||
789       encoding.equalsIgnoreCase("ISO-8859-1"))
790         _entities = XmlLatin1Entities.create();
791       else
792         _entities = XmlEntities.create();
793     }
794     
795     _lastTextChar = NEWLINE;
796   }
797
798   /**
799    * Prints the doctype declaration
800    *
801    * @param topElt name of the top element
802    */

803   private void printDoctype(String JavaDoc topElt)
804     throws IOException JavaDoc
805   {
806     if (_publicId != null && _systemId != null)
807       println("<!DOCTYPE " + topElt + " PUBLIC \"" + _publicId + "\" \"" +
808               _systemId + "\">");
809     else if (_publicId != null)
810       println("<!DOCTYPE " + topElt + " PUBLIC \"" + _publicId + "\">");
811     else if (_systemId != null)
812       println("<!DOCTYPE " + topElt + " SYSTEM \"" + _systemId + "\">");
813     else if (_currentDocument instanceof QDocument) {
814       QDocumentType dtd = (QDocumentType) _currentDocument.getDoctype();
815
816       if (dtd != null && dtd.getName() != null && dtd.getParentNode() == null) {
817         dtd.print(this);
818         println();
819       }
820     }
821   }
822
823   public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri)
824     throws IOException JavaDoc
825   {
826   }
827   
828   public void endPrefixMapping(String JavaDoc prefix)
829     throws IOException JavaDoc
830   {
831   }
832
833   /**
834    * Pretty printing for a start tag.
835    *
836    * @param qName the name of the element
837    */

838   private void printPrettyStart(String JavaDoc qName)
839     throws IOException JavaDoc
840   {
841     int code = _isHtml ? _prettyMap.get(qName.toLowerCase()) : -1;
842
843     if (code == NO_PRETTY) {
844       if (_lastTextChar == OMITTED_NEWLINE)
845         println();
846       else if (_lastTextChar == OMITTED_SPACE)
847         print(' ');
848     }
849     else if (code != INLINE && _lastTextChar < WHITESPACE) {
850       if (_lastTextChar != NEWLINE)
851         println();
852       for (int i = 0; i < _indent; i++)
853         print(' ');
854     }
855     else if (code == INLINE && _lastTextChar < WHITESPACE) {
856       if (_lastTextChar == OMITTED_NEWLINE)
857         println();
858       else if (_lastTextChar == OMITTED_SPACE)
859         print(' ');
860     }
861
862     if (! _isHtml || code < 0) {
863       _indent += 2;
864     }
865
866     if (code == PRE) {
867       _preCount++;
868       _lastTextChar = 'a';
869     }
870     else if (code == NO_PRETTY || code == INLINE)
871       _lastTextChar = 'a';
872     else
873       _lastTextChar = NULL_SPACE;
874   }
875
876   /**
877    * Prints an attribute
878    *
879    * @param uri namespace uri
880    * @param localName localname of the attribute
881    * @param qName qualified name of the attribute
882    * @param value value of the attribute.
883    */

884   public void attribute(String JavaDoc uri, String JavaDoc localName, String JavaDoc qName,
885                         String JavaDoc value)
886     throws IOException JavaDoc
887   {
888     if (_isText)
889       return;
890
891     if (_currentElement != null) {
892     }
893     else if (qName.equals("encoding")) {
894       _encoding = value;
895       return;
896     }
897     else if (qName.startsWith("xmlns")) {
898     }
899     else
900       throw new IOException JavaDoc(L.l("attribute `{0}' outside element.", qName));
901     
902     qName = qName.intern();
903
904     if (qName.startsWith("xmlns")) {
905       if (localName == null)
906         localName = "";
907
908       if (_isHtml && localName.equals("") && value.equals(""))
909         return;
910
911       _namespace.put(localName, value);
912       if (_prefixList == null)
913         _prefixList = new ArrayList JavaDoc<String JavaDoc>();
914       if (! _prefixList.contains(localName))
915         _prefixList.add(localName);
916       return;
917     }
918     else if (qName.equals("xtp:jsp-attribute")) {
919       _attributeNames.add("<%= " + value + "%>");
920       _attributeValues.add(null);
921       return;
922     }
923     
924     if (_isHtml && ! _hasMetaContentType &&
925         _currentElement.equals("meta") &&
926         qName.equalsIgnoreCase("http-equiv") &&
927         value.equalsIgnoreCase("content-type")) {
928       _hasMetaContentType = true;
929     }
930
931     for (int i = 0; i < _attributeNames.size(); i++) {
932       String JavaDoc oldName = _attributeNames.get(i);
933
934       if (oldName == qName) {
935         _attributeValues.set(i, value);
936         return;
937       }
938     }
939
940     if (qName == null || qName.equals(""))
941       throw new NullPointerException JavaDoc();
942
943     _attributeNames.add(qName);
944     _attributeValues.add(value);
945   }
946
947   /**
948    * Complete printing of the attributes when the open tag completes.
949    */

950   public boolean finishAttributes()
951     throws IOException JavaDoc
952   {
953     if (_currentElement == null)
954       return false;
955
956     for (int i = 0; i < _attributeNames.size(); i++) {
957       String JavaDoc qName = _attributeNames.get(i);
958       String JavaDoc value = _attributeValues.get(i);
959       
960       if (_isHtml &&
961           _booleanAttrs.get(qName.toLowerCase()) != null &&
962           (value == null || value.equals("") || value.equals(qName))) {
963         print(' ');
964         print(qName);
965       }
966       else {
967         print(' ');
968         print(qName);
969
970         if (value != null) {
971           print("=\"");
972
973           if (! _escapeText || _inVerbatim)
974             print(value);
975           /*
976           else if (isHtml && isURIAttribute(currentElement, qName)) {
977             int len = value.length();
978             int offset = 0;
979
980             while (len > abuf.length) {
981               value.getChars(offset, offset + abuf.length, abuf, 0);
982               entities.printURIAttr(this, abuf, 0, abuf.length);
983               len -= abuf.length;
984               offset += abuf.length;
985             }
986             
987             value.getChars(offset, offset + len, abuf, 0);
988             entities.printURIAttr(this, abuf, 0, len);
989           }
990           */

991           else {
992             int len = value.length();
993             int offset = 0;
994
995             while (len > _abuf.length) {
996               value.getChars(offset, offset + _abuf.length, _abuf, 0);
997               _entities.printText(this, _abuf, 0, _abuf.length, true);
998               len -= _abuf.length;
999               offset += _abuf.length;
1000            }
1001            
1002            value.getChars(offset, offset + len, _abuf, 0);
1003            _entities.printText(this, _abuf, 0, len, true);
1004          }
1005          print('\"');
1006        }
1007        else if (! _isHtml) {
1008          print("=\"\"");
1009        }
1010      }
1011    }
1012
1013    if (_prefixList != null && _prefixList.size() > 0) {
1014      for (int i = 0; i < _prefixList.size(); i++) {
1015        String JavaDoc prefix = _prefixList.get(i);
1016        String JavaDoc url = _namespace.get(prefix);
1017
1018        if (prefix.equals("")) {
1019          print(" xmlns=\"");
1020          print(url);
1021          print('\"');
1022        }
1023        else if (prefix.startsWith("xmlns")) {
1024          print(" ");
1025          print(prefix);
1026          print("=\"");
1027          print(url);
1028          print('\"');
1029        }
1030        else {
1031          print(" xmlns:");
1032          print(prefix);
1033          print("=\"");
1034          print(url);
1035          print('\"');
1036        }
1037      }
1038      
1039      _prefixList.clear();
1040      _namespace.clear();
1041    }
1042    
1043    _currentElement = null;
1044    // lastTextChar = NULL_SPACE;
1045

1046    return true;
1047  }
1048
1049  /**
1050   * Prints the end tag of an element
1051   *
1052   * @param uri the namespace uri of the element
1053   * @param localName the localname of the element tag
1054   * @param qName qualified name of the element
1055   */

1056  public void endElement(String JavaDoc uri, String JavaDoc localName, String JavaDoc qName)
1057    throws IOException JavaDoc
1058  {
1059    if (_isText)
1060      return;
1061
1062    String JavaDoc normalName = _isHtml ? qName.toLowerCase() : qName;
1063    
1064    if (_isHtml && _verbatimTags.get(normalName) != null)
1065      _inVerbatim = false;
1066
1067    int prevTextChar = _lastTextChar;
1068    boolean isEmpty = _currentElement != null;
1069    if (_currentElement != null)
1070      finishAttributes();
1071      
1072    if (! _isHtml || _hasMetaContentType) {
1073    }
1074    else if (normalName.equals("head")) {
1075      if (isEmpty)
1076        print(">");
1077      isEmpty = false;
1078      printMetaContentType();
1079      _currentElement = null;
1080    }
1081      
1082    if (isEmpty) {
1083      if (_isHtml && _empties.get(normalName) >= 0)
1084        print(">");
1085      else if (prevTextChar <= OMITTED) {
1086        print(">");
1087        printPrettyEnd(qName);
1088        print("</");
1089        print(qName);
1090        print(">");
1091        return;
1092      }
1093      else if (_isHtml) {
1094        print("></");
1095        print(qName);
1096        print(">");
1097      }
1098      else {
1099        print("/>");
1100      }
1101
1102      if (_isPretty)
1103        closePretty(qName);
1104    }
1105    else if (_isHtml && _empties.get(normalName) >= 0 &&
1106             ! normalName.equals("p")) {
1107      if (_isPretty)
1108        closePretty(qName);
1109    }
1110    else if (_isPretty) {
1111      printPrettyEnd(qName);
1112      print("</");
1113      print(qName);
1114      print(">");
1115    }
1116    else {
1117      print("</");
1118      print(qName);
1119      print(">");
1120    }
1121    
1122    _currentElement = null;
1123  }
1124
1125  /**
1126   * Handle pretty printing at an end tag.
1127   */

1128  private void printPrettyEnd(String JavaDoc qName)
1129    throws IOException JavaDoc
1130  {
1131    int code = _isHtml ? _prettyMap.get(qName.toLowerCase()) : -1;
1132
1133    if (code == PRE) {
1134      _preCount--;
1135      _lastTextChar = NULL_SPACE;
1136      return;
1137    }
1138    else if (_preCount > 0) {
1139      return;
1140    }
1141    else if (code == NO_PRETTY) {
1142      if (_lastTextChar <= OMITTED)
1143        println();
1144      _lastTextChar = 'a';
1145      // indent -= 2;
1146
return;
1147    }
1148    else if (code == INLINE) {
1149      _lastTextChar = NULL_SPACE;
1150      return;
1151    }
1152    
1153    if (! _isHtml || code < 0) {
1154      _indent -= 2;
1155    }
1156
1157    if (_lastTextChar <= WHITESPACE) {
1158      if (_lastTextChar != NEWLINE)
1159        println();
1160      for (int i = 0; i < _indent; i++)
1161        print(' ');
1162    }
1163    _lastTextChar = NULL_SPACE;
1164  }
1165
1166  /**
1167   * Handle the pretty printing after the closing of a tag.
1168   */

1169  private void closePretty(String JavaDoc qName)
1170  {
1171    int code = _isHtml ? _prettyMap.get(qName.toLowerCase()) : -1;
1172
1173    if (code == PRE) {
1174      _preCount--;
1175      _lastTextChar = NULL_SPACE;
1176      return;
1177    }
1178    if (_preCount > 0)
1179      return;
1180    
1181    if (! _isHtml || code < 0) {
1182      _indent -= 2;
1183    }
1184    
1185    if (code != NO_PRETTY)
1186      _lastTextChar = NULL_SPACE;
1187    else
1188      _lastTextChar = 'a';
1189  }
1190
1191  /**
1192   * Prints a processing instruction
1193   *
1194   * @param name the name of the processing instruction
1195   * @param data the processing instruction data
1196   */

1197  public void processingInstruction(String JavaDoc name, String JavaDoc data)
1198    throws IOException JavaDoc
1199  {
1200    if (_isText)
1201      return;
1202    
1203    if (_currentElement != null)
1204      completeOpenTag();
1205
1206    if (_isTop && ! _isHtml && ! _isAutomaticMethod) {
1207      printHeader(null);
1208      _isTop = false;
1209    }
1210
1211    print("<?");
1212    print(name);
1213
1214    if (data != null && data.length() > 0) {
1215      print(" ");
1216      print(data);
1217    }
1218
1219    if (isHtml())
1220      print(">");
1221    else
1222      print("?>");
1223    
1224    _lastTextChar = NULL_SPACE;
1225  }
1226
1227  /**
1228   * Prints a comment
1229   *
1230   * @param data the comment data
1231   */

1232  public void comment(String JavaDoc data)
1233    throws IOException JavaDoc
1234  {
1235    if (_isText)
1236      return;
1237
1238    int textChar = _lastTextChar;
1239    
1240    if (_currentElement != null)
1241      completeOpenTag();
1242
1243    if (_isPretty && _preCount <= 0 &&
1244        (textChar == OMITTED_NEWLINE || textChar == NULL_SPACE)) {
1245      println();
1246      
1247      for (int i = 0; i < _indent; i++)
1248        print(' ');
1249    }
1250
1251    print("<!--");
1252    print(data);
1253    print("-->");
1254    
1255    _lastTextChar = NULL_SPACE;
1256  }
1257
1258  /**
1259   * Returns true if the text is currently being escaped
1260   */

1261  public boolean getEscapeText()
1262  {
1263    return _escapeText;
1264  }
1265
1266  /**
1267   * Sets true if the text should be escaped, else it will be printed
1268   * verbatim.
1269   */

1270  public void setEscapeText(boolean isEscaped)
1271  {
1272    _escapeText = isEscaped;
1273  }
1274  
1275  /**
1276   * Prints text. If the text is escaped, codes like &lt; will be printed as
1277   * &amp;lt;.
1278   */

1279  public void text(String JavaDoc text)
1280    throws IOException JavaDoc
1281  {
1282    int length = text.length();
1283    
1284    for (int offset = 0; offset < length; offset += _cbuf.length) {
1285      int sublen = length - offset;
1286      if (sublen > _cbuf.length)
1287        sublen = _cbuf.length;
1288
1289      text.getChars(offset, offset + sublen, _cbuf, 0);
1290      text(_cbuf, 0, sublen);
1291    }
1292  }
1293
1294  /**
1295   * Prints text. If the text is escaped, codes like &lt; will be printed as
1296   * &amp;lt;.
1297   */

1298  public void text(char []buffer, int offset, int length)
1299    throws IOException JavaDoc
1300  {
1301    if (length == 0)
1302      return;
1303
1304    int prevTextChar = _lastTextChar;
1305    if ((_isPretty && _preCount <= 0 || _isTop) && ! _isText &&
1306        trimPrettyWhitespace(buffer, offset, length)) {
1307      if (prevTextChar <= WHITESPACE)
1308        return;
1309      if (_lastTextChar == OMITTED_SPACE)
1310        _lastTextChar = SPACE;
1311      if (_lastTextChar == OMITTED_NEWLINE)
1312        _lastTextChar = NEWLINE;
1313    }
1314
1315    int nextTextChar = _lastTextChar;
1316    if (_currentElement != null) {
1317      completeOpenTag();
1318      
1319      if (_isPretty && _preCount <= 0 && prevTextChar <= OMITTED)
1320        println();
1321    }
1322
1323    _lastTextChar = nextTextChar;
1324
1325    if (! _isTop) {
1326    }
1327    else if (! _isJsp) {
1328      _isTop = false;
1329    }
1330    else if (_isAutomaticMethod) {
1331    }
1332    else if (! _isHtml) {
1333      printHeader(null);
1334      _isTop = false;
1335    }
1336    
1337    if (_isHtml && ! _hasMetaContentType && ! _inHead) {
1338      int textChar = _lastTextChar;
1339      
1340      if (_isPretty && _preCount <= 0 && prevTextChar <= OMITTED)
1341        println();
1342      
1343      // printHeadContentType();
1344
_lastTextChar = textChar;
1345      prevTextChar = 'a';
1346    }
1347    
1348    if (! _isPretty || _preCount > 0) {
1349    }
1350    else if (prevTextChar == OMITTED_NEWLINE) {
1351      if (buffer[offset] != '\n')
1352        println();
1353    }
1354    else if (prevTextChar == OMITTED_SPACE) {
1355      char ch = buffer[offset];
1356      
1357      if (ch != ' ' && ch != '\n')
1358        print(' ');
1359    }
1360
1361    if (_lineMap == null) {
1362    }
1363    else if (_locator != null) {
1364      _lineMap.add(_locator.getFilename(), _locator.getLineNumber(), _line);
1365    }
1366    else if (_srcFilename != null)
1367      _lineMap.add(_srcFilename, _srcLine, _line);
1368
1369    if (! _escapeText || _inVerbatim || _entities == null)
1370      print(buffer, offset, length);
1371    else
1372      _entities.printText(this, buffer, offset, length, false);
1373  }
1374
1375  /**
1376   * If the text is completely whitespace, skip it.
1377   */

1378  boolean trimPrettyWhitespace(char []buffer, int offset, int length)
1379  {
1380    char textChar = 'a';
1381    int i = length - 1;
1382
1383    for (; i >= 0; i--) {
1384      char ch = buffer[offset + i];
1385
1386      if (ch == '\r' || ch == '\n') {
1387        if (textChar != NEWLINE)
1388          textChar = OMITTED_NEWLINE;
1389      }
1390      else if (ch == ' ' || ch == '\t') {
1391        if (textChar == 'a' || textChar == NULL_SPACE)
1392          textChar = OMITTED_SPACE;
1393      }
1394      else if (textChar == OMITTED_NEWLINE) {
1395        textChar = NEWLINE;
1396        break;
1397      }
1398      else if (textChar == OMITTED_SPACE) {
1399        textChar = SPACE;
1400        break;
1401      }
1402      else
1403        break;
1404    }
1405
1406    _lastTextChar = textChar;
1407
1408    return (i < 0 && textChar <= WHITESPACE);
1409  }
1410
1411  public void cdata(String JavaDoc text)
1412    throws IOException JavaDoc
1413  {
1414    if (text.length() == 0)
1415      return;
1416    
1417    _isTop = false;
1418    
1419    if (_currentElement != null)
1420      completeOpenTag();
1421
1422    if (_lineMap != null && _srcFilename != null)
1423      _lineMap.add(_srcFilename, _srcLine, _line);
1424
1425    print("<![CDATA[");
1426
1427    print(text);
1428    
1429    print("]]>");
1430    
1431    _lastTextChar = NEWLINE;
1432  }
1433
1434  private void completeOpenTag()
1435    throws IOException JavaDoc
1436  {
1437    boolean isHead = (_isHtml && ! _hasMetaContentType &&
1438                      _currentElement.equalsIgnoreCase("head"));
1439    
1440    finishAttributes();
1441    print(">");
1442
1443    if (isHead)
1444      printHeadContentType();
1445  }
1446
1447  public void cdata(char []buffer, int offset, int length)
1448    throws IOException JavaDoc
1449  {
1450    cdata(new String JavaDoc(buffer, offset, length));
1451  }
1452
1453  private void printHeadContentType()
1454    throws IOException JavaDoc
1455  {
1456    printMetaContentType();
1457  }
1458
1459  private void printMetaContentType()
1460    throws IOException JavaDoc
1461  {
1462    if (! _includeContentType)
1463      return;
1464    
1465    _hasMetaContentType = true;
1466    if (_lastTextChar != NEWLINE)
1467      println();
1468
1469    if (_encoding == null || _encoding.equals("US-ASCII"))
1470      _encoding = "ISO-8859-1";
1471    String JavaDoc mimeType = _mimeType;
1472    if (mimeType == null)
1473      mimeType = "text/html";
1474
1475    println(" <meta http-equiv=\"Content-Type\" content=\"" +
1476            mimeType + "; charset=" + _encoding + "\">");
1477    _lastTextChar = NEWLINE;
1478  }
1479
1480  void printDecl(String JavaDoc text) throws IOException JavaDoc
1481  {
1482    for (int i = 0; i < text.length(); i++) {
1483      char ch = text.charAt(i);
1484
1485      switch (ch) {
1486      case '&':
1487    if (i + 1 < text.length() && text.charAt(i + 1) == '#')
1488      print("&#38;");
1489    else
1490      print(ch);
1491    break;
1492
1493      case '"':
1494    print("&#34;");
1495    break;
1496
1497      case '\'':
1498    print("&#39;");
1499    break;
1500
1501      case '\n':
1502        print("\n");
1503        break;
1504
1505      default:
1506    print(ch);
1507      }
1508    }
1509  }
1510
1511  /**
1512   * Prints a newline to the output stream.
1513   */

1514  void println() throws IOException JavaDoc
1515  {
1516    print('\n');
1517  }
1518
1519  void println(String JavaDoc text) throws IOException JavaDoc
1520  {
1521    print(text);
1522    println();
1523  }
1524
1525  /**
1526   * Prints a char buffer.
1527   */

1528  void print(char []buf)
1529    throws IOException JavaDoc
1530  {
1531    print(buf, 0, buf.length);
1532  }
1533  
1534  /**
1535   * Prints a char buffer.
1536   */

1537  void print(char []buf, int off, int len)
1538    throws IOException JavaDoc
1539  {
1540    for (int i = 0; i < len; i++)
1541      print(buf[off + i]);
1542  }
1543
1544  /**
1545   * Prints a chunk of text.
1546   */

1547  void print(String JavaDoc text) throws IOException JavaDoc
1548  {
1549    int len = text.length();
1550
1551    for (int i = 0; i < len; i++) {
1552      char ch = text.charAt(i);
1553      print(ch);
1554    }
1555  }
1556
1557  /**
1558   * Prints a character.
1559   */

1560  void print(char ch) throws IOException JavaDoc
1561  {
1562    if (_capacity <= _length) {
1563      _os.print(_buffer, 0, _length);
1564      _length = 0;
1565    }
1566
1567    _buffer[_length++] = ch;
1568    if (ch == '\n')
1569      _line++;
1570  }
1571
1572  /**
1573   * Prints an integer to the output stream.
1574   */

1575  void print(int i) throws IOException JavaDoc
1576  {
1577    if (i < 0) {
1578    }
1579    else if (i < 10) {
1580      print((char) ('0' + i));
1581      return;
1582    }
1583    else if (i < 100) {
1584      print((char) ('0' + i / 10));
1585      print((char) ('0' + i % 10));
1586      return;
1587    }
1588    
1589    if (_length >= 0) {
1590      _os.print(_buffer, 0, _length);
1591      _length = 0;
1592    }
1593
1594    _os.print(i);
1595  }
1596
1597  private void flush() throws IOException JavaDoc
1598  {
1599    if (_length >= 0) {
1600      _os.print(_buffer, 0, _length);
1601      _length = 0;
1602    }
1603
1604    if (_isEnclosedStream)
1605      _os.flush();
1606  }
1607
1608  private void close() throws IOException JavaDoc
1609  {
1610    flush();
1611    
1612    if (_isEnclosedStream)
1613      _os.close();
1614  }
1615    
1616
1617  static void add(IntMap map, String JavaDoc name, int value)
1618  {
1619    map.put(name, value);
1620    map.put(name.toUpperCase(), value);
1621  }
1622
1623  static void add(HashMap JavaDoc<String JavaDoc,String JavaDoc> map, String JavaDoc name)
1624  {
1625    map.put(name, name);
1626    map.put(name.toUpperCase(), name);
1627  }
1628
1629  static {
1630    _empties = new IntMap();
1631    add(_empties, "basefont", ALWAYS_EMPTY);
1632    add(_empties, "br", ALWAYS_EMPTY);
1633    add(_empties, "area", ALWAYS_EMPTY);
1634    add(_empties, "link", ALWAYS_EMPTY);
1635    add(_empties, "img", ALWAYS_EMPTY);
1636    add(_empties, "param", ALWAYS_EMPTY);
1637    add(_empties, "hr", ALWAYS_EMPTY);
1638    add(_empties, "input", ALWAYS_EMPTY);
1639    add(_empties, "col", ALWAYS_EMPTY);
1640    add(_empties, "frame", ALWAYS_EMPTY);
1641    add(_empties, "isindex", ALWAYS_EMPTY);
1642    add(_empties, "base", ALWAYS_EMPTY);
1643    add(_empties, "meta", ALWAYS_EMPTY);
1644    
1645    add(_empties, "p", ALWAYS_EMPTY);
1646    add(_empties, "li", ALWAYS_EMPTY);
1647    
1648    add(_empties, "option", EMPTY_IF_EMPTY);
1649    
1650    _booleanAttrs = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
1651    // input
1652
add(_booleanAttrs, "checked");
1653    // dir, menu, dl, ol, ul
1654
add(_booleanAttrs, "compact");
1655    // object
1656
add(_booleanAttrs, "declare");
1657    // script
1658
add(_booleanAttrs, "defer");
1659    // button, input, optgroup, option, select, textarea
1660
add(_booleanAttrs, "disabled");
1661    // img
1662
add(_booleanAttrs, "ismap");
1663    // select
1664
add(_booleanAttrs, "multiple");
1665    // area
1666
add(_booleanAttrs, "nohref");
1667    // frame
1668
add(_booleanAttrs, "noresize");
1669    // hr
1670
add(_booleanAttrs, "noshade");
1671    // td, th
1672
add(_booleanAttrs, "nowrap");
1673    // textarea, input
1674
add(_booleanAttrs, "readonly");
1675    // option
1676
add(_booleanAttrs, "selected");
1677    
1678    _prettyMap = new IntMap();
1679    // next two break browsers
1680
add(_prettyMap, "img", NO_PRETTY);
1681    add(_prettyMap, "a", NO_PRETTY);
1682    add(_prettyMap, "embed", NO_PRETTY);
1683    add(_prettyMap, "th", NO_PRETTY);
1684    add(_prettyMap, "td", NO_PRETTY);
1685    // inline tags look better without the indent
1686
add(_prettyMap, "tt", INLINE);
1687    add(_prettyMap, "i", INLINE);
1688    add(_prettyMap, "b", INLINE);
1689    add(_prettyMap, "big", INLINE);
1690    add(_prettyMap, "em", INLINE);
1691    add(_prettyMap, "string", INLINE);
1692    add(_prettyMap, "dfn", INLINE);
1693    add(_prettyMap, "code", INLINE);
1694    add(_prettyMap, "samp", INLINE);
1695    add(_prettyMap, "kbd", INLINE);
1696    add(_prettyMap, "var", INLINE);
1697    add(_prettyMap, "cite", INLINE);
1698    add(_prettyMap, "abbr", INLINE);
1699    add(_prettyMap, "acronym", INLINE);
1700    add(_prettyMap, "object", INLINE);
1701    add(_prettyMap, "q", INLINE);
1702    add(_prettyMap, "sub", INLINE);
1703    add(_prettyMap, "sup", INLINE);
1704    add(_prettyMap, "font", INLINE);
1705    add(_prettyMap, "small", INLINE);
1706    add(_prettyMap, "span", INLINE);
1707    add(_prettyMap, "bdo", INLINE);
1708    add(_prettyMap, "jsp:expression", INLINE);
1709    
1710    add(_prettyMap, "textarea", PRE);
1711    add(_prettyMap, "pre", PRE);
1712    
1713    add(_prettyMap, "html", NO_INDENT);
1714    add(_prettyMap, "body", NO_INDENT);
1715    add(_prettyMap, "ul", NO_INDENT);
1716    add(_prettyMap, "table", NO_INDENT);
1717    add(_prettyMap, "frameset", NO_INDENT);
1718    
1719    _verbatimTags = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
1720    add(_verbatimTags, "script");
1721    add(_verbatimTags, "style");
1722  }
1723}
1724
Popular Tags