All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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("');

            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("");
    }

    /**
     * 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