KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > xml > io > XMLFormatter


1 /*
2  * Enhydra Java Application Server Project
3  *
4  * The contents of this file are subject to the Enhydra Public License
5  * Version 1.1 (the "License"); you may not use this file except in
6  * compliance with the License. You may obtain a copy of the License on
7  * the Enhydra web site ( http://www.enhydra.org/ ).
8  *
9  * Software distributed under the License is distributed on an "AS IS"
10  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
11  * the License for the specific terms governing rights and limitations
12  * under the License.
13  *
14  * The Initial Developer of the Enhydra Application Server is Lutris
15  * Technologies, Inc. The Enhydra Application Server and portions created
16  * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
17  * All Rights Reserved.
18  *
19  * Contributor(s):
20  *
21  * $Id: XMLFormatter.java,v 1.7 2005/01/26 08:29:24 jkjome Exp $
22  */

23
24 package org.enhydra.xml.io;
25
26 import java.io.IOException JavaDoc;
27
28 import org.enhydra.xml.dom.DOMAccess;
29 import org.enhydra.xml.dom.DOMOps;
30 import org.w3c.dom.Attr JavaDoc;
31 import org.w3c.dom.CDATASection JavaDoc;
32 import org.w3c.dom.Document JavaDoc;
33 import org.w3c.dom.DocumentFragment JavaDoc;
34 import org.w3c.dom.DocumentType JavaDoc;
35 import org.w3c.dom.Element JavaDoc;
36 import org.w3c.dom.Node JavaDoc;
37 import org.w3c.dom.ProcessingInstruction JavaDoc;
38 import org.w3c.dom.Text JavaDoc;
39
40 /*
41  * FIXME:
42  * - Add option to encode entity references from the DTD.
43  * - namespaces
44  * - writespace
45  * - Need to handle entities for 8bit encodings
46  */

47
48 /**
49  * Formatter for outputting a HTML DOM as a HTML text document.
50  */

