You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by cz...@apache.org on 2008/12/22 15:50:00 UTC
svn commit: r728704 [2/2] - in /cocoon/whiteboard/xml-utils: ./ src/
src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/
src/main/java/org/apache/cocoon/ src/main/java/org/apache/cocoon/xml/
src/main/java/org/apache/cocoon/xml/util/ s...
Added: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/SaxBuffer.java
URL: http://svn.apache.org/viewvc/cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/SaxBuffer.java?rev=728704&view=auto
==============================================================================
--- cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/SaxBuffer.java (added)
+++ cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/SaxBuffer.java Mon Dec 22 06:49:59 2008
@@ -0,0 +1,563 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.xml.util;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * A class that can record SAX events and replay them later.
+ *
+ * <p>Compared to the old Cocoon.XMLByteStreamCompiler,
+ * this class is many times faster at sending out the recorded SAX events since
+ * it doesn't need to convert between byte and char representations etc.
+ * On the other hand, its data structure is more complex then a simple byte array,
+ * making XMLByteStreamCompiler better in case the recorded SAX should be stored long-term.
+ *
+ * <p>Use this class if you need to frequently generate smaller amounts of SAX events,
+ * or replay a set of recorded start events immediately.</p>
+ *
+ * <p>Both {@link ContentHandler} and {@link LexicalHandler} are supported, the only
+ * exception is that the setDocumentLocator event is not recorded.</p>
+ *
+ * @version $Id$
+ */
+public class SaxBuffer implements Serializable {
+
+ /**
+ * Stores list of {@link SaxBit} objects.
+ */
+ protected List<SaxBit> saxbits;
+
+ /**
+ * Creates empty SaxBuffer
+ */
+ public SaxBuffer() {
+ this.saxbits = new ArrayList<SaxBit>();
+ }
+
+ /**
+ * Creates SaxBuffer based on the provided bits list.
+ */
+ public SaxBuffer(List<SaxBit> bits) {
+ this.saxbits = bits;
+ }
+
+ /**
+ * Creates copy of another SaxBuffer
+ */
+ public SaxBuffer(SaxBuffer saxBuffer) {
+ this.saxbits = new ArrayList<SaxBit>(saxBuffer.saxbits);
+ }
+
+ //
+ // ContentHandler Interface
+ //
+
+ public void skippedEntity(String name) throws SAXException {
+ saxbits.add(new SkippedEntity(name));
+ }
+
+ public void setDocumentLocator(Locator locator) {
+ // Don't record this event
+ }
+
+ public void ignorableWhitespace(char ch[], int start, int length) throws SAXException {
+ saxbits.add(new IgnorableWhitespace(ch, start, length));
+ }
+
+ public void processingInstruction(String target, String data) throws SAXException {
+ saxbits.add(new PI(target, data));
+ }
+
+ public void startDocument() throws SAXException {
+ saxbits.add(StartDocument.SINGLETON);
+ }
+
+ public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
+ saxbits.add(new StartElement(namespaceURI, localName, qName, atts));
+ }
+
+ public void endPrefixMapping(String prefix) throws SAXException {
+ saxbits.add(new EndPrefixMapping(prefix));
+ }
+
+ public void characters(char ch[], int start, int length) throws SAXException {
+ saxbits.add(new Characters(ch, start, length));
+ }
+
+ public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
+ saxbits.add(new EndElement(namespaceURI, localName, qName));
+ }
+
+ public void endDocument() throws SAXException {
+ saxbits.add(EndDocument.SINGLETON);
+ }
+
+ public void startPrefixMapping(String prefix, String uri) throws SAXException {
+ saxbits.add(new StartPrefixMapping(prefix, uri));
+ }
+
+ //
+ // LexicalHandler Interface
+ //
+
+ public void endCDATA() throws SAXException {
+ saxbits.add(EndCDATA.SINGLETON);
+ }
+
+ public void comment(char ch[], int start, int length) throws SAXException {
+ saxbits.add(new Comment(ch, start, length));
+ }
+
+ public void startEntity(String name) throws SAXException {
+ saxbits.add(new StartEntity(name));
+ }
+
+ public void endDTD() throws SAXException {
+ saxbits.add(EndDTD.SINGLETON);
+ }
+
+ public void startDTD(String name, String publicId, String systemId) throws SAXException {
+ saxbits.add(new StartDTD(name, publicId, systemId));
+ }
+
+ public void startCDATA() throws SAXException {
+ saxbits.add(StartCDATA.SINGLETON);
+ }
+
+ public void endEntity(String name) throws SAXException {
+ saxbits.add(new EndEntity(name));
+ }
+
+ //
+ // Public Methods
+ //
+
+ /**
+ * Add a another buffer
+ */
+ public void saxBuffer(SaxBuffer xml) {
+ saxbits.add(new XMLizableBit(xml));
+ }
+
+ /**
+ * @return true if buffer is empty
+ */
+ public boolean isEmpty() {
+ return saxbits.isEmpty();
+ }
+
+ /**
+ * @return unmodifiable list of SAX bits
+ */
+ public List<SaxBit> getBits() {
+ return Collections.unmodifiableList(saxbits);
+ }
+
+ /**
+ * Stream this buffer into the provided content handler.
+ * If contentHandler object implements LexicalHandler, it will get lexical
+ * events as well.
+ */
+ public void toSAX(ContentHandler contentHandler) throws SAXException {
+ for (Iterator<SaxBit> i = saxbits.iterator(); i.hasNext();) {
+ SaxBit saxbit = i.next();
+ saxbit.send(contentHandler);
+ }
+ }
+
+ /**
+ * @return String value of the buffer
+ */
+ public String toString() {
+ // NOTE: This method is used in i18n XML bundle implementation
+ final StringBuffer value = new StringBuffer();
+ for (Iterator<SaxBit> i = saxbits.iterator(); i.hasNext();) {
+ final SaxBit saxbit = i.next();
+ if (saxbit instanceof Characters) {
+ ((Characters) saxbit).toString(value);
+ }
+ }
+
+ return value.toString();
+ }
+
+ /**
+ * Clear this buffer
+ */
+ public void recycle() {
+ saxbits.clear();
+ }
+
+ /**
+ * Dump buffer contents into the provided writer.
+ */
+ public void dump(Writer writer) throws IOException {
+ Iterator<SaxBit> i = saxbits.iterator();
+ while (i.hasNext()) {
+ final SaxBit saxbit = i.next();
+ saxbit.dump(writer);
+ }
+ writer.flush();
+ }
+
+ //
+ // Implementation Methods
+ //
+
+ /**
+ * Adds a SaxBit to the bits list
+ */
+ protected final void addBit(SaxBit bit) {
+ saxbits.add(bit);
+ }
+
+ /**
+ * Iterates through the bits list
+ */
+ protected final Iterator<SaxBit> bits() {
+ return saxbits.iterator();
+ }
+
+ /**
+ * SaxBit is a representation of the SAX event. Every SaxBit is immutable object.
+ */
+ interface SaxBit {
+ public void send(ContentHandler contentHandler) throws SAXException;
+ public void dump(Writer writer) throws IOException;
+ }
+
+ public final static class StartDocument implements SaxBit, Serializable {
+ public static final StartDocument SINGLETON = new StartDocument();
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ contentHandler.startDocument();
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[StartDocument]\n");
+ }
+ }
+
+ public final static class EndDocument implements SaxBit, Serializable {
+ public static final EndDocument SINGLETON = new EndDocument();
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ contentHandler.endDocument();
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[EndDocument]\n");
+ }
+ }
+
+ public final static class PI implements SaxBit, Serializable {
+ public final String target;
+ public final String data;
+
+ public PI(String target, String data) {
+ this.target = target;
+ this.data = data;
+ }
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ contentHandler.processingInstruction(target, data);
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[ProcessingInstruction] target=" + target + ",data=" + data + "\n");
+ }
+ }
+
+ public final static class StartDTD implements SaxBit, Serializable {
+ public final String name;
+ public final String publicId;
+ public final String systemId;
+
+ public StartDTD(String name, String publicId, String systemId) {
+ this.name = name;
+ this.publicId = publicId;
+ this.systemId = systemId;
+ }
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ if (contentHandler instanceof LexicalHandler)
+ ((LexicalHandler)contentHandler).startDTD(name, publicId, systemId);
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[StartDTD] name=" + name + ",publicId=" + publicId + ",systemId=" + systemId + "\n");
+ }
+ }
+
+ public final static class EndDTD implements SaxBit, Serializable {
+ public static final EndDTD SINGLETON = new EndDTD();
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ if (contentHandler instanceof LexicalHandler)
+ ((LexicalHandler)contentHandler).endDTD();
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[EndDTD]\n");
+ }
+ }
+
+ public final static class StartEntity implements SaxBit, Serializable {
+ public final String name;
+
+ public StartEntity(String name) {
+ this.name = name;
+ }
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ if (contentHandler instanceof LexicalHandler)
+ ((LexicalHandler)contentHandler).startEntity(name);
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[StartEntity] name=" + name + "\n");
+ }
+ }
+
+ public final static class EndEntity implements SaxBit, Serializable {
+ public final String name;
+
+ public EndEntity(String name) {
+ this.name = name;
+ }
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ if (contentHandler instanceof LexicalHandler)
+ ((LexicalHandler)contentHandler).endEntity(name);
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[EndEntity] name=" + name + "\n");
+ }
+ }
+
+ public final static class SkippedEntity implements SaxBit, Serializable {
+ public final String name;
+
+ public SkippedEntity(String name) {
+ this.name = name;
+ }
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ contentHandler.skippedEntity(name);
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[SkippedEntity] name=" + name + "\n");
+ }
+ }
+
+ public final static class StartPrefixMapping implements SaxBit, Serializable {
+ public final String prefix;
+ public final String uri;
+
+ public StartPrefixMapping(String prefix, String uri) {
+ this.prefix = prefix;
+ this.uri = uri;
+ }
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ contentHandler.startPrefixMapping(prefix, uri);
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[StartPrefixMapping] prefix=" + prefix + ",uri=" + uri + "\n");
+ }
+ }
+
+ public final static class EndPrefixMapping implements SaxBit, Serializable {
+ public final String prefix;
+
+ public EndPrefixMapping(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ contentHandler.endPrefixMapping(prefix);
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[EndPrefixMapping] prefix=" + prefix + "\n");
+ }
+ }
+
+ public final static class StartElement implements SaxBit, Serializable {
+ public final String namespaceURI;
+ public final String localName;
+ public final String qName;
+ public final Attributes attrs;
+
+ public StartElement(String namespaceURI, String localName, String qName, Attributes attrs) {
+ this.namespaceURI = namespaceURI;
+ this.localName = localName;
+ this.qName = qName;
+ this.attrs = new org.xml.sax.helpers.AttributesImpl(attrs);
+ }
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ contentHandler.startElement(namespaceURI, localName, qName, attrs);
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[StartElement] namespaceURI=" + namespaceURI + ",localName=" + localName + ",qName=" + qName + "\n");
+ for (int i = 0; i < attrs.getLength(); i++) {
+ writer.write(" [Attribute] namespaceURI=" + attrs.getURI(i) + ",localName=" + attrs.getLocalName(i) + ",qName=" + attrs.getQName(i) + ",type=" + attrs.getType(i) + ",value=" + attrs.getValue(i) + "\n");
+ }
+ }
+ }
+
+ public final static class EndElement implements SaxBit, Serializable {
+ public final String namespaceURI;
+ public final String localName;
+ public final String qName;
+
+ public EndElement(String namespaceURI, String localName, String qName) {
+ this.namespaceURI = namespaceURI;
+ this.localName = localName;
+ this.qName = qName;
+ }
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ contentHandler.endElement(namespaceURI, localName, qName);
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[EndElement] namespaceURI=" + namespaceURI + ",localName=" + localName + ",qName=" + qName + "\n");
+ }
+ }
+
+ public final static class Characters implements SaxBit, Serializable {
+ public final char[] ch;
+
+ public Characters(char[] ch, int start, int length) {
+ // make a copy so that we don't hold references to a potentially large array we don't control
+ this.ch = new char[length];
+ System.arraycopy(ch, start, this.ch, 0, length);
+ }
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ contentHandler.characters(ch, 0, ch.length);
+ }
+
+ public void toString(StringBuffer value) {
+ value.append(ch);
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[Characters] ch=" + new String(ch) + "\n");
+ }
+ }
+
+ public final static class Comment implements SaxBit, Serializable {
+ public final char[] ch;
+
+ public Comment(char[] ch, int start, int length) {
+ // make a copy so that we don't hold references to a potentially large array we don't control
+ this.ch = new char[length];
+ System.arraycopy(ch, start, this.ch, 0, length);
+ }
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ if (contentHandler instanceof LexicalHandler)
+ ((LexicalHandler)contentHandler).comment(ch, 0, ch.length);
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[Comment] ch=" + new String(ch) + "\n");
+ }
+ }
+
+ public final static class StartCDATA implements SaxBit, Serializable {
+ public static final StartCDATA SINGLETON = new StartCDATA();
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ if (contentHandler instanceof LexicalHandler)
+ ((LexicalHandler)contentHandler).startCDATA();
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[StartCDATA]\n");
+ }
+ }
+
+ public final static class EndCDATA implements SaxBit, Serializable {
+ public static final EndCDATA SINGLETON = new EndCDATA();
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ if (contentHandler instanceof LexicalHandler)
+ ((LexicalHandler)contentHandler).endCDATA();
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[EndCDATA]\n");
+ }
+ }
+
+ public final static class IgnorableWhitespace implements SaxBit, Serializable {
+ public final char[] ch;
+
+ public IgnorableWhitespace(char[] ch, int start, int length) {
+ // make a copy so that we don't hold references to a potentially large array we don't control
+ this.ch = new char[length];
+ System.arraycopy(ch, start, this.ch, 0, length);
+ }
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ contentHandler.ignorableWhitespace(ch, 0, ch.length);
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[IgnorableWhitespace] ch=" + new String(ch) + "\n");
+ }
+ }
+
+ public final static class XMLizableBit implements SaxBit, Serializable {
+ public final SaxBuffer xml;
+
+ public XMLizableBit(SaxBuffer xml) {
+ this.xml = xml;
+ }
+
+ public void send(ContentHandler contentHandler) throws SAXException {
+ this.xml.toSAX(new EmbeddedXMLPipe(contentHandler));
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[XMLizable] Begin nested SaxBuffer\n");
+ xml.dump(writer);
+ writer.write("[XMLizable] End nested SaxBuffer\n");
+ }
+ }
+}
Propchange: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/SaxBuffer.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/SaxBuffer.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/SaxBuffer.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/XMLUtils.java
URL: http://svn.apache.org/viewvc/cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/XMLUtils.java?rev=728704&view=auto
==============================================================================
--- cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/XMLUtils.java (added)
+++ cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/XMLUtils.java Mon Dec 22 06:49:59 2008
@@ -0,0 +1,617 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.xml.util;
+
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Properties;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.cocoon.xml.util.dom.DOMStreamer;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * XML utility methods.
+ *
+ * @version $Id$
+ */
+public class XMLUtils {
+
+ /**
+ * Empty attributes immutable object.
+ */
+ public static final Attributes EMPTY_ATTRIBUTES = new ImmutableAttributesImpl();
+
+ private static final SAXTransformerFactory FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
+ private static final Properties XML_FORMAT = createDefaultPropertiesForXML(false);
+ private static final Properties XML_FORMAT_NODECL = createDefaultPropertiesForXML(true);
+
+
+ // FIXME: parent parameter not used anymore
+ // Using parent because some dom implementations like jtidy are bugged,
+ // cannot get parent or delete child
+ public static void stripDuplicateAttributes(Node node, Node parent) {
+
+ // The output depends on the type of the node
+ switch(node.getNodeType()) {
+ case Node.DOCUMENT_NODE: {
+ Document doc = (Document)node;
+ Node child = doc.getFirstChild();
+ while(child != null) {
+ stripDuplicateAttributes(child, node);
+ child = child.getNextSibling();
+ }
+ break;
+ }
+
+ case Node.ELEMENT_NODE: {
+ Element elt = (Element) node;
+ NamedNodeMap attrs = elt.getAttributes();
+
+ ArrayList<Node> nodesToRemove = new ArrayList<Node>();
+ int nodesToRemoveNum = 0;
+
+ for (int i = 0; i < attrs.getLength(); i++) {
+ final Node a = attrs.item(i);
+
+ for (int j = 0; j < attrs.getLength(); j++) {
+ final Node b = attrs.item(j);
+
+ //if there are two attributes with same name
+ if (i != j && (a.getNodeName().equals(b.getNodeName()))) {
+ nodesToRemove.add(b);
+ nodesToRemoveNum++;
+ }
+ }
+ }
+
+ for (int i = 0; i < nodesToRemoveNum; i++) {
+ Attr nodeToDelete = (Attr) nodesToRemove.get(i);
+ Element nodeToDeleteParent = (Element) node; // nodeToDelete.getParentNode();
+ nodeToDeleteParent.removeAttributeNode(nodeToDelete);
+ }
+
+ nodesToRemove.clear();
+
+ Node child = elt.getFirstChild();
+ while (child != null) {
+ stripDuplicateAttributes(child, node);
+ child = child.getNextSibling();
+ }
+
+ break;
+ }
+
+ default:
+ //do nothing
+ break;
+ }
+ }
+
+ /**
+ * Method for static initializer
+ */
+ private static Properties createDefaultPropertiesForXML(boolean omitXMLDeclaration) {
+ final Properties format = new Properties();
+ format.put(OutputKeys.METHOD, "xml");
+ format.put(OutputKeys.OMIT_XML_DECLARATION, (omitXMLDeclaration ? "yes" : "no"));
+ format.put(OutputKeys.INDENT, "yes");
+ return format;
+ }
+
+ /**
+ * Create a new properties set for serializing xml.
+ * The omit xml declaration property can be controlled by the flag.
+ *
+ * <ul>
+ * <li>Method: xml
+ * <li>Omit xml declaration: according to the flag
+ * <li>Indent: yes
+ * </ul>
+ */
+ public static Properties createPropertiesForXML(boolean omitXMLDeclaration) {
+ /* Properties passed as parameters to the Properties constructor become "default properties".
+ But Xalan does not use the default properties, so they are lost.
+ Therefore, we must promote them to "set properties".
+ */
+ Properties propertiesForXML = new Properties(omitXMLDeclaration? XML_FORMAT_NODECL: XML_FORMAT);
+ for (Enumeration e = propertiesForXML.propertyNames(); e.hasMoreElements(); ) {
+ String propertyName = (String)e.nextElement();
+ propertiesForXML.setProperty(propertyName, propertiesForXML.getProperty(propertyName, ""));
+ }
+ return propertiesForXML;
+ }
+
+ /**
+ * Serialize a DOM node into a string using format created by
+ * <code>createPropertiesForXML(false)</code>.
+ *
+ * @see #createPropertiesForXML
+ */
+ public static String serializeNode(Node node)
+ throws TransformerException {
+ // Don't create new properties as we do not intend to modify defaults.
+ return serializeNode(node, XML_FORMAT);
+ }
+
+ /**
+ * Serialize a DOM node into a string.
+ * If the node is null the empty string is returned.
+ *
+ * @param format The format of the output to be used by SAX transformer.
+ * @see OutputKeys
+ */
+ public static String serializeNode(Node node, Properties format)
+ throws TransformerException {
+
+ try {
+ if (node == null) {
+ return "";
+ }
+
+ StringWriter writer = new StringWriter();
+ TransformerHandler transformerHandler;
+ transformerHandler = FACTORY.newTransformerHandler();
+ transformerHandler.getTransformer().setOutputProperties(format);
+ transformerHandler.setResult(new StreamResult(writer));
+ if (node.getNodeType() != Node.DOCUMENT_NODE) {
+ transformerHandler.startDocument();
+ }
+ DOMStreamer domStreamer = new DOMStreamer(transformerHandler, transformerHandler);
+ domStreamer.stream(node);
+ if (node.getNodeType() != Node.DOCUMENT_NODE) {
+ transformerHandler.endDocument();
+ }
+
+ return writer.toString();
+ } catch (TransformerException e) {
+ throw e;
+ } catch (SAXException e) {
+ throw new TransformerException("SAXException while streaming DOM node to SAX: " + e, e);
+ }
+ }
+
+ /**
+ * Add string data
+ *
+ * @param contentHandler The SAX content handler
+ * @param data The string data
+ */
+ public static void data(ContentHandler contentHandler,
+ String data)
+ throws SAXException {
+
+ contentHandler.characters(data.toCharArray(), 0, data.length());
+ }
+
+ /**
+ * Implementation of <xsp:expr> for <code>String</code> :
+ * outputs characters representing the value.
+ *
+ * @param contentHandler the SAX content handler
+ * @param text the value
+ */
+ public static void valueOf(ContentHandler contentHandler, String text)
+ throws SAXException {
+
+ if (text != null) {
+ data(contentHandler, text);
+ }
+ }
+
+ /**
+ * Implementation of <xsp:expr> for <code>SaxBuffer</code> :
+ * outputs the value by calling <code>v.toSax(contentHandler)</code>.
+ *
+ * @param contentHandler the SAX content handler
+ * @param v the XML fragment
+ */
+ public static void valueOf(ContentHandler contentHandler,
+ SaxBuffer v)
+ throws SAXException {
+
+ if (v != null) {
+ v.toSAX(contentHandler);
+ }
+ }
+
+ /**
+ * Implementation of <xsp:expr> for <code>org.w3c.dom.Node</code> :
+ * converts the Node to a SAX event stream.
+ *
+ * @param contentHandler the SAX content handler
+ * @param v the value
+ */
+ public static void valueOf(ContentHandler contentHandler, Node v)
+ throws SAXException {
+
+ if (v != null) {
+ DOMStreamer streamer = new DOMStreamer(contentHandler);
+ if (contentHandler instanceof LexicalHandler) {
+ streamer.setLexicalHandler((LexicalHandler)contentHandler);
+ }
+ streamer.stream(v);
+ }
+ }
+
+ /**
+ * Implementation of <xsp:expr> for <code>java.util.Collection</code> :
+ * outputs the value by calling <code>xspExpr()</code> on each element of the
+ * collection.
+ *
+ * @param contentHandler the SAX content handler
+ * @param v the XML fragment
+ */
+ public static void valueOf(ContentHandler contentHandler,
+ Collection v)
+ throws SAXException {
+
+ if (v != null) {
+ Iterator iterator = v.iterator();
+ while (iterator.hasNext()) {
+ valueOf(contentHandler, iterator.next());
+ }
+ }
+ }
+
+ /**
+ * Implementation of <xsp:expr> for <code>Object</code> depending on its class :
+ * <ul>
+ * <li>if it's an array, call <code>xspExpr()</code> on all its elements,</li>
+ * <li>if it's class has a specific <code>xspExpr()</code>implementation, use it,</li>
+ * <li>else, output it's string representation.</li>
+ * </ul>
+ *
+ * @param contentHandler the SAX content handler
+ * @param v the value
+ */
+ public static void valueOf(ContentHandler contentHandler, Object v)
+ throws SAXException {
+
+ if (v == null) {
+ return;
+ }
+
+ // Array: recurse over each element
+ if (v.getClass().isArray()) {
+ Object[] elements = (Object[]) v;
+
+ for (int i = 0; i < elements.length; i++) {
+ valueOf(contentHandler, elements[i]);
+ }
+ return;
+ }
+
+
+ // XMLizable
+ if (v instanceof SaxBuffer) {
+ valueOf(contentHandler, (SaxBuffer)v);
+ return;
+ }
+
+ // Node
+ if (v instanceof Node) {
+ valueOf(contentHandler, (Node)v);
+ return;
+ }
+
+ // Collection
+ if (v instanceof Collection) {
+ valueOf(contentHandler, (Collection)v);
+ return;
+ }
+
+ // Give up: hope it's a string or has a meaningful string representation
+ data(contentHandler, String.valueOf(v));
+ }
+
+ /**
+ * Create a start and endElement with a empty Namespace and without Attributes
+ *
+ * @param localName The local name (without prefix)
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #endElement(ContentHandler, String)
+ */
+ public static void createElement(ContentHandler contentHandler,
+ String localName)
+ throws SAXException {
+
+ startElement(contentHandler, localName);
+ endElement(contentHandler, localName);
+ }
+
+ /**
+ * Create a start and endElement with a empty Namespace and without Attributes
+ * The content of the Element is set to the stringValue parameter
+ *
+ * @param localName The local name (without prefix)
+ * @param stringValue The content of the Element
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #endElement(ContentHandler, String)
+ */
+ public static void createElement(ContentHandler contentHandler,
+ String localName,
+ String stringValue)
+ throws SAXException {
+
+ startElement(contentHandler, localName);
+ data(contentHandler, stringValue);
+ endElement(contentHandler, localName);
+ }
+
+ /**
+ * Create a start and endElement with a empty Namespace
+ *
+ * @param localName The local name (without prefix)
+ * @param atts The attributes attached to the element. If
+ * there are no attributes, it shall be an empty
+ * Attributes object.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #endElement(ContentHandler, String)
+ * @see org.xml.sax.Attributes
+ */
+ public static void createElement(ContentHandler contentHandler,
+ String localName,
+ Attributes atts)
+ throws SAXException {
+
+ startElement(contentHandler, localName, atts);
+ endElement(contentHandler, localName);
+ }
+
+ /**
+ * Create a start and endElement with a empty Namespace
+ * The content of the Element is set to the stringValue parameter
+ *
+ * @param localName The local name (without prefix)
+ * @param atts The attributes attached to the element. If
+ * there are no attributes, it shall be an empty
+ * Attributes object.
+ * @param stringValue The content of the Element
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #endElement(ContentHandler, String)
+ * @see org.xml.sax.Attributes
+ */
+ public static void createElement(ContentHandler contentHandler,
+ String localName,
+ Attributes atts,
+ String stringValue)
+ throws SAXException {
+
+ startElement(contentHandler, localName, atts);
+ data(contentHandler, stringValue);
+ endElement(contentHandler, localName);
+ }
+
+ /**
+ * Create a start and endElement without Attributes
+ *
+ * @param localName The local name (without prefix)
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #endElement(ContentHandler, String)
+ */
+ public static void createElementNS(ContentHandler contentHandler,
+ String namespaceURI,
+ String localName)
+ throws SAXException {
+
+ startElement(contentHandler, namespaceURI, localName);
+ endElement(contentHandler, namespaceURI, localName);
+ }
+
+ /**
+ * Create a start and endElement without Attributes
+ * The content of the Element is set to the stringValue parameter
+ *
+ * @param localName The local name (without prefix)
+ * @param stringValue The content of the Element
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #endElement(ContentHandler, String)
+ */
+ public static void createElementNS(ContentHandler contentHandler,
+ String namespaceURI,
+ String localName,
+ String stringValue)
+ throws SAXException {
+
+ startElement(contentHandler, namespaceURI, localName);
+ data(contentHandler, stringValue);
+ endElement(contentHandler, namespaceURI, localName);
+ }
+
+ /**
+ * Create a start and endElement
+ *
+ * @param localName The local name (without prefix)
+ * @param atts The attributes attached to the element. If
+ * there are no attributes, it shall be an empty
+ * Attributes object.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #endElement(ContentHandler, String)
+ * @see org.xml.sax.Attributes
+ */
+ public static void createElementNS(ContentHandler contentHandler,
+ String namespaceURI,
+ String localName,
+ Attributes atts)
+ throws SAXException {
+
+ startElement(contentHandler, namespaceURI, localName, atts);
+ endElement(contentHandler, namespaceURI, localName);
+ }
+
+ /**
+ * Create a start and endElement with a empty Namespace
+ * The content of the Element is set to the stringValue parameter
+ *
+ * @param localName The local name (without prefix)
+ * @param atts The attributes attached to the element. If
+ * there are no attributes, it shall be an empty
+ * Attributes object.
+ * @param stringValue The content of the Element
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #endElement(ContentHandler, String)
+ * @see org.xml.sax.Attributes
+ */
+ public static void createElementNS(ContentHandler contentHandler,
+ String namespaceURI,
+ String localName,
+ Attributes atts,
+ String stringValue)
+ throws SAXException {
+
+ startElement(contentHandler, namespaceURI, localName, atts);
+ data(contentHandler, stringValue);
+ endElement(contentHandler, namespaceURI, localName);
+ }
+
+
+ /**
+ * Create endElement with empty Namespace
+ *
+ * <p>For information on the names, see startElement.</p>
+ *
+ * @param localName The local name (without prefix)
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ */
+ public static void endElement(ContentHandler contentHandler,
+ String localName)
+ throws SAXException {
+
+ contentHandler.endElement("", localName, localName);
+ }
+
+ /**
+ * Create endElement
+ * Prefix must be mapped to empty String
+ *
+ * <p>For information on the names, see startElement.</p>
+ *
+ * @param localName The local name (without prefix)
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ */
+ public static void endElement(ContentHandler contentHandler,
+ String namespaceURI,
+ String localName)
+ throws SAXException {
+
+ contentHandler.endElement(namespaceURI, localName, localName);
+ }
+
+ /**
+ * Create a startElement with a empty Namespace and without Attributes
+ *
+ * @param localName The local name (without prefix)
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #endElement(ContentHandler, String)
+ */
+ public static void startElement(ContentHandler contentHandler,
+ String localName)
+ throws SAXException {
+
+ contentHandler.startElement("", localName, localName, EMPTY_ATTRIBUTES);
+ }
+
+ /**
+ * Create a startElement without Attributes
+ * Prefix must be mapped to empty String
+ *
+ * @param namespaceURI The Namespace URI
+ * @param localName The local name (without prefix)
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #endElement(ContentHandler, String)
+ */
+ public static void startElement(ContentHandler contentHandler,
+ String namespaceURI,
+ String localName)
+ throws SAXException {
+
+ contentHandler.startElement(namespaceURI, localName, localName, EMPTY_ATTRIBUTES);
+ }
+
+ /**
+ * Create a startElement with a empty Namespace
+ *
+ * @param localName The local name (without prefix)
+ * @param atts The attributes attached to the element. If
+ * there are no attributes, it shall be an empty
+ * Attributes object.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #endElement(ContentHandler, String)
+ * @see org.xml.sax.Attributes
+ */
+ public static void startElement(ContentHandler contentHandler,
+ String localName,
+ Attributes atts)
+ throws SAXException {
+
+ contentHandler.startElement("", localName, localName, atts);
+ }
+
+ /**
+ * Create a startElement with a empty Namespace
+ * Prefix must be mapped to empty String
+ *
+ * @param namespaceURI The Namespace URI
+ * @param localName The local name (without prefix)
+ * @param atts The attributes attached to the element. If
+ * there are no attributes, it shall be an empty
+ * Attributes object.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #endElement(ContentHandler, String)
+ * @see org.xml.sax.Attributes
+ */
+ public static void startElement(ContentHandler contentHandler,
+ String namespaceURI,
+ String localName,
+ Attributes atts)
+ throws SAXException {
+
+ contentHandler.startElement(namespaceURI, localName, localName, atts);
+ }
+}
Propchange: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/XMLUtils.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/XMLUtils.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/XMLUtils.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/dom/DOMBuilder.java
URL: http://svn.apache.org/viewvc/cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/dom/DOMBuilder.java?rev=728704&view=auto
==============================================================================
--- cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/dom/DOMBuilder.java (added)
+++ cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/dom/DOMBuilder.java Mon Dec 22 06:49:59 2008
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.xml.util.dom;
+
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+
+import org.apache.cocoon.xml.util.AbstractXMLPipe;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+/**
+ * The <code>DOMBuilder</code> is a utility class that will generate a W3C
+ * DOM Document from SAX events.
+ *
+ * @version $Id$
+ */
+public class DOMBuilder extends AbstractXMLPipe {
+
+ /** The default transformer factory shared by all instances */
+ protected static final SAXTransformerFactory FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
+
+ /** The transformer factory */
+ protected SAXTransformerFactory factory;
+
+ /** The listener */
+ protected Listener listener;
+
+ /** The result */
+ protected DOMResult result;
+
+ /** The parentNode */
+ protected Node parentNode;
+
+ /**
+ * Construct a new instance of this DOMBuilder.
+ */
+ public DOMBuilder() {
+ this((Listener) null, (Node) null);
+ }
+
+ /**
+ * Construct a new instance of this DOMBuilder.
+ */
+ public DOMBuilder(SAXTransformerFactory factory) {
+ this(factory, null, null);
+ }
+
+ /**
+ * Construct a new instance of this DOMBuilder.
+ */
+ public DOMBuilder(Listener listener) {
+ this(listener, null);
+ }
+
+ /**
+ * Constructs a new instance that appends nodes to the given parent node.
+ * <br/>
+ * <strong>Note:</strong> You cannot use a <code>Listener<code> when appending to a
+ * <code>Node</code>, because the notification occurs at <code>endDocument()</code>
+ * which does not happen here.
+ */
+ public DOMBuilder(Node parentNode) {
+ this(null, parentNode);
+ }
+
+ /**
+ * Construct a new instance of this DOMBuilder.
+ */
+ public DOMBuilder(Listener listener, Node parentNode) {
+ this((SAXTransformerFactory) null, listener, parentNode);
+ }
+
+ /**
+ * Construct a new instance of this DOMBuilder.
+ */
+ public DOMBuilder(SAXTransformerFactory factory, Listener listener, Node parentNode) {
+ super();
+ this.factory = factory == null? FACTORY: factory;
+ this.listener = listener;
+ this.parentNode = parentNode;
+ setup();
+ }
+
+ /**
+ * Setup this instance transformer and result objects.
+ */
+ private void setup() {
+ try {
+ TransformerHandler handler = this.factory.newTransformerHandler();
+ setContentHandler(handler);
+ setLexicalHandler(handler);
+ if (this.parentNode != null) {
+ this.result = new DOMResult(this.parentNode);
+ } else {
+ this.result = new DOMResult();
+ }
+ handler.setResult(this.result);
+ } catch (javax.xml.transform.TransformerException local) {
+ throw new RuntimeException("Fatal-Error: Unable to get transformer handler", local);
+ }
+ }
+
+ /**
+ * Recycle this builder, prepare for re-use.
+ */
+ public void recycle() {
+ super.recycle();
+ setup();
+ }
+
+ /**
+ * Return the newly built Document.
+ */
+ public Document getDocument() {
+ if (this.result == null || this.result.getNode() == null) {
+ return null;
+ } else if (this.result.getNode().getNodeType() == Node.DOCUMENT_NODE) {
+ return (Document) this.result.getNode();
+ } else {
+ return this.result.getNode().getOwnerDocument();
+ }
+ }
+
+ /**
+ * Receive notification of the end of a document.
+ *
+ * @exception SAXException If this method was not called appropriately.
+ */
+ public void endDocument() throws SAXException {
+ super.endDocument();
+ // Notify the listener
+ notifyListener();
+ }
+
+ /**
+ * Receive notification of a successfully completed DOM tree generation.
+ */
+ protected void notifyListener() throws SAXException {
+ if (this.listener != null) {
+ this.listener.notify(getDocument());
+ }
+ }
+
+ /**
+ * The Listener interface must be implemented by those objects willing to
+ * be notified of a successful DOM tree generation.
+ */
+ public interface Listener {
+
+ /**
+ * Receive notification of a successfully completed DOM tree generation.
+ */
+ void notify(Document doc) throws SAXException;
+ }
+}
Propchange: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/dom/DOMBuilder.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/dom/DOMBuilder.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/dom/DOMBuilder.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/dom/DOMStreamer.java
URL: http://svn.apache.org/viewvc/cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/dom/DOMStreamer.java?rev=728704&view=auto
==============================================================================
--- cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/dom/DOMStreamer.java (added)
+++ cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/dom/DOMStreamer.java Mon Dec 22 06:49:59 2008
@@ -0,0 +1,689 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.xml.util.dom;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXResult;
+
+import org.apache.cocoon.xml.util.AbstractXMLProducer;
+import org.apache.cocoon.xml.util.EmbeddedXMLPipe;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Element;
+import org.w3c.dom.EntityReference;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * The <code>DOMStreamer</code> is a utility class that will generate SAX
+ * events from a W3C DOM Document.
+ *
+ * <p>The DOMStreamer uses a different strategy based on the value of the
+ * normalizeNamespaces property:
+ * <ul>
+ * <li>if true (the default), the DOMStreamer will normalize namespace
+ * declarations (i.e. add missing xmlns attributes or correct them). See
+ * also {@link NamespaceNormalizingDOMStreamer}.
+ * <li>if false, the standard JAXP identity transformer is used.
+ * </ul>
+ *
+ * @version $Id$
+ */
+public class DOMStreamer {
+
+ /** The transformer factory shared by all instances (only used by DefaultDOMStreamer) */
+ private static final TransformerFactory FACTORY = TransformerFactory.newInstance();
+
+ /** Default value for normalizeNamespaces. */
+ private static final boolean DEFAULT_NORMALIZE_NAMESPACES = true;
+
+ /** Indicates whether namespace normalization should happen. */
+ protected boolean normalizeNamespaces = DEFAULT_NORMALIZE_NAMESPACES;
+
+ /** DOMStreamer used in case of namespace normalization. */
+ protected NamespaceNormalizingDOMStreamer namespaceNormalizingDOMStreamer = new NamespaceNormalizingDOMStreamer();
+
+ /** DOMStreamer used when namespace normalization should not explicitely happen. */
+ protected DefaultDOMStreamer defaultDOMStreamer = new DefaultDOMStreamer();
+
+ /**
+ * Create a new <code>DOMStreamer</code> instance.
+ */
+ public DOMStreamer() {
+ super();
+ }
+
+ /**
+ * Create a new <code>DOMStreamer</code> instance.
+ */
+ public DOMStreamer(ContentHandler content, LexicalHandler lexical) {
+ this();
+ setContentHandler(content);
+ setLexicalHandler(lexical);
+ }
+
+ /**
+ * Create a new <code>DOMStreamer</code> instance.
+ */
+ public DOMStreamer(ContentHandler content) {
+ this(content, content instanceof LexicalHandler ? (LexicalHandler) content : null);
+ }
+
+ /**
+ * Set the <code>ContentHandler</code> that will receive XML data.
+ */
+ public void setContentHandler(ContentHandler handler) {
+ defaultDOMStreamer.setContentHandler(handler);
+ namespaceNormalizingDOMStreamer.setContentHandler(handler);
+ }
+
+ /**
+ * Set the <code>LexicalHandler</code> that will receive XML data.
+ */
+ public void setLexicalHandler(LexicalHandler handler) {
+ defaultDOMStreamer.setLexicalHandler(handler);
+ namespaceNormalizingDOMStreamer.setLexicalHandler(handler);
+ }
+
+ /**
+ * Start the production of SAX events.
+ */
+ public void stream(Node node) throws SAXException {
+ if (normalizeNamespaces) {
+ namespaceNormalizingDOMStreamer.stream(node);
+ } else {
+ defaultDOMStreamer.stream(node);
+ }
+ }
+
+ public boolean isNormalizeNamespaces() {
+ return normalizeNamespaces;
+ }
+
+ public void setNormalizeNamespaces(boolean normalizeNamespaces) {
+ this.normalizeNamespaces = normalizeNamespaces;
+ }
+
+ public void recycle() {
+ defaultDOMStreamer.recycle();
+ namespaceNormalizingDOMStreamer.recycle();
+ normalizeNamespaces = DEFAULT_NORMALIZE_NAMESPACES;
+ }
+
+ /**
+ * Streams a DOM tree to SAX events and normalizes namespace declarations on the way.
+ *
+ * <p>The code in this class is based on the org.apache.xml.utils.TreeWalker class from Xalan,
+ * though it differs in some important ways.
+ *
+ * <p>This class will automatically fix up ("normalize") namespace declarations
+ * while streaming to SAX. The original DOM-tree is not modified. The algorithm
+ * used is described in
+ * <a href="http://www.w3.org/TR/2002/WD-DOM-Level-3-Core-20021022/namespaces-algorithms.html#normalizeDocumentAlgo">an appendix of the DOM Level 3 spec</a>.
+ *
+ * <p>This class will NOT check the correctness of namespaces, e.g. it will not
+ * check that the "xml" prefix is not misused etc.
+ *
+ */
+ public static class NamespaceNormalizingDOMStreamer extends AbstractXMLProducer {
+ /**
+ * Information about the current element. Used to remember the localName, qName
+ * and namespaceURI for generating the endElement event, and holds the namespaces
+ * declared on the element. This extra class is needed because we don't want to
+ * modify the DOM-tree itself. The currentElementInfo has a pointer to its parent
+ * elementInfo.
+ */
+ protected NamespaceNormalizingDOMStreamer.ElementInfo currentElementInfo;
+
+ /** Counter used when generating new namespace prefixes. */
+ protected int newPrefixCounter;
+
+ public void recycle() {
+ super.recycle();
+ currentElementInfo = null;
+ newPrefixCounter = 0;
+ }
+
+ /**
+ * Start the production of SAX events.
+ *
+ * <p>Perform a pre-order traversal non-recursive style.
+ *
+ * <p>Note that TreeWalker assumes that the subtree is intended to represent
+ * a complete (though not necessarily well-formed) document and, during a
+ * traversal, startDocument and endDocument will always be issued to the
+ * SAX listener.
+ *
+ * @param pos Node in the tree where to start traversal
+ */
+ protected void stream(Node pos) throws SAXException {
+
+ // Start document only if we're streaming a document
+ boolean isDoc = (pos.getNodeType() == Node.DOCUMENT_NODE);
+ if (isDoc) {
+ contentHandler.startDocument();
+ }
+
+ Node top = pos;
+ while (null != pos) {
+ startNode(pos);
+
+ Node nextNode = pos.getFirstChild();
+ while (null == nextNode) {
+ endNode(pos);
+
+ if (top.equals(pos)) {
+ break;
+ }
+
+ nextNode = pos.getNextSibling();
+ if (null == nextNode) {
+ pos = pos.getParentNode();
+
+ if ((null == pos) || (top.equals(pos))) {
+ if (null != pos) {
+ endNode(pos);
+ }
+
+ break;
+ }
+ }
+ }
+
+ pos = nextNode;
+ }
+
+ if (isDoc) {
+ contentHandler.endDocument();
+ }
+ }
+
+ private final void dispatchChars(Node node) throws SAXException {
+ final String data = ((Text) node).getData();
+ if ( data != null ) {
+ contentHandler.characters(data.toCharArray(), 0, data.length());
+ }
+ }
+
+ /**
+ * Start processing given node
+ *
+ * @param node Node to process
+ */
+ protected void startNode(Node node) throws SAXException {
+
+ switch (node.getNodeType()) {
+ case Node.COMMENT_NODE:
+ {
+ if (lexicalHandler != null) {
+ final String data = ((Comment) node).getData();
+ if ( data != null ) {
+ lexicalHandler.comment(data.toCharArray(), 0, data.length());
+ }
+ }
+ }
+ break;
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ // ??;
+ case Node.DOCUMENT_NODE:
+ break;
+ case Node.ELEMENT_NODE:
+ NamedNodeMap atts = node.getAttributes();
+ int nAttrs = atts.getLength();
+
+ // create a list of localy declared namespace prefixes
+ currentElementInfo = new NamespaceNormalizingDOMStreamer.ElementInfo(currentElementInfo);
+ for (int i = 0; i < nAttrs; i++) {
+ Node attr = atts.item(i);
+ String attrName = attr.getNodeName();
+
+ if (attrName.equals("xmlns") || attrName.startsWith("xmlns:")) {
+ int index;
+ String prefix = (index = attrName.indexOf(":")) < 0
+ ? "" : attrName.substring(index + 1);
+
+ currentElementInfo.put(prefix, attr.getNodeValue());
+ }
+ }
+
+ String namespaceURI = node.getNamespaceURI();
+ String prefix = node.getPrefix();
+ String localName = node.getLocalName();
+
+ if (localName == null) {
+ // this is an element created with createElement instead of createElementNS
+ String[] prefixAndLocalName = getPrefixAndLocalName(node.getNodeName());
+ prefix = prefixAndLocalName[0];
+ localName = prefixAndLocalName[1];
+ // note: if prefix is null, there can still be a default namespace...
+ namespaceURI = getNamespaceForPrefix(prefix, (Element)node);
+ }
+
+ if (namespaceURI != null) {
+ // no prefix means: make this the default namespace
+ if (prefix == null) {
+ prefix = "";
+ }
+ // check that is declared
+ String uri = currentElementInfo.findNamespaceURI(prefix);
+ if ( uri != null && uri.equals(namespaceURI) ) {
+ // System.out.println("namespace is declared");
+ // prefix is declared correctly, do nothing
+ //} else if (uri != null) {
+ // System.out.println("prefix is declared with other namespace, overwriting it");
+ // prefix exists but is bound to another namespace, overwrite it
+ // currentElementInfo.put(prefix, namespaceURI);
+ } else {
+ // System.out.println("prefix is not yet declared, declaring it now");
+ currentElementInfo.put(prefix, namespaceURI);
+ }
+ } else {
+ // element has no namespace
+ // check if there is a default namespace, if so undeclare it
+ String uri = currentElementInfo.findNamespaceURI("");
+ if (uri != null && uri.trim().length() > 0 ) {
+ // System.out.println("undeclaring default namespace");
+ currentElementInfo.put("", "");
+ }
+ }
+
+ // SAX uses empty string to denote no namespace, while DOM uses null.
+ if (namespaceURI == null)
+ namespaceURI = "";
+
+ String qName;
+ if (prefix != null && prefix.trim().length() > 0 ) {
+ qName = prefix + ":" + localName;
+ } else {
+ qName = localName;
+ }
+
+ // make the attributes
+ AttributesImpl newAttrs = new AttributesImpl();
+ for (int i = 0; i < nAttrs; i++) {
+ Node attr = atts.item(i);
+ String attrName = attr.getNodeName();
+ String assignedAttrPrefix = null;
+
+ // only do non-namespace attributes
+ if (!(attrName.equals("xmlns") || attrName.startsWith("xmlns:"))) {
+ String attrPrefix;
+ String attrLocalName;
+ String attrNsURI;
+
+ if (attr.getLocalName() == null) {
+ // this is an attribute created with setAttribute instead of setAttributeNS
+ String[] prefixAndLocalName = getPrefixAndLocalName(attrName);
+ attrPrefix = prefixAndLocalName[0];
+ // the statement below causes the attribute to keep its prefix even if it is not
+ // bound to a namespace (to support pre-namespace XML).
+ assignedAttrPrefix = attrPrefix;
+ attrLocalName = prefixAndLocalName[1];
+ // note: if prefix is null, the attribute has no namespace (namespace defaulting
+ // does not apply to attributes)
+ if (attrPrefix != null)
+ attrNsURI = getNamespaceForPrefix(attrPrefix, (Element)node);
+ else
+ attrNsURI = null;
+ } else {
+ attrLocalName = attr.getLocalName();
+ attrPrefix = attr.getPrefix();
+ attrNsURI = attr.getNamespaceURI();
+ }
+
+ if (attrNsURI != null) {
+ String declaredUri = currentElementInfo.findNamespaceURI(attrPrefix);
+ // if the prefix is null, or the prefix has not been declared, or conflicts with an in-scope binding
+ if (declaredUri == null || !declaredUri.equals(attrNsURI)) {
+ String availablePrefix = currentElementInfo.findPrefix(attrNsURI);
+ if (availablePrefix != null && !availablePrefix.equals(""))
+ assignedAttrPrefix = availablePrefix;
+ else {
+ if (attrPrefix != null && declaredUri == null) {
+ // prefix is not null and is not yet declared: declare it
+ assignedAttrPrefix = attrPrefix;
+ currentElementInfo.put(assignedAttrPrefix, attrNsURI);
+ } else {
+ // attribute has no prefix (which is not allowed for namespaced attributes) or
+ // the prefix is already bound to something else: generate a new prefix
+ newPrefixCounter++;
+ assignedAttrPrefix = "NS" + newPrefixCounter;
+ currentElementInfo.put(assignedAttrPrefix, attrNsURI);
+ }
+ }
+ } else {
+ assignedAttrPrefix = attrPrefix;
+ }
+ }
+
+ String assignedAttrNsURI = attrNsURI != null ? attrNsURI : "";
+ String attrQName;
+ if (assignedAttrPrefix != null) {
+ attrQName = assignedAttrPrefix + ":" + attrLocalName;
+ } else {
+ attrQName = attrLocalName;
+ }
+ newAttrs.addAttribute(assignedAttrNsURI, attrLocalName, attrQName, "CDATA", attr.getNodeValue());
+ }
+ }
+
+ // add local namespace declaration and fire startPrefixMapping events
+ if (currentElementInfo.namespaceDeclarations != null && currentElementInfo.namespaceDeclarations.size() > 0) {
+ Iterator<Map.Entry<String, String>> localNsDeclIt = currentElementInfo.namespaceDeclarations.entrySet().iterator();
+ while (localNsDeclIt.hasNext()) {
+ Map.Entry<String, String> entry = localNsDeclIt.next();
+ String pr = entry.getKey();
+ String ns = entry.getValue();
+ // the following lines enable the creation of explicit xmlns attributes
+ //String pr1 = pr.equals("") ? "xmlns" : pr;
+ //String qn = pr.equals("") ? "xmlns" : "xmlns:" + pr;
+ //newAttrs.addAttribute("", pr1, qn, "CDATA", ns);
+ // System.out.println("starting prefix mapping for prefix " + pr + " for " + ns);
+ contentHandler.startPrefixMapping(pr, ns);
+ }
+ }
+
+ contentHandler.startElement(namespaceURI, localName, qName, newAttrs);
+
+ currentElementInfo.localName = localName;
+ currentElementInfo.namespaceURI = namespaceURI;
+ currentElementInfo.qName = qName;
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ {
+ ProcessingInstruction pi = (ProcessingInstruction) node;
+ contentHandler.processingInstruction(pi.getNodeName(), pi.getData());
+ }
+ break;
+ case Node.CDATA_SECTION_NODE:
+ {
+ if (lexicalHandler != null)
+ lexicalHandler.startCDATA();
+
+ dispatchChars(node);
+
+ if (lexicalHandler != null)
+ lexicalHandler.endCDATA();
+ }
+ break;
+ case Node.TEXT_NODE:
+ {
+ dispatchChars(node);
+ }
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ {
+ EntityReference eref = (EntityReference) node;
+
+ if (lexicalHandler != null) {
+ lexicalHandler.startEntity(eref.getNodeName());
+ } else {
+ // warning("Can not output entity to a pure SAX ContentHandler");
+ }
+ }
+ break;
+ default :
+ }
+ }
+
+ /**
+ * Searches the namespace for a given namespace prefix starting from a
+ * given Element.
+ *
+ * <p>Note that this resolves the prefix in the orginal DOM-tree, not in
+ * the {@link ElementInfo} objects. This is used to resolve prefixes
+ * of elements or attributes created with createElement or setAttribute
+ * instead of createElementNS or setAttributeNS.
+ *
+ * <p>The code in this method is largely based on
+ * org.apache.xml.utils.DOMHelper.getNamespaceForPrefix() (from Xalan).
+ *
+ * @param prefix the prefix to look for, can be empty or null to find the
+ * default namespace
+ *
+ * @return the namespace, or null if not found.
+ */
+ public String getNamespaceForPrefix(String prefix, Element namespaceContext) {
+ int type;
+ Node parent = namespaceContext;
+ String namespace = null;
+
+ if (prefix == null)
+ prefix = "";
+
+ if (prefix.equals("xml")) {
+ namespace = "http://www.w3.org/XML/1998/namespace";
+ } else if(prefix.equals("xmlns")) {
+ namespace = "http://www.w3.org/2000/xmlns/";
+ } else {
+ // Attribute name for this prefix's declaration
+ String declname = (prefix.length() == 0) ? "xmlns" : "xmlns:" + prefix;
+
+ // Scan until we run out of Elements or have resolved the namespace
+ while ((null != parent)
+ && (((type = parent.getNodeType()) == Node.ELEMENT_NODE)
+ || (type == Node.ENTITY_REFERENCE_NODE))) {
+ if (type == Node.ELEMENT_NODE) {
+ Attr attr=((Element)parent).getAttributeNode(declname);
+ if (attr != null) {
+ namespace = attr.getNodeValue();
+ break;
+ }
+ }
+ parent = parent.getParentNode();
+ }
+ }
+ return namespace;
+ }
+
+ /**
+ * Splits a nodeName into a prefix and a localName
+ *
+ * @return an array containing two elements, the first one is the prefix (can be null), the
+ * second one is the localName
+ */
+ private String[] getPrefixAndLocalName(String nodeName) {
+ String prefix, localName;
+ int colonPos = nodeName.indexOf(":");
+ if (colonPos != -1) {
+ prefix = nodeName.substring(0, colonPos);
+ localName = nodeName.substring(colonPos + 1, nodeName.length());
+ } else {
+ prefix = null;
+ localName = nodeName;
+ }
+ return new String[] {prefix, localName};
+ }
+
+ /**
+ * End processing of given node
+ *
+ * @param node Node we just finished processing
+ */
+ protected void endNode(Node node) throws org.xml.sax.SAXException {
+
+ switch (node.getNodeType()) {
+ case Node.ELEMENT_NODE:
+ contentHandler.endElement(currentElementInfo.namespaceURI,
+ currentElementInfo.localName, currentElementInfo.qName);
+
+ // generate endPrefixMapping events if needed
+ if (currentElementInfo.namespaceDeclarations != null && currentElementInfo.namespaceDeclarations.size() > 0) {
+ Iterator<Map.Entry<String, String>> namespaceIt = currentElementInfo.namespaceDeclarations.entrySet().iterator();
+ while (namespaceIt.hasNext()) {
+ Map.Entry<String, String> entry = namespaceIt.next();
+ contentHandler.endPrefixMapping(entry.getKey());
+ //System.out.println("ending prefix mapping " + (String) entry.getKey());
+ }
+ }
+ currentElementInfo = currentElementInfo.parent;
+ break;
+ case Node.DOCUMENT_NODE:
+ case Node.CDATA_SECTION_NODE:
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ {
+ EntityReference eref = (EntityReference) node;
+
+ if (lexicalHandler != null) {
+ lexicalHandler.endEntity(eref.getNodeName());
+ }
+ }
+ break;
+ default :
+ }
+ }
+
+ public static class ElementInfo {
+ public String localName;
+ public String namespaceURI;
+ public String qName;
+ public Map<String, String> namespaceDeclarations;
+ public ElementInfo parent;
+
+ public ElementInfo(ElementInfo parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Declare a new namespace prefix on this element, possibly overriding
+ * an existing one.
+ */
+ public void put(String prefix, String namespaceURI) {
+ if (namespaceDeclarations == null)
+ namespaceDeclarations = new HashMap<String, String>();
+ namespaceDeclarations.put(prefix, namespaceURI);
+ }
+
+ /**
+ * Finds a prefix declared on this element.
+ */
+ public String getPrefix(String namespaceURI) {
+ if (namespaceDeclarations == null || namespaceDeclarations.size() == 0)
+ return null;
+ // note: there could be more than one prefix for the same namespaceURI, but
+ // we return the first found one.
+ Iterator<Map.Entry<String, String>> it = namespaceDeclarations.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<String, String> entry = it.next();
+ if (entry.getValue().equals(namespaceURI))
+ return entry.getKey();
+ }
+ return null;
+ }
+
+ /**
+ * Finds a namespace URI declared on this element.
+ */
+ public String getNamespaceURI(String prefix) {
+ if (namespaceDeclarations == null || namespaceDeclarations.size() == 0)
+ return null;
+
+ return namespaceDeclarations.get(prefix);
+ }
+
+ /**
+ * Finds a prefix declaration on this element or containing elements.
+ */
+ public String findPrefix(String namespaceURI) {
+ if (namespaceDeclarations != null && namespaceDeclarations.size() != 0) {
+ String prefix = getPrefix(namespaceURI);
+ if (prefix != null) {
+ return prefix;
+ }
+ }
+ if (parent != null) {
+ return parent.findPrefix(namespaceURI);
+ }
+ return null;
+ }
+
+ /**
+ * Finds a namespace declaration on this element or containing elements.
+ */
+ public String findNamespaceURI(String prefix) {
+ if (namespaceDeclarations != null && namespaceDeclarations.size() != 0) {
+ String uri = namespaceDeclarations.get(prefix);
+ if (uri != null) {
+ return uri;
+ }
+ }
+ if (parent != null) {
+ return parent.findNamespaceURI(prefix);
+ }
+ return null;
+ }
+ }
+ }
+
+ /**
+ * The <code>DefaultDOMStreamer</code> is a utility class that will generate SAX
+ * events from a W3C DOM Document.
+ */
+ public static class DefaultDOMStreamer extends AbstractXMLProducer {
+
+ /** The private transformer for this instance */
+ protected Transformer transformer;
+
+ /**
+ * Start the production of SAX events.
+ */
+ public void stream(Node node)
+ throws SAXException {
+ if (this.transformer == null) {
+ try {
+ this.transformer = FACTORY.newTransformer();
+ } catch (TransformerConfigurationException e) {
+ throw new SAXException(e);
+ }
+ }
+ DOMSource source = new DOMSource(node);
+
+ ContentHandler handler;
+ if (node.getNodeType() == Node.DOCUMENT_NODE) {
+ // Pass all SAX events
+ handler = contentHandler;
+ } else {
+ // Strip start/endDocument
+ handler = new EmbeddedXMLPipe(contentHandler);
+ }
+
+ SAXResult result = new SAXResult(handler);
+ result.setLexicalHandler(lexicalHandler);
+
+ try {
+ transformer.transform(source, result);
+ } catch (TransformerException e) {
+ throw new SAXException(e);
+ }
+ }
+ }
+}
Propchange: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/dom/DOMStreamer.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/dom/DOMStreamer.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: cocoon/whiteboard/xml-utils/src/main/java/org/apache/cocoon/xml/util/dom/DOMStreamer.java
------------------------------------------------------------------------------
svn:mime-type = text/plain