/* * Copyright (C) 2010 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.commons.xml; import java.io.IOException; import java.io.Writer; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import org.exoplatform.commons.utils.HTMLEntityEncoder; import org.gatein.common.logging.Logger; import org.gatein.common.logging.LoggerFactory; import org.w3c.dom.Attr; import org.w3c.dom.CDATASection; import org.w3c.dom.CharacterData; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * An high performance and custom DOM serializer based on stax {@link XMLStreamWriter}. * * <p> * The serializer takes care of correctly writing empty script elements with their non empty form, because we want to ouput * xhtml text that will still work on html browsers. * </p> * * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> * @version $Revision$ */ public class DOMSerializer { /** . */ private static final Logger log = LoggerFactory.getLogger(DOMSerializer.class); /** Thread safe. */ private static final XMLOutputFactory outputFactory; /** . */ private static final String DEFAULT_XML_OUTPUT_FACTORY = "com.sun.xml.internal.stream.XMLOutputFactoryImpl"; static { XMLOutputFactory tmp; try { Class<XMLOutputFactory> cl = (Class<XMLOutputFactory>) Thread.currentThread().getContextClassLoader() .loadClass(DEFAULT_XML_OUTPUT_FACTORY); tmp = cl.newInstance(); } catch (Exception e) { tmp = XMLOutputFactory.newInstance(); log.warn("Could not instantiate " + DEFAULT_XML_OUTPUT_FACTORY + " will use default provided by runtime instead " + tmp.getClass().getName()); } // outputFactory = tmp; } public static void serialize(Element element, Writer writer) throws IOException, XMLStreamException { XMLStreamWriter xml = outputFactory.createXMLStreamWriter(writer); serialize(element, xml); xml.writeEndDocument(); xml.flush(); } private static void serialize(Element element, XMLStreamWriter writer) throws IOException, XMLStreamException { String tagName = element.getTagName(); // Determine if empty // Note that we won't accumulate the elements that would be serialized for performance reason // we will just reiterate later before ending the element boolean empty; if (tagName.equalsIgnoreCase("script")) { empty = false; } else { empty = true; NodeList children = element.getChildNodes(); int length = children.getLength(); for (int i = 0; i < length && empty; i++) { Node child = children.item(i); if (child instanceof CharacterData) { empty = false; } else if (child instanceof Element) { empty = false; } } } // if (empty) { writer.writeEmptyElement(tagName); } else { writer.writeStartElement(tagName); } // Write attributes if (element.hasAttributes()) { NamedNodeMap attrs = element.getAttributes(); int length = attrs.getLength(); for (int i = 0; i < length; i++) { Attr attr = (Attr) attrs.item(i); writer.writeAttribute(attr.getName(), attr.getValue()); } } // if (!empty) { // Serialize children that are worth to be NodeList children = element.getChildNodes(); int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if (child instanceof CDATASection) { writer.writeCData(((CDATASection) child).getData()); } else if (child instanceof CharacterData) { writeTextData(writer, ((CharacterData) child).getData()); } else if (child instanceof Element) { serialize((Element) child, writer); } } // Close writer.writeEndElement(); } } private static void writeTextData(XMLStreamWriter writer, String data) throws XMLStreamException { StringBuilder builder = new StringBuilder(); for (int i = 0; i < data.length(); i++) { char c = data.charAt(i); String encodedValue = HTMLEntityEncoder.getInstance().lookupEntityName(c); if (encodedValue == null) { builder.append(c); } else { builder.append(encodedValue); } } writer.writeCharacters(builder.toString()); } }