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

org.exolab.castor.types.AnyNode Maven / Gradle / Ivy

Go to download

The core XML data binding framework with support for marshalling Java objects to and unmarshalling from XML documents.

The newest version!
/*
 * Redistribution and use of this software and associated documentation ("Software"), with or
 * without modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
 * must also contain a copy of this document.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided with
 * the distribution.
 *
 * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
 * without prior written permission of Intalio, Inc. For written permission, please contact
 * [email protected].
 *
 * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
 * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
 * Intalio, Inc.
 *
 * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
 *
 * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Copyright 2001 (C) Intalio, Inc. All Rights Reserved.
 *
 * $Id$ Date Author Changes 04/24/2001 Arnaud Blandin Rewrited from scratch 04/22/2001 Arnaud
 * Blandin Clean-up and support of comments 04/04/2001 Arnaud Blandin Created
 */
package org.exolab.castor.types;

import java.io.StringWriter;
import java.util.Stack;

import org.castor.xml.BackwardCompatibilityContext;
import org.exolab.castor.xml.Serializer;
import org.exolab.castor.xml.util.AnyNode2SAX;

/**
 * A class used to represent an XML node. This is an alternative to DOM which is too heavy for our
 * purpose (mainly handle XML Fragment when {@literal } is used in an XML schema). The model is
 * based on XPath Node. An AnyNode can be a:
 * 
    *
  • ELEMENT
  • *
  • ATTRIBUTE
  • *
  • NAMESPACE
  • *
  • COMMENT
  • *
  • TEXT
  • *
  • PROCESSING INSTRUCTION
  • *
* * @author Assaf Arkin * @author Arnaud Blandin * @author Keith Visco * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $ */ public final class AnyNode implements java.io.Serializable { // TODO Processing Instructions // TODO Full handling of namespaces /** SerialVersionUID */ private static final long serialVersionUID = -4104117996051705975L; /** * The prefix for XML namespace */ private final static String XMLNS_PREFIX = "xmlns"; /** * Representation for an element node. */ public static final short ELEMENT = 1; /** * Representation for an attribute node. */ public static final short ATTRIBUTE = 2; /** * Representation for a Namespace node. */ public static final short NAMESPACE = 3; /** * Representation for a processing instruction node. */ public static final short PI = 4; /** * Representation for a comment node. */ public static final short COMMENT = 5; /** * Representation for a text node. */ public static final short TEXT = 6; /** * The type of the current node. ELEMENT is the default value. */ private short _nodeType = ELEMENT; /** * The next sibling of this AnyNode */ private AnyNode _nextSiblingNode = null; /** * The first child of this AnyNode */ private AnyNode _firstChildNode = null; /** * the local name of the current node. */ private String _localName; /** * the Namespace URI of the current node */ private String _uri; /** * The prefix of the Namespace */ private String _prefix; /** * A stack used for avoiding endless loop in toString() */ private static Stack _elements; /** * The namespace context used in the toString() */ /** * The value of this node defined as follow: *
    *
  • for an element the value is its TEXT NODE value (if any)
  • *
  • for an attribute the value is the value of the attribute
  • *
  • for a text node it is the character data
  • *
  • for a namespace it is the namespace URI that is being bound to the namespace prefix
  • *
  • for a comment it is the content of the comment not including the opening <!-- and the * closing -->.
  • *
*/ private String _value; /** * Default constructor: creates an empty element node */ public AnyNode() { this(ELEMENT, null, null, null, null); } /** * Creates a node given all the necessary information: type, localName, prefix, uri and value. * This constructor is not user-friendly and launched RunTime exception is you try to instantiate * non-valid node. * * @param type the node type. * @param localName the name of the node. * @param prefix the prefix if any of the namespace. * @param uri the namespace uri of this node. * @param value the value of this node. */ public AnyNode(short type, String localName, String prefix, String uri, String value) { if ((type > 6) && (type < 1)) { throw new IllegalArgumentException("Illegal node type"); } _nodeType = type; // comment and text nodes don't have name if ((type > PI) && (localName != null)) { String err = "This node can not have a local name"; throw new IllegalArgumentException(err); } _localName = localName; // for comment, pi or text we should have no namespaces if ((type > NAMESPACE) && ((uri != null) || (prefix != null))) { String err = "This node can not handle namespace"; throw new IllegalArgumentException(err); } _uri = uri; _prefix = prefix; // attributes can not be namespaces if (type == AnyNode.ATTRIBUTE) if (localName.startsWith(XMLNS_PREFIX)) { String err = "Namespaces can't be used as attributes."; throw new IllegalArgumentException(err); } // you can't set value for element if ((type == ELEMENT) && (value != null)) { String err = "You can't set a value for this node type"; throw new IllegalArgumentException(err); } _value = value; } /** * Adds an AnyNode to the current node * * @param node the node to append */ public void addAnyNode(AnyNode node) { if (node == null) { throw new IllegalArgumentException("null argument in addAnyNode"); } switch (node.getNodeType()) { case ATTRIBUTE: addAttribute(node); break; case NAMESPACE: addNamespace(node); break; default: addChild(node); break; } } /** *

* Adds a child AnyNode to this node. A 'child' can be either an ELEMENT node, a COMMENT node, a * TEXT node or a PROCESSING INSTRUCTION. If the current node already has a child then the node to * add will be append as a sibling. *

* Note: you cannot add a child to a TEXT node. * * @param node the node to add. */ public void addChild(AnyNode node) { if (node == null) { throw new IllegalArgumentException("null argument in appendChild"); } if (node.getNodeType() == ATTRIBUTE || node.getNodeType() == NAMESPACE) { throw new IllegalArgumentException("An Attribute or an Namespace can't be added as a child"); } if (this.getNodeType() == TEXT) { throw new IllegalArgumentException("a TEXT node can't have children."); } if (_firstChildNode == null) { _firstChildNode = node; } else if (_firstChildNode.getNodeType() == ATTRIBUTE || _firstChildNode.getNodeType() == NAMESPACE) { _firstChildNode.addChild(node); } else { _firstChildNode.appendSibling(node); } } /** * Adds an attribute to the current node. * * @param node the attribute to add. */ public void addAttribute(AnyNode node) { if (node == null) { throw new IllegalArgumentException("null argument in addAttribute"); } if (node.getNodeType() != ATTRIBUTE) { throw new IllegalArgumentException("Only attribute can be added as an attribute"); } if (_firstChildNode == null) { _firstChildNode = node; } else { if (_firstChildNode.getNodeType() == ATTRIBUTE) { // if we reach an attribute then we add the node as its sibling _firstChildNode.appendSibling(node); } else if (_firstChildNode.getNodeType() == NAMESPACE) { // if we reach an namespace the attributre should be added to // the first child of the namespace _firstChildNode.addAttribute(node); } else { // unplug the current firstNode to add a new one node.addChild(_firstChildNode); _firstChildNode = node; } } } // addAttribute /** * Appends an namespace to the current node. * * @param node the attribute to add. */ public void addNamespace(AnyNode node) { if (node == null) { throw new IllegalArgumentException("null argument in addNamespace"); } if (node.getNodeType() != NAMESPACE) { throw new IllegalArgumentException("Only namespace can be added as an namespace"); } if (_firstChildNode == null) { _firstChildNode = node; } else { if (_firstChildNode.getNodeType() == NAMESPACE) { // if we reach an namepace then we add the node as its sibling _firstChildNode.appendSibling(node); } else if (_firstChildNode.getNodeType() == ATTRIBUTE) { // if we reach an attribute the attributre should be added to // the first child of the attribute _firstChildNode.addNamespace(node); } else { // unplug the current firstNode to add a new one node.addChild(_firstChildNode); _firstChildNode = node; } } } // addNamespace /** * Returns the first attribute of the current ELEMENT node or null. The next attribute,if any,is * the sibling of the returned node. */ public AnyNode getFirstAttribute() { if (this.getNodeType() != ELEMENT) { String err = "This node type can not contain attributes"; throw new UnsupportedOperationException(err); } boolean found = false; AnyNode tempNode = this.getFirstChildNode(); while (tempNode != null && !found) { short type = tempNode.getNodeType(); // if the child is not an attribute or a namespace // this element does not have any attribute if (type == ELEMENT || type == COMMENT || type == TEXT || type == PI) { tempNode = null; } else if (type == NAMESPACE) { tempNode = tempNode.getFirstChildNode(); } else { found = true; } } return tempNode; } /** * Returns the first namespace of the current ELEMENT node or null. The next attribute if any is * the sibling of the returned node. * * @return the first namespace of the current ELEMENT node or null. */ public AnyNode getFirstNamespace() { if (this.getNodeType() != ELEMENT) { String err = "This node type can not contain namespaces"; throw new UnsupportedOperationException(err); } AnyNode tempNode = this.getFirstChildNode(); boolean found = false; while (tempNode != null && !found) { short type = tempNode.getNodeType(); // if the child is not an attribute or a namespace // this element does not have any namespace if (type == ELEMENT || type == COMMENT || type == TEXT || type == PI) { tempNode = null; } else if (type == ATTRIBUTE) { tempNode = tempNode.getFirstChildNode(); } else { found = true; } } return tempNode; } /** * Returns the first Child node of this node. A 'child' can be either an ELEMENT node, a COMMENT * node, a TEXT node or a PROCESSING INSTRUCTION. * * @return the first child of this node */ public AnyNode getFirstChild() { // an ATTRIBUTE or a NAMESPACE can not // have children if (this.getNodeType() == ATTRIBUTE || this.getNodeType() == NAMESPACE) { return null; } // loop througth the first two (in the worst case) nodes // and then return the firstChild if any AnyNode tempNode = this.getFirstChildNode(); boolean found = false; while (tempNode != null && !found) { short type = tempNode.getNodeType(); if (type == ELEMENT || type == COMMENT || type == TEXT || type == PI) { found = true; } else if (type == ATTRIBUTE || type == NAMESPACE) { tempNode = tempNode.getFirstChildNode(); } } return tempNode; } /** * Returns the next sibling of the current node. When the AnyNode is an ATTRIBUTE, it will return * the next ATTRIBUTE node. When the AnyNode is a NAMESPACE, it will return the next NAMESPACE * node. * * @return the next sibling of the current node */ public AnyNode getNextSibling() { return _nextSiblingNode; } /** * Returns the type of this node. * * @return The type of this node */ public short getNodeType() { return _nodeType; } /** * Returns the local name of the node. Returns the local name of an element or attribute, the * prefix of a namespace node, the target of a processing instruction, or null for all other node * types. * * @return The local name of the node, or null if the node has no name */ public String getLocalName() { return _localName; } /** * Returns the namespace URI of the node. Returns the namespace URI of an element, attribute or * namespace node, or null for all other node types. * * @return The namespace URI of the node, or null if the node has no namespace URI */ public String getNamespaceURI() { return _uri; } /** * Returns the string value of the node. The string value of a text node or an attribute node is * its text value. The string value of an element or a root node is the concatenation of the * string value of all its child nodes. The string value of a namespace node is its namespace URI. * The string value of a processing instruction is the instruction, and the string value of a * comment is the comment text. * * @return The string value of the node */ public String getStringValue() { switch (_nodeType) { case ATTRIBUTE: case TEXT: return _value; case NAMESPACE: return _uri; // not yet supported case PI: return ""; case COMMENT: return _value; case ELEMENT: StringBuffer result = new StringBuffer(4096); AnyNode tempNode = this.getNextSibling(); while (tempNode != null && tempNode.getNodeType() == TEXT) { result.append(tempNode.getStringValue()); tempNode = tempNode.getNextSibling(); } tempNode = this.getFirstChild(); while (tempNode != null) { result.append(tempNode.getStringValue()); tempNode = tempNode.getNextSibling(); } tempNode = null; return result.toString(); default: return null; } } /** * Returns the namespace prefix associated with the namespace URI of this node. Returns null if no * prefix. is defined for this namespace URI. Returns an empty string if the default prefix is * associated with this namespace URI. * * @return The namespace prefix, or null */ public String getNamespacePrefix() { return _prefix; } /** * Returns the String representation of this AnyNode. The String representation is a xml * well-formed fragment corresponding to the representation of this node. * * @return the String representation of this AnyNode. */ public String toString() { Serializer serializer = new BackwardCompatibilityContext().getSerializer(); if (serializer == null) { throw new RuntimeException("Unable to obtain serializer"); } StringWriter writer = new StringWriter(); serializer.setOutputCharStream(writer); try { AnyNode2SAX.fireEvents(this, serializer.asDocumentHandler()); } catch (java.io.IOException ioe) { return privateToString(); } catch (org.xml.sax.SAXException saxe) { throw new RuntimeException(saxe.getMessage()); } return writer.toString(); } private String privateToString() { StringBuilder sb = new StringBuilder(4096); if (_elements == null) { _elements = new Stack<>(); } // check the Stack too see if we have // already proceed the node if (_elements.search(this) == -1) { _elements.push(this); if (this.getNodeType() == ELEMENT) { // open the tag sb.append('<'); String prefix = getNamespacePrefix(); if (prefix != null) { sb.append(prefix).append(':'); } prefix = null; sb.append(getLocalName()); // append the attributes AnyNode tempNode = this.getFirstAttribute(); while (tempNode != null) { sb.append(' ').append(tempNode.getLocalName()).append("='") .append(tempNode.getStringValue()).append('\''); tempNode = tempNode.getNextSibling(); } // append the namespaces tempNode = this.getFirstNamespace(); while (tempNode != null) { sb.append(' ').append(XMLNS_PREFIX); prefix = tempNode.getNamespacePrefix(); if (prefix != null && prefix.length() != 0) { sb.append(':').append(prefix); } sb.append("='").append(tempNode.getNamespaceURI()).append('\''); tempNode = tempNode.getNextSibling(); } // namespaceNode tempNode = this.getFirstChild(); if (tempNode != null) { sb.append('>'); while (tempNode != null) { sb.append(tempNode.privateToString()); tempNode = tempNode.getNextSibling(); } // close the tag sb.append("'); } else { sb.append("/>"); } } else { sb.append(this.getStringValue()); } } return sb.toString(); }// toString() /** * Appends a sibling AnyNode to the current node. The node to append will be added at the end of * the sibling branch. * * @param node the node to add */ protected void appendSibling(AnyNode node) { if (node == null) { throw new IllegalArgumentException(); } if (((node.getNodeType() == ATTRIBUTE) || (node.getNodeType() == NAMESPACE)) && (this.getNodeType() != node.getNodeType())) { String err = "a NAMESPACE or an ATTRIBUTE can only be add as a sibling to a node of the same type"; throw new UnsupportedOperationException(err); } if (_nextSiblingNode == null) { // if we already have a TEXT node -> merge if ((node.getNodeType() == TEXT) && (this.getNodeType() == TEXT)) { mergeTextNode(this, node); } else { _nextSiblingNode = node; } } else { _nextSiblingNode.appendSibling(node); } } /** * Returns the first child node in the tree. * * @return the first child node in the tree. */ protected AnyNode getFirstChildNode() { return _firstChildNode; } /** * Adds the text value of a TEXT node to another TEXT node. * * @param node1 the AnyNode that receives the text value * @param node2 the AnyNode that needs to be merges with node1. */ private void mergeTextNode(AnyNode node1, AnyNode node2) { if (node1.getNodeType() != node2.getNodeType()) { return; } if (node1.getNodeType() != AnyNode.TEXT) { return; } node1._value = node1.getStringValue() + node2.getStringValue(); node2 = null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy