
org.enhydra.xml.io.XMLFormatter Maven / Gradle / Ivy
The newest version!
/*
* Enhydra Java Application Server Project
*
* The contents of this file are subject to the Enhydra Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License on
* the Enhydra web site ( http://www.enhydra.org/ ).
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific terms governing rights and limitations
* under the License.
*
* The Initial Developer of the Enhydra Application Server is Lutris
* Technologies, Inc. The Enhydra Application Server and portions created
* by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
* All Rights Reserved.
*
* Contributor(s):
*
* $Id: XMLFormatter.java,v 1.7 2005/01/26 08:29:24 jkjome Exp $
*/
package org.enhydra.xml.io;
import java.io.IOException;
import org.enhydra.xml.dom.DOMAccess;
import org.enhydra.xml.dom.DOMOps;
import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;
/*
* FIXME:
* - Add option to encode entity references from the DTD.
* - namespaces
* - writespace
* - Need to handle entities for 8bit encodings
*/
/**
* Formatter for outputting a HTML DOM as a HTML text document.
*/
final class XMLFormatter extends BaseDOMFormatter implements Formatter {
/**
* Default XML encoding.
*/
private static final String DEFAULT_XML_ENCODING = "UTF-8";
/**
* XML version (only 1.0 supported, as thats all there is now).
*/
private static final String XML_VERSION = "1.0";
/**
* Table use to optimized checking for characters that should be
* represented as entity references.
*/
private static final boolean[] fEntityQuickCheck
= new boolean[MAX_ENTITY_QUICK_CHECK_CHAR+1];
/**
* Indicates then a text has just been handled
*/
private boolean fHandleText = false;
/**
* Indicates the next Sibling is a Text node
*/
private boolean fNextSiblingText = false;
/**
* Static constructor.
*/
static {
fEntityQuickCheck['<'] = true;
fEntityQuickCheck['>'] = true;
fEntityQuickCheck['"'] = true;
fEntityQuickCheck['\''] = true;
fEntityQuickCheck['&'] = true;
}
/**
* Constructor.
*/
public XMLFormatter(Node node,
OutputOptions outputOptions,
boolean forPreFormatting) {
super(node, outputOptions, forPreFormatting, DEFAULT_XML_ENCODING, fEntityQuickCheck);
}
/**
* Get the default OutputOptions for a document formatter with this
* formatter. The encoding will not be set, which signals to use the
* default encoding.
*/
static OutputOptions getDefaultOutputOptions() {
return new OutputOptions(); // Nothing special
}
/**
* @see BaseDOMFormatter#getCharacterEntity
*/
protected final String getCharacterEntity(char textChar) {
switch (textChar) {
case '<':
return "lt";
case '>':
return "gt";
case '"':
return "quot";
case '\'':
return "apos";
case '&':
return "amp";
default:
return null;
}
}
/**
* Write the XML header.
*/
private void writeXMLHeader(Document document) throws IOException {
fOut.write("");
writeln();
}
/**
* Handler called for Document nodes; creates the XML file header.
* @see org.enhydra.xml.dom.DOMTraversal.Handler#handleDocument
*/
public void handleDocument(Document document) throws IOException {
if (!fOptions.getOmitXMLHeader()) {
writeXMLHeader(document);
}
fTraverser.processDocumentType(document);
fTraverser.processChildren(document);
}
/**
* Handler called for Document nodes; writes the DOCTYPE specification.
* @see org.enhydra.xml.dom.DOMTraversal.Handler#handleDocumentType
*/
public void handleDocumentType(DocumentType documentType) throws IOException {
if (fOptions.getOmitDocType()) {
return; // Don't output DOCTYPE.
}
String internalSubset = documentType.getInternalSubset();
if ((fPublicId == null) && (fSystemId == null)
&& (internalSubset == null)) {
return; // No DOCTYPE
}
Element docElement = fDocument.getDocumentElement();
if (docElement == null) {
throw new XMLIOError("Document has DocumentType, with out having a root element");
}
fOut.write("');
writeln();
}
/**
* Handler called for DocumentFragment nodes; just process children
* @see org.enhydra.xml.dom.DOMTraversal.Handler#handleDocumentFragment
*/
public void handleDocumentFragment(DocumentFragment documentFragment) {
fTraverser.processChildren(documentFragment);
}
/**
* Handler called for Attr nodes.
* @see org.enhydra.xml.dom.DOMTraversal.Handler#handleAttr
*/
public void handleAttr(Attr attr) throws IOException {
fOut.write(' ');
fOut.write(attr.getName());
writeAttributeValue(attr);
}
/**
* Write an element open tag.
*/
protected final void writeOpenTag(Element element,
String tagName,
boolean hasChildren) throws IOException {
String formattedTag = null;
if (fPrettyPrinting) {
if (fNextSiblingText) {
fOut.write('\n');
}
fNextSiblingText = (element.getNextSibling() instanceof Text);
} // end of if ()
if (fUsePreFormattedElements && (element instanceof PreFormattedText)) {
formattedTag = ((PreFormattedText)element).getPreFormattedText();
}
if (formattedTag != null) {
fOut.write(formattedTag);
fPreFormattedElementCount++;
} else {
if (fPrettyPrinting && !(element.getPreviousSibling() instanceof Text)) {
printIndent();
} // end of if ()
fOut.write('<');
fOut.write(tagName);
fTraverser.processAttributes(element);
if (!hasChildren && !(fOptions.getEnableXHTMLCompatibility() && !isXHTMLContentModelEmpty(tagName))) {
//see http://www.w3.org/TR/xhtml1/#C_2
if (fOptions.getEnableXHTMLCompatibility()) fOut.write(" /");
else fOut.write('/');
}
fOut.write('>');
fDynamicFormattedElementCount++;
if (fPrettyPrinting && !(element.getFirstChild() instanceof Text)) {
fOut.write('\n');
} // end of if ()
}
}
/**
* Handler called for Element nodes.
* @see org.enhydra.xml.dom.DOMTraversal.Handler#handleElement
*/
public void handleElement(Element element) throws IOException {
String tagName = element.getTagName();
// Start Barracuda Kludge ======
// check the element to see if it contains a
// attribute "visdom".
// (org.enhydra.barracuda.core.comp.BComponent.VISIBILITY_MARKER)
// This controls DOM visibility. If this value exists and does not
// match to true, don't print this particular node.
// Note: This should be made generic, but for now...
Attr attr = DOMAccess.accessAttribute(fDocument, element, null, "visdom");
if (attr != null && !(Boolean.valueOf(attr.getValue()).booleanValue())) return;
// End Barracuda Kludge ======
boolean hasChildren = element.hasChildNodes();
writeOpenTag(element, tagName, hasChildren);
// Process children and close.
if (hasChildren || (fOptions.getEnableXHTMLCompatibility() && !isXHTMLContentModelEmpty(tagName))) {
fTraverser.processChildren(element);
if (fHandleText) {
fHandleText = false;
} else {
printIndent();
} // end of else
fOut.write("");
fOut.write(tagName);
fOut.write('>');
if (fPrettyPrinting && !fNextSiblingText) {
fOut.write('\n');
} // end of if ()
}
}
/**
* Handler called for ProcessingInstruction nodes.
* @see org.enhydra.xml.dom.DOMTraversal.Handler#handleProcessingInstruction
*/
public void handleProcessingInstruction(ProcessingInstruction pi) throws IOException {
fOut.write("");
fOut.write(pi.getTarget());
String data = pi.getData();
if (data != null) {
fOut.write(' ');
fOut.write(data);
}
fOut.write("?>");
}
/**
* Handler called for CDATASection nodes.
* @see org.enhydra.xml.dom.DOMTraversal.Handler#handleCDATASection
*/
public void handleCDATASection(CDATASection cdata) throws IOException {
//FIXME: Should we handle embedded ]]>???
fOut.write("");
}
/**
* Handler called for Text nodes.
* @see org.enhydra.xml.dom.DOMTraversal.Handler#handleText
*/
public final void handleText(Text text) throws IOException {
fHandleText = true;
super.handleText(text);
}
/**
* See {@link http://www.w3.org/TR/xhtml1/#C_3}
*
* Utility method to tell us which XHTML elements contain an empty
* content model allowing us to, in the converse, encapsulate the
* logic needed to deal with XHTML tags that, if backward compatibility
* is requested, should be written with an explicit close tag rather than
* use the default minimized syntax when the element has no children.
*
* @version 2.2
*/
private static boolean isXHTMLContentModelEmpty(String tagName) {
String[] emptyTags = {"br","area","link","img","param","hr","input","col","base","meta"};
boolean isEmptyTag = false;
for (int i = 0; i < emptyTags.length; i++) {
if (emptyTags[i].equalsIgnoreCase(tagName)) {
isEmptyTag = true;
break;
}
}
return isEmptyTag;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy