Java tutorial
/* * $Id: XmlEmitter.java,v 1.9 2008/10/06 13:31:41 obecker Exp $ * * The contents of this file are subject to the Mozilla 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 at * http://www.mozilla.org/MPL/ * * 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 language governing rights and limitations under the * License. * * The Original Code is: this file * * The Initial Developer of the Original Code is Oliver Becker. * * Portions created by ______________________ * are Copyright (C) ______ _______________________. * All Rights Reserved. * * Contributor(s): Anatolij Zubow */ package net.sf.joost.emitter; import net.sf.joost.OptionalLog; import java.io.IOException; import java.io.Writer; import java.util.Properties; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; import org.apache.commons.logging.Log; import org.xml.sax.Attributes; import org.xml.sax.SAXException; /** * This class implements an emitter that uses the <code>xml</code> output * method for byte or character streams. * @version $Revision: 1.9 $ $Date: 2008/10/06 13:31:41 $ * @author Oliver Becker, Anatolij Zubow */ public class XmlEmitter extends StreamEmitter { // Log initialization private static Log log = OptionalLog.getLog(XmlEmitter.class); /** output property: omit-xml-declaration */ private boolean propOmitXmlDeclaration = false; /** output property: standalone */ private boolean propStandalone = false; /** output property: version */ private String propVersion = "1.0"; /** string buffer for namespace declarations */ private StringBuffer nsDeclarations = new StringBuffer(); /** qName of the previous element */ private String lastQName; /** attributes of the previous element */ private Attributes lastAttrs; /** flag indicating if we're within a CDATA section */ private boolean insideCDATA = false; /** flag indicating if disable output escaping will be supported */ private boolean supportDisableOutputEscaping = false; /** flag indicating if disabled output escaping is active */ private boolean disabledOutputEscaping = false; /** Constructor */ public XmlEmitter(Writer writer, String encoding, Properties outputProperties) { super(writer, encoding); if (outputProperties != null) { String val; val = outputProperties.getProperty(OutputKeys.OMIT_XML_DECLARATION); if (val != null) propOmitXmlDeclaration = val.equals("yes"); if (!encoding.equals("UTF-8") && !encoding.equals("UTF-16")) propOmitXmlDeclaration = false; val = outputProperties.getProperty(OutputKeys.STANDALONE); if (val != null) propStandalone = val.equals("yes"); val = outputProperties.getProperty(OutputKeys.VERSION); if (val != null) propVersion = val; } } /** * Defines whether the XML declaration should be omitted, default is * <code>false</code>. * @param flag <code>true</code>: the XML declaration will be omitted; * <code>false</code>: the XML declaration will be output */ public void setOmitXmlDeclaration(boolean flag) { propOmitXmlDeclaration = flag; } /** * Defines whether disable-output-escaping will be supported * (means whether the corresponding processing instructions * {@link Result#PI_DISABLE_OUTPUT_ESCAPING} and * {@link Result#PI_ENABLE_OUTPUT_ESCAPING} will be interpreted). * The default is <code>false</code> * @param flag <code>true</code> the PIs will be interpreted; * <code>false</code> the PIs will be written literally */ public void setSupportDisableOutputEscaping(boolean flag) { this.supportDisableOutputEscaping = flag; } /** * Outputs a start or empty element tag if there is one stored. * @param end true if this method was called due to an endElement event, * i.e. an empty element tag has to be output. * @return true if something was output (needed for endElement to * determine, if a separate end tag must be output) */ private boolean processLastElement(boolean end) throws SAXException { if (lastQName != null) { StringBuffer out = new StringBuffer("<"); out.append(lastQName); out.append(nsDeclarations); nsDeclarations.setLength(0); // attributes int length = lastAttrs.getLength(); for (int i = 0; i < length; i++) { out.append(' ').append(lastAttrs.getQName(i)).append("=\""); char[] attChars = lastAttrs.getValue(i).toCharArray(); // output escaping for (int j = 0; j < attChars.length; j++) switch (attChars[j]) { case '&': out.append("&"); break; case '<': out.append("<"); break; case '>': out.append(">"); break; case '\"': out.append("""); break; case '\t': out.append("	"); break; case '\n': out.append("
"); break; case '\r': out.append("
"); break; default: j = encodeCharacters(attChars, j, out); } out.append('\"'); } out.append(end ? " />" : ">"); try { // stream string to writer writer.write(out.toString()); if (DEBUG) log.debug(out); } catch (IOException ex) { if (log != null) log.error(ex); throw new SAXException(ex); } lastQName = null; return true; } return false; } /** * SAX2-Callback - Outputs XML-Deklaration with encoding. */ public void startDocument() throws SAXException { if (propOmitXmlDeclaration) return; try { writer.write("<?xml version=\""); writer.write(propVersion); writer.write("\" encoding=\""); writer.write(encoding); if (propStandalone) writer.write("\" standalone=\"yes"); writer.write("\"?>\n"); } catch (IOException ex) { if (log != null) log.error(ex); throw new SAXException(ex); } } /** * SAX2-Callback - Flushes the output writer */ public void endDocument() throws SAXException { processLastElement(false); try { writer.write("\n"); writer.flush(); } catch (IOException ex) { if (log != null) log.error(ex); throw new SAXException(ex); } } /** * SAX2-Callback */ public void startElement(String uri, String lName, String qName, Attributes attrs) throws SAXException { processLastElement(false); this.lastQName = qName; this.lastAttrs = attrs; } /** * SAX2-Callback - Outputs the element-tag. */ public void endElement(String uri, String lName, String qName) throws SAXException { // output end tag only if processLastElement didn't output // something (here: empty element tag) if (processLastElement(true) == false) { try { writer.write("</"); writer.write(qName); writer.write(">"); } catch (IOException ex) { if (log != null) log.error(ex); throw new SAXException(ex); } } } /** * SAX2-Callback - Constructs characters. */ public void characters(char[] ch, int start, int length) throws SAXException { processLastElement(false); try { if (insideCDATA || disabledOutputEscaping) { // check that the characters can be represented in the current // encoding (escaping not possible within CDATA) for (int i = 0; i < length; i++) if (!charsetEncoder.canEncode(ch[start + i])) throw new SAXException("Cannot output character with code " + (int) ch[start + i] + " in the encoding '" + encoding + "' within a CDATA section"); writer.write(ch, start, length); } else { StringBuffer out = new StringBuffer(length); // output escaping for (int i = 0; i < length; i++) switch (ch[start + i]) { case '&': out.append("&"); break; case '<': out.append("<"); break; case '>': out.append(">"); break; default: i = encodeCharacters(ch, start + i, out) - start; } writer.write(out.toString()); } if (DEBUG) log.debug("'" + new String(ch, start, length) + "'"); } catch (IOException ex) { if (log != null) log.error(ex); throw new SAXException(ex); } } /** * SAX2-Callback */ public void startPrefixMapping(String prefix, String uri) throws SAXException { processLastElement(false); if ("".equals(prefix)) nsDeclarations.append(" xmlns=\""); else nsDeclarations.append(" xmlns:").append(prefix).append("=\""); nsDeclarations.append(uri).append('\"'); } /** * SAX2-Callback - Outputs a PI */ public void processingInstruction(String target, String data) throws SAXException { processLastElement(false); if (supportDisableOutputEscaping) { if (Result.PI_DISABLE_OUTPUT_ESCAPING.equals(target)) { disabledOutputEscaping = true; return; } else if (Result.PI_ENABLE_OUTPUT_ESCAPING.equals(target)) { disabledOutputEscaping = false; return; } } try { writer.write("<?"); writer.write(target); if (!data.equals("")) { writer.write(" "); writer.write(data); } writer.write("?>"); } catch (IOException ex) { if (log != null) log.error(ex); throw new SAXException(ex); } } /** * SAX2-Callback - Notify the start of a CDATA section */ public void startCDATA() throws SAXException { processLastElement(false); try { writer.write("<![CDATA["); } catch (IOException ex) { if (log != null) log.error(ex); throw new SAXException(ex); } insideCDATA = true; } /** * SAX2-Callback - Notify the end of a CDATA section */ public void endCDATA() throws SAXException { insideCDATA = false; try { writer.write("]]>"); } catch (IOException ex) { if (log != null) log.error(ex); throw new SAXException(ex); } } /** * SAX2-Callback - Outputs a comment */ public void comment(char[] ch, int start, int length) throws SAXException { processLastElement(false); try { writer.write("<!--"); writer.write(ch, start, length); writer.write("-->"); } catch (IOException ex) { if (log != null) log.error(ex); throw new SAXException(ex); } } /** * SAX2-Callback - Outputs a document type declaration */ public void startDTD(String name, String publicId, String systemId) throws SAXException { try { writer.write("<!DOCTYPE "); writer.write(name); if (publicId != null) { writer.write(" PUBLIC \""); writer.write(publicId); writer.write("\" \""); if (systemId != null) { writer.write(systemId); } writer.write("\""); } else if (systemId != null) { writer.write(" SYSTEM \""); writer.write(systemId); writer.write("\""); } // internal subset not supported yet writer.write(">\n"); } catch (IOException ex) { if (log != null) log.error(ex); throw new SAXException(ex); } } }