51 final class XMLFormatter extends BaseDOMFormatter implements Formatter {
52     /**
53      * Default XML encoding.
54      */

55     private static final String JavaDoc DEFAULT_XML_ENCODING = "UTF-8";
56
57     /**
58      * XML version (only 1.0 supported, as thats all there is now).
59      */

60     private static final String JavaDoc XML_VERSION = "1.0";
61
62     /**
63      * Table use to optimized checking for characters that should be
64      * represented as entity references.
65      */

66     private static final boolean[] fEntityQuickCheck
67         = new boolean[MAX_ENTITY_QUICK_CHECK_CHAR+1];
68
69     /**
70      * Indicates then a text has just been handled
71      */

72     private boolean fHandleText = false;
73
74     /**
75      * Indicates the next Sibling is a Text node
76      */

77     private boolean fNextSiblingText = false;
78
79     /**
80      * Static constructor.
81      */

82     static {
83         fEntityQuickCheck['<'] = true;
84         fEntityQuickCheck['>'] = true;
85         fEntityQuickCheck['"'] = true;
86         fEntityQuickCheck['\''] = true;
87         fEntityQuickCheck['&'] = true;
88     }
89
90     /**
91      * Constructor.
92      */

93     public XMLFormatter(Node JavaDoc node,
94                         OutputOptions outputOptions,
95                         boolean forPreFormatting) {
96         super(node, outputOptions, forPreFormatting, DEFAULT_XML_ENCODING, fEntityQuickCheck);
97     }
98
99     /**
100      * Get the default OutputOptions for a document formatter with this
101      * formatter. The encoding will not be set, which signals to use the
102      * default encoding.
103      */

104     static OutputOptions getDefaultOutputOptions() {
105         return new OutputOptions(); // Nothing special
106
}
107
108     /**
109      * @see BaseDOMFormatter#getCharacterEntity
110      */

111     protected final String JavaDoc getCharacterEntity(char textChar) {
112         switch (textChar) {
113     case '<':
114             return "lt";
115     case '>':
116             return "gt";
117     case '"':
118             return "quot";
119     case '\'':
120             return "apos";
121     case '&':
122             return "amp";
123         default:
124             return null;
125         }
126     }
127
128     /**
129      * Write the XML header.
130      */

131     private void writeXMLHeader(Document JavaDoc document) throws IOException JavaDoc {
132         fOut.write("<?xml version=\"");
133         fOut.write(XML_VERSION);
134         fOut.write("\"");
135
136         // Encoding
137
if (!fOptions.getOmitEncoding()) {
138             fOut.write(" encoding=\"");
139             fOut.write(getMIMEEncoding());
140             fOut.write('"');
141         }
142
143         // Standalone - can only determine this if document
144
// supports the method. If not, default to "no"
145
try {
146         if (DOMOps.getStandalone(document)) {
147         fOut.write(" standalone=\"yes\"");
148         }
149     } catch (UnsupportedOperationException JavaDoc e) {
150         // Ignore this - we assume 'standalone="no"' in this case...
151
}
152
153         fOut.write("?>");
154         writeln();
155     }
156
157     /**
158      * Handler called for Document nodes; creates the XML file header.
159      * @see org.enhydra.xml.dom.DOMTraversal.Handler#handleDocument
160      */

161     public void handleDocument(Document JavaDoc document) throws IOException JavaDoc {
162         if (!fOptions.getOmitXMLHeader()) {
163             writeXMLHeader(document);
164         }
165         fTraverser.processDocumentType(document);
166         fTraverser.processChildren(document);
167     }
168
169     /**
170      * Handler called for Document nodes; writes the DOCTYPE specification.
171      * @see org.enhydra.xml.dom.DOMTraversal.Handler#handleDocumentType
172      */

173     public void handleDocumentType(DocumentType JavaDoc documentType) throws IOException JavaDoc {
174         if (fOptions.getOmitDocType()) {
175             return; // Don't output DOCTYPE.
176
}
177         String JavaDoc internalSubset = documentType.getInternalSubset();
178         if ((fPublicId == null) && (fSystemId == null)
179             && (internalSubset == null)) {
180             return; // No DOCTYPE
181
}
182         Element JavaDoc docElement = fDocument.getDocumentElement();
183         if (docElement == null) {
184             throw new XMLIOError("Document has DocumentType, with out having a root element");
185         }
186
187         fOut.write("<!DOCTYPE ");
188         fOut.write(docElement.getTagName());
189         
190         if (fPublicId != null) {
191             // Public requires system.
192
if (fSystemId == null) {
193                 throw new XMLIOError("No SYSTEM id to accompany PUBLIC id: "
194                                      + fPublicId);
195             }
196             fOut.write(" PUBLIC \"");
197             fOut.write(fPublicId);
198             fOut.write("\" \"");
199             fOut.write(fSystemId);
200             fOut.write("\"");
201         } else if (fSystemId != null) {
202             fOut.write(" SYSTEM \"");
203             fOut.write(fSystemId);
204             fOut.write("\"");
205         }
206
207         if (internalSubset != null) {
208             writeln();
209             fOut.write(" [");
210             fOut.write(internalSubset);
211             fOut.write("]");
212         }
213         fOut.write('>');
214         writeln();
215     }
216
217     /**
218      * Handler called for DocumentFragment nodes; just process children
219      * @see org.enhydra.xml.dom.DOMTraversal.Handler#handleDocumentFragment
220      */

221     public void handleDocumentFragment(DocumentFragment JavaDoc documentFragment) {
222         fTraverser.processChildren(documentFragment);
223     }
224
225     /**
226      * Handler called for Attr nodes.
227      * @see org.enhydra.xml.dom.DOMTraversal.Handler#handleAttr
228      */

229     public void handleAttr(Attr JavaDoc attr) throws IOException JavaDoc {
230         fOut.write(' ');
231         fOut.write(attr.getName());
232         writeAttributeValue(attr);
233     }
234
235     /**
236      * Write an element open tag.
237      */

238     protected final void writeOpenTag(Element JavaDoc element,
239                                       String JavaDoc tagName,
240                                       boolean hasChildren) throws IOException JavaDoc {
241         String JavaDoc formattedTag = null;
242         if (fPrettyPrinting) {
243             if (fNextSiblingText) {
244                 fOut.write('\n');
245             }
246             fNextSiblingText = (element.getNextSibling() instanceof Text JavaDoc);
247         } // end of if ()
248

249         if (fUsePreFormattedElements && (element instanceof PreFormattedText)) {
250             formattedTag = ((PreFormattedText)element).getPreFormattedText();
251         }
252         if (formattedTag != null) {
253             fOut.write(formattedTag);
254             fPreFormattedElementCount++;
255         } else {
256             if (fPrettyPrinting && !(element.getPreviousSibling() instanceof Text JavaDoc)) {
257                 printIndent();
258             } // end of if ()
259

260             fOut.write('<');
261             fOut.write(tagName);
262             fTraverser.processAttributes(element);
263             if (!hasChildren && !(fOptions.getEnableXHTMLCompatibility() && !isXHTMLContentModelEmpty(tagName))) {
264                 //see http://www.w3.org/TR/xhtml1/#C_2
265
if (fOptions.getEnableXHTMLCompatibility()) fOut.write(" /");
266                 else fOut.write('/');
267             }
268             fOut.write('>');
269             fDynamicFormattedElementCount++;
270             if (fPrettyPrinting && !(element.getFirstChild() instanceof Text JavaDoc)) {
271                 fOut.write('\n');
272             } // end of if ()
273
}
274     }
275
276     /**
277      * Handler called for Element nodes.
278      * @see org.enhydra.xml.dom.DOMTraversal.Handler#handleElement
279      */

280     public void handleElement(Element JavaDoc element) throws IOException JavaDoc {
281         String JavaDoc tagName = element.getTagName();
282
283         // Start Barracuda Kludge ======
284
// check the element to see if it contains a
285
// attribute "visdom".
286
// (org.enhydra.barracuda.core.comp.BComponent.VISIBILITY_MARKER)
287
// This controls DOM visibility. If this value exists and does not
288
// match to true, don't print this particular node.
289
// Note: This should be made generic, but for now...
290
Attr JavaDoc attr = DOMAccess.accessAttribute(fDocument, element, null, "visdom");
291         if (attr != null && !(Boolean.valueOf(attr.getValue()).booleanValue())) return;
292         // End Barracuda Kludge ======
293

294         boolean hasChildren = element.hasChildNodes();
295
296         writeOpenTag(element, tagName, hasChildren);
297
298         // Process children and close.
299
if (hasChildren || (fOptions.getEnableXHTMLCompatibility() && !isXHTMLContentModelEmpty(tagName))) {
300             fTraverser.processChildren(element);
301
302             if (fHandleText) {
303                 fHandleText = false;
304             } else {
305                 printIndent();
306             } // end of else
307

308             fOut.write("</");
309             fOut.write(tagName);
310             fOut.write('>');
311
312             if (fPrettyPrinting && !fNextSiblingText) {
313                 fOut.write('\n');
314             } // end of if ()
315
}
316     }
317
318     /**
319      * Handler called for ProcessingInstruction nodes.
320      * @see org.enhydra.xml.dom.DOMTraversal.Handler#handleProcessingInstruction
321      */

322     public void handleProcessingInstruction(ProcessingInstruction JavaDoc pi) throws IOException JavaDoc {
323         fOut.write("<?");
324         fOut.write(pi.getTarget());
325         String JavaDoc data = pi.getData();
326         if (data != null) {
327             fOut.write(' ');
328             fOut.write(data);
329         }
330         fOut.write("?>");
331     }
332
333     /**
334      * Handler called for CDATASection nodes.
335      * @see org.enhydra.xml.dom.DOMTraversal.Handler#handleCDATASection
336      */

337     public void handleCDATASection(CDATASection JavaDoc cdata) throws IOException JavaDoc {
338         //FIXME: Should we handle embedded ]]>???
339
fOut.write("<![CDATA[");
340         fOut.write(cdata.getNodeValue());
341         fOut.write("]]>");
342     }
343
344     /**
345      * Handler called for Text nodes.
346      * @see org.enhydra.xml.dom.DOMTraversal.Handler#handleText
347      */

348     public final void handleText(Text JavaDoc text) throws IOException JavaDoc {
349         fHandleText = true;
350         super.handleText(text);
351     }
352
353     /**
354      * See {@link http://www.w3.org/TR/xhtml1/#C_3}
355      *
356      * <p>Utility method to tell us which XHTML elements contain an empty
357      * content model allowing us to, in the converse, encapsulate the
358      * logic needed to deal with XHTML tags that, if backward compatibility
359      * is requested, should be written with an explicit close tag rather than
360      * use the default minimized syntax when the element has no children.</p>
361      *
362      * @version 2.2
363      */

364     private static boolean isXHTMLContentModelEmpty(String JavaDoc tagName) {
365         String JavaDoc[] emptyTags = {"br","area","link","img","param","hr","input","col","base","meta"};
366         boolean isEmptyTag = false;
367         for (int i = 0; i < emptyTags.length; i++) {
368             if (emptyTags[i].equalsIgnoreCase(tagName)) {
369                 isEmptyTag = true;
370                 break;
371             }
372         }
373         return isEmptyTag;
374     }
375
376 }
377
Popular Tags