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

com.sun.xml.tree.ElementNode Maven / Gradle / Ivy

The newest version!
/*
 * $Id: ElementNode.java,v 1.11 1999/04/14 20:46:23 mode Exp $
 * 
 * Copyright (c) 1998-1999 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of Sun
 * Microsystems, Inc. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Sun.
 * 
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
 * THIS SOFTWARE OR ITS DERIVATIVES.
 */

package com.sun.xml.tree;

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.Writer;

import java.util.Enumeration;

import org.w3c.dom.*;


/**
 * This class represents XML elements in a parse tree, and is often
 * subclassed to add custom behaviors.  When an XML Document object
 * is built using an XmlDocumentBuilder instance, simple
 * declarative configuration information may be used to control whether
 * this class, or some specialized subclass (e.g. supporting HTML DOM
 * methods) is used for elements in the resulting tree.
 *
 * 

As well as defining new methods to provide behaviors which are * specific to application frameworks, such as Servlets or Swing, such * subclasses may also override methods such as doneParse * and appendChild to perform some kinds of processing during * tree construction. Such processing can include transforming tree * structure to better suit the needs of a given application. When * such transformation is done, the XmlWritable methods * may need to be overridden to make elements transform themselves back * to XML without losing information. (One common transformation is * eliminating redundant representations of data; attributes of an XML * element may correspond to defaultable object properties, and so on.) * *

Element nodes also support a single userObject property, * which may be used to bind objects to elements where subclassing is * either not possible or is inappropriate. For example, user interface * objects often derive from java.awt.Component, so that * they can't extend a different class (ElementNode). * * @see XmlDocumentBuilder * * @author David Brownell * @version $Revision: 1.11 $ */ public class ElementNode extends ParentNode implements ElementEx { private String tag; private AttributeSet attributes; private String idAttributeName; private Object userObject; private static final char tagStart [] = { '<', '/' }; private static final char tagEnd [] = { ' ', '/', '>' }; /** * Partially constructs an element; its tag will be assigned * by the factory (or subclass), while attributes and the * parent (and implicitly, siblings) will assigned when it is * joined to a DOM document. */ public ElementNode () { } public void trimToSize () { super.trimToSize (); if (attributes != null) attributes.trimToSize (); } /** * Assigns the element's tag, when the element has been * constructed using the default constructor. For use by * element factories potentially by custom subclasses. */ protected void setTag (String t) { tag = t; } // Assigns the element's attributes. void setAttributes (AttributeSet a) { AttributeSet oldAtts = attributes; // Check if the current AttributeSet or any attribute is readonly // isReadonly checks if any of the attributes in the AttributeSet // is readonly.. if (oldAtts != null && oldAtts.isReadonly ()) throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR); if (a != null) a.setNameScope (this); attributes = a; if (oldAtts != null) oldAtts.setNameScope (null); } // package private -- overrides base class method void checkChildType (int type) throws DOMException { switch (type) { case ELEMENT_NODE: case TEXT_NODE: case COMMENT_NODE: case PROCESSING_INSTRUCTION_NODE: case CDATA_SECTION_NODE: case ENTITY_REFERENCE_NODE: return; default: throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR); } } // package private -- overrides base class method public void setReadonly (boolean deep) { if (attributes != null) attributes.setReadonly (); super.setReadonly (deep); } public String getNamespace () { String prefix; String value; if ((prefix = getPrefix ()) == null) return getInheritedAttribute ("xmlns"); // // XXX return real URLs for these, both here and // in AttributeNode ... // if ("xml".equals (prefix) || "xmlns".equals (prefix)) return null; value = getInheritedAttribute ("xmlns:" + prefix); if (value == null) throw new IllegalStateException (getMessage ("EN-000", new Object [] { prefix })); return value; } public String getLocalName () { int index = tag.indexOf (':'); if (index < 0) return tag; return tag.substring (index + 1); } public String getPrefix () { int index = tag.indexOf (':'); return index < 0 ? null : tag.substring (0, index); } public void setPrefix (String prefix) { int index = tag.indexOf (':'); if (prefix == null) { if (index < 0) return; else tag = tag.substring (index + 1); return; } StringBuffer tmp = new StringBuffer (prefix); tmp.append (':'); if (index < 0 ) tmp.append (tag); else tmp.append (tag.substring (index + 1)); tag = tmp.toString (); } /** DOM: Returns the attributes of this element. */ public NamedNodeMap getAttributes () { if (attributes == null) attributes = new AttributeSet (this); return attributes; } /** * Returns the element and its content as a string, which includes * all the markup embedded in this element. If the element is not * fully constructed, the content will not be an XML tag. */ public String toString () { try { CharArrayWriter out = new CharArrayWriter (); XmlWriteContext x = new XmlWriteContext (out); writeXml (x); return out.toString (); } catch (Exception e) { return super.toString (); } } /** * Writes this element and all of its children out, as well * formed XML. */ public void writeXml (XmlWriteContext context) throws IOException { Writer out = context.getWriter (); if (tag == null) throw new IllegalStateException ( getMessage ("EN-002")); out.write (tagStart, 0, 1); // "<" out.write (tag); if (attributes != null) attributes.writeXml (context); // // Write empty nodes as "" to make sure version 3 // and 4 web browsers can read empty tag output as HTML. // XML allows "" too, of course. // if (!hasChildNodes ()) out.write (tagEnd, 0, 3); // " />" else { out.write (tagEnd, 2, 1); // ">" writeChildrenXml (context); out.write (tagStart, 0, 2); // "" } } /** * Assigns the name of the element's ID attribute; only one attribute * may have the ID type. XML supports a kind of validatable internal * linking using ID attributes, with IDREF attributes identifying * specific nodes (and IDREFS attributes identifying sets of them). */ public void setIdAttributeName (String attName) { if (readonly) throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR); idAttributeName = attName; } /** * Returns the name of the element's ID attribute, if one is known. */ public String getIdAttributeName () { return idAttributeName; } public void setUserObject (Object userObject) { this.userObject = userObject; } public Object getUserObject () { return userObject; } // DOM support /** DOM: Returns the ELEMENT_NODE node type. */ public short getNodeType () { return ELEMENT_NODE; } /** DOM: Returns the name of the XML tag for this element. */ public String getTagName () { return tag; } /** DOM: Returns the name of the XML tag for this element. */ public String getNodeName () { return tag; } /** DOM: Returns the value of the named attribute, or null */ public String getAttribute (String name) { return (attributes == null) ? "" : attributes.getValue (name); } public String getAttribute (String uri, String name) { Attr attr; if (attributes == null) return ""; attr = getAttributeNode (uri, name); if (attr == null) return ""; return attr.getValue (); } public Attr getAttributeNode (String uri, String name) { if (name == null) return null; if (attributes != null) { for (int i = 0; ; i++) { AttributeNode attr; String ns; attr = (AttributeNode) attributes.item (i); if (attr == null) return null; if (!name.equals (attr.getName ())) continue; ns = attr.getNamespace (); if (ns != null && ns.equals (uri)) return attr; } } return null; } /** * DOM: Assigns or modifies the value of the specified attribute. */ public void setAttribute (String name, String value) throws DOMException { AttributeNode att; if (readonly) throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR); if (attributes == null) attributes = new AttributeSet (this); if ((att = (AttributeNode) attributes.getNamedItem (name)) != null) att.setNodeValue (value); else { att = new AttributeNode (name, value, true, null); att.setOwnerDocument ((XmlDocument) getOwnerDocument ()); attributes.setNamedItem (att); } } /** DOM: Remove the named attribute. */ public void removeAttribute (String name) throws DOMException { if (readonly) throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR); if (attributes == null) throw new DomEx (DomEx.NOT_FOUND_ERR); attributes.removeNamedItem (name); } /** DOM: returns the attribute */ public Attr getAttributeNode (String name) { if (attributes != null) return (Attr) attributes.getNamedItem (name); else return null; } /** DOM: assigns the attribute */ public Attr setAttributeNode (Attr newAttr) throws DOMException { if (readonly) throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR); if (!(newAttr instanceof AttributeNode)) throw new DomEx (DomEx.WRONG_DOCUMENT_ERR); if (attributes == null) attributes = new AttributeSet (this); return (Attr)attributes.setNamedItem (newAttr); } /** DOM: removes the attribute with the same name as this one */ public Attr removeAttributeNode (Attr oldAttr) throws DOMException { if (isReadonly ()) throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR); Attr attr = getAttributeNode (oldAttr.getNodeName ()); if (attr == null) throw new DomEx (DomEx.NOT_FOUND_ERR); removeAttribute (attr.getNodeName ()); return attr; } /** * DOM: Merges all adjacent Text nodes in the tree rooted by this * element. Avoid using this on large blocks of text not separated * by markup such as elements or processing instructions, since it * can require arbitrarily large blocks of contiguous memory. * *

As a compatible extension to DOM, this normalizes treatment * of whitespace except when the xml:space='preserve' * attribute value applies to a node. All whitespace is normalized * to one space. This ensures that text which is pretty-printed and * then reread (and normalized) retains the same content.

*/ public void normalize () { Node node; boolean preserve = false; boolean knowPreserve = false; if (readonly) throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR); for (int i = 0; true; i++) { if ((node = item (i)) == null) break; switch (node.getNodeType ()) { case ELEMENT_NODE: ((Element)node).normalize (); continue; // case CDATA_SECTION_NODE: case TEXT_NODE: { Node node2 = item (i + 1); if (node2 == null || node2.getNodeType () != TEXT_NODE) { // See if xml:space='preserve' is set... if (!knowPreserve) { preserve = "preserve".equals ( getInheritedAttribute ("xml:space")); knowPreserve = true; } // ... and if not, normalize whitespace if (!preserve) { char buf [] = ((TextNode)node).data; // XXX this isn't supposed to happen if (buf == null || buf.length == 0) { removeChild (node); i--; continue; } int current = removeWhiteSpaces (buf); // compact if it shrank if (current != buf.length) { char tmp [] = new char [current]; System.arraycopy (buf, 0, tmp, 0, current); ((TextNode)node).data = tmp; } } continue; } ((TextNode) node).joinNextText (); i--; continue; } default: continue; } } } /* * removes white leading, trailing and extra white spaces from the buffer. * returns the size of the new buf after the white spaces are removed. */ public int removeWhiteSpaces (char [] buf) { int current = 0, j = 0; // copy to beginning, normalizing whitespace // (including leading, trailing) to one space while (j < buf.length) { boolean sawSpace = false; char c = buf [j++]; if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { c = ' '; sawSpace = true; } buf [current++] = c; if (sawSpace) { while (j < buf.length) { c = buf [j]; if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { j++; continue; } else break; } } } return current; } /** * Creates a new unparented node whose attributes are the same as * this node's attributes; if deep is true, the children * of this node are cloned as children of the new node. */ public Node cloneNode (boolean deep) { try { ElementNode retval; retval = (ElementNode) getOwnerDocument().createElement (tag); if (attributes != null) retval.setAttributes (new AttributeSet (attributes, true)); if (deep) { for (int i = 0; true; i++) { Node node = item (i); if (node == null) break; retval.appendChild (node.cloneNode (true)); } } return retval; } catch (DOMException e) { throw new RuntimeException (getMessage ("EN-001")); } } /** * Convenience method to construct a non-prettyprinting XML write * context and call writeXml with it. Subclasses may choose to * to override this method to generate non-XML text, * * @param out where to emit the XML content of this node */ public void write (Writer out) throws IOException { writeXml (new XmlWriteContext (out)); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy