| /* | ||
| * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved. | ||
| * | ||
| * This software is open source. | ||
| * See the bottom of this file for the licence. | ||
| * | ||
| * $Id: AbstractBranch.java,v 1.41 2004/06/25 08:03:40 maartenc Exp $ | ||
| */ | ||
| package org.dom4j.tree; | ||
| import java.util.ArrayList; | ||
| import java.util.Iterator; | ||
| import java.util.List; | ||
| import java.util.StringTokenizer; | ||
| import org.dom4j.Branch; | ||
| import org.dom4j.Comment; | ||
| import org.dom4j.Element; | ||
| import org.dom4j.IllegalAddException; | ||
| import org.dom4j.Namespace; | ||
| import org.dom4j.Node; | ||
| import org.dom4j.ProcessingInstruction; | ||
| import org.dom4j.QName; | ||
| import org.dom4j.io.OutputFormat; | ||
| /** <p><code>AbstractBranch</code> is an abstract base class for | ||
| * tree implementors to use for implementation inheritence.</p> | ||
| * | ||
| * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> | ||
| * @version $Revision: 1.41 $ | ||
| */ | ||
| public abstract class AbstractBranch extends AbstractNode implements Branch { | ||
| /** The output format used by default */ | ||
| protected static final OutputFormat outputFormat = new OutputFormat(); | ||
| protected static final int DEFAULT_CONTENT_LIST_SIZE = 5; | ||
| 140660x | public AbstractBranch() { | |
| 140660x | } | |
| public boolean isReadOnly() { | ||
| 234x | return false; | |
| } | ||
| public boolean hasContent() { | ||
| 0/2 0x | return nodeCount() > 0; | |
| } | ||
| public List content() { | ||
| 102x | List backingList = contentList(); | |
| 102x | return new ContentListFacade(this, backingList); | |
| } | ||
| public String getText() { | ||
| 26x | List content = contentList(); | |
| 1/2 26x | if (content != null) { | |
| 26x | int size = content.size(); | |
| 1/2 26x | if (size >= 1) { | |
| 26x | Object first = content.get(0); | |
| 26x | String firstText = getContentAsText( first ); | |
| 2/2 26x | if (size == 1) { | |
| // optimised to avoid StringBuffer creation | ||
| 4x | return firstText; | |
| } | ||
| else { | ||
| 22x | StringBuffer buffer = new StringBuffer( firstText ); | |
| 2/2 72x | for ( int i = 1; i < size; i++ ) { | |
| 50x | Object node = content.get(i); | |
| 50x | buffer.append( getContentAsText( node ) ); | |
| } | ||
| 22x | return buffer.toString(); | |
| } | ||
| } | ||
| } | ||
| 0x | return ""; | |
| } | ||
| /** @return the text value of the given content object | ||
| * as text which returns the text value of CDATA, Entity or Text nodes | ||
| */ | ||
| protected String getContentAsText(Object content) { | ||
| 1/2 128x | if ( content instanceof Node) { | |
| 128x | Node node = (Node) content; | |
| 128x | switch ( node.getNodeType() ) { | |
| case CDATA_SECTION_NODE: | ||
| //case ENTITY_NODE: | ||
| case ENTITY_REFERENCE_NODE: | ||
| case TEXT_NODE: | ||
| 118x | return node.getText(); | |
| } | ||
| } | ||
| 0/2 0x | else if ( content instanceof String) { | |
| 0x | return (String) content; | |
| } | ||
| 10x | return ""; | |
| } | ||
| /** @return the XPath defined string-value of the given content object | ||
| */ | ||
| protected String getContentAsStringValue(Object content) { | ||
| 1/2 2448x | if ( content instanceof Node) { | |
| 2448x | Node node = (Node) content; | |
| 2448x | switch ( node.getNodeType() ) { | |
| case CDATA_SECTION_NODE: | ||
| //case ENTITY_NODE: | ||
| case ENTITY_REFERENCE_NODE: | ||
| case TEXT_NODE: | ||
| case ELEMENT_NODE: | ||
| 2448x | return node.getStringValue(); | |
| } | ||
| } | ||
| 0/2 0x | else if ( content instanceof String) { | |
| 0x | return (String) content; | |
| } | ||
| 0x | return ""; | |
| } | ||
| public String getTextTrim() { | ||
| 20x | String text = getText(); | |
| 20x | StringBuffer textContent = new StringBuffer(); | |
| 20x | StringTokenizer tokenizer = new StringTokenizer(text); | |
| 2/2 50x | while (tokenizer.hasMoreTokens()) { | |
| 30x | String str = tokenizer.nextToken(); | |
| 30x | textContent.append(str); | |
| 2/2 30x | if (tokenizer.hasMoreTokens()) { | |
| 10x | textContent.append(" "); // separator | |
| } | ||
| } | ||
| 20x | return textContent.toString(); | |
| } | ||
| public void setProcessingInstructions(List listOfPIs) { | ||
| 0/2 0x | for ( Iterator iter = listOfPIs.iterator(); iter.hasNext(); ) { | |
| 0x | ProcessingInstruction pi = (ProcessingInstruction) iter.next(); | |
| 0x | addNode(pi); | |
| } | ||
| 0x | } | |
| public Element addElement(String name) { | ||
| 0x | Element node = getDocumentFactory().createElement( name ); | |
| 0x | add( node ); | |
| 0x | return node; | |
| } | ||
| public Element addElement(String qualifiedName, String namespaceURI) { | ||
| 4x | Element node = getDocumentFactory().createElement( qualifiedName, namespaceURI ); | |
| 4x | add( node ); | |
| 4x | return node; | |
| } | ||
| public Element addElement(QName qname) { | ||
| 109326x | Element node = getDocumentFactory().createElement( qname ); | |
| 109326x | add( node ); | |
| 109326x | return node; | |
| } | ||
| public Element addElement(String name, String prefix, String uri) { | ||
| 0x | Namespace namespace = Namespace.get( prefix, uri ); | |
| 0x | QName qName = getDocumentFactory().createQName( name, namespace ); | |
| 0x | return addElement( qName ); | |
| } | ||
| // polymorphic node methods | ||
| public void add(Node node) { | ||
| 16x | switch ( node.getNodeType() ) { | |
| case ELEMENT_NODE: | ||
| 16x | add((Element) node); | |
| 16x | break; | |
| case COMMENT_NODE: | ||
| 0x | add((Comment) node); | |
| 0x | break; | |
| case PROCESSING_INSTRUCTION_NODE: | ||
| 0x | add((ProcessingInstruction) node); | |
| 0x | break; | |
| default: | ||
| 0x | invalidNodeTypeAddException(node); | |
| } | ||
| 16x | } | |
| public boolean remove(Node node) { | ||
| 4x | switch ( node.getNodeType() ) { | |
| case ELEMENT_NODE: | ||
| 4x | return remove((Element) node); | |
| case COMMENT_NODE: | ||
| 0x | return remove((Comment) node); | |
| case PROCESSING_INSTRUCTION_NODE: | ||
| 0x | return remove((ProcessingInstruction) node); | |
| default: | ||
| 0x | invalidNodeTypeAddException(node); | |
| 0x | return false; | |
| } | ||
| } | ||
| // typesafe versions using node classes | ||
| public void add(Comment comment) { | ||
| 8x | addNode(comment); | |
| 8x | } | |
| public void add(Element element) { | ||
| 12048x | addNode(element); | |
| 12048x | } | |
| public void add(ProcessingInstruction pi) { | ||
| 28x | addNode(pi); | |
| 28x | } | |
| public boolean remove(Comment comment) { | ||
| 0x | return removeNode(comment); | |
| } | ||
| public boolean remove(Element element) { | ||
| 4x | return removeNode(element); | |
| } | ||
| public boolean remove(ProcessingInstruction pi) { | ||
| 0x | return removeNode(pi); | |
| } | ||
| public Element elementByID(String elementID) { | ||
| 2/2 82x | for ( int i = 0, size = nodeCount(); i < size; i++ ) { | |
| 62x | Node node = node(i); | |
| 2/2 62x | if ( node instanceof Element ) { | |
| 24x | Element element = (Element) node; | |
| 24x | String id = elementID(element); | |
| 4/4 24x | if ( id != null && id.equals( elementID ) ) { | |
| 2x | return element; | |
| } | ||
| else { | ||
| 22x | element = element.elementByID( elementID ); | |
| 2/2 22x | if ( element != null ) { | |
| 4x | return element; | |
| } | ||
| } | ||
| } | ||
| } | ||
| 20x | return null; | |
| } | ||
| public void appendContent(Branch branch) { | ||
| 2/2 790x | for ( int i = 0, size = branch.nodeCount(); i < size; i++ ) { | |
| 546x | Node node = branch.node(i); | |
| 546x | add( (Node) node.clone() ); | |
| } | ||
| 244x | } | |
| public Node node(int index) { | ||
| 160x | Object object = contentList().get(index); | |
| 1/2 160x | if (object instanceof Node) { | |
| 160x | return (Node) object; | |
| } | ||
| 0/2 0x | if (object instanceof String) { | |
| 0x | return getDocumentFactory().createText(object.toString()); | |
| } | ||
| 0x | return null; | |
| } | ||
| public int nodeCount() { | ||
| 160x | return contentList().size(); | |
| } | ||
| public int indexOf(Node node) { | ||
| 0x | return contentList().indexOf( node ); | |
| } | ||
| public Iterator nodeIterator() { | ||
| 218x | return contentList().iterator(); | |
| } | ||
| // Implementation methods | ||
| /** @return the ID of the given <code>Element</code> | ||
| */ | ||
| protected String elementID(Element element) { | ||
| // XXX: there will be other ways of finding the ID | ||
| // XXX: should probably have an IDResolver or something | ||
| 24x | return element.attributeValue( "ID" ); | |
| } | ||
| /** @return the internal List used to manage the content */ | ||
| protected abstract List contentList(); | ||
| /** A Factory Method pattern which creates | ||
| * a List implementation used to store content | ||
| */ | ||
| protected List createContentList() { | ||
| 50674x | return new ArrayList( DEFAULT_CONTENT_LIST_SIZE ); | |
| } | ||
| /** A Factory Method pattern which creates | ||
| * a List implementation used to store content | ||
| */ | ||
| protected List createContentList(int size) { | ||
| 4x | return new ArrayList( size ); | |
| } | ||
| /** A Factory Method pattern which creates | ||
| * a BackedList implementation used to store results of | ||
| * a filtered content query. */ | ||
| protected BackedList createResultList() { | ||
| 22028x | return new BackedList( this, contentList() ); | |
| } | ||
| /** A Factory Method pattern which creates | ||
| * a BackedList implementation which contains a single result | ||
| */ | ||
| protected List createSingleResultList( Object result ) { | ||
| 0x | BackedList list = new BackedList( this, contentList(), 1 ); | |
| 0x | list.addLocal( result ); | |
| 0x | return list; | |
| } | ||
| /** A Factory Method pattern which creates an empty | ||
| * a BackedList implementation | ||
| */ | ||
| protected List createEmptyList() { | ||
| 12x | return new BackedList( this, contentList(), 0 ); | |
| } | ||
| protected abstract void addNode(Node node); | ||
| protected abstract void addNode(int index, Node node); | ||
| protected abstract boolean removeNode(Node node); | ||
| /** Called when a new child node has been added to me | ||
| * to allow any parent relationships to be created or | ||
| * events to be fired. | ||
| */ | ||
| protected abstract void childAdded(Node node); | ||
| /** Called when a child node has been removed | ||
| * to allow any parent relationships to be deleted or | ||
| * events to be fired. | ||
| */ | ||
| protected abstract void childRemoved(Node node); | ||
| /** Called when the given List content has been removed so | ||
| * each node should have its parent and document relationships | ||
| * cleared | ||
| */ | ||
| protected void contentRemoved() { | ||
| 14x | List content = contentList(); | |
| 2/2 20x | for ( int i = 0, size = content.size(); i < size; i++ ) { | |
| 6x | Object object = content.get(i); | |
| 1/2 6x | if ( object instanceof Node ) { | |
| 6x | childRemoved( (Node) object ); | |
| } | ||
| } | ||
| 14x | } | |
| /** Called when an invalid node has been added. | ||
| * Throws an {@link IllegalAddException}. | ||
| */ | ||
| protected void invalidNodeTypeAddException(Node node) { | ||
| 0x | throw new IllegalAddException( "Invalid node type. Cannot add node: " + node + " to this branch: " + this ); | |
| } | ||
| } | ||
| /* | ||
| * 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 "DOM4J" must not be used to endorse or promote | ||
| * products derived from this Software without prior written | ||
| * permission of MetaStuff, Ltd. For written permission, | ||
| * please contact dom4j-info@metastuff.com. | ||
| * | ||
| * 4. Products derived from this Software may not be called "DOM4J" | ||
| * nor may "DOM4J" appear in their names without prior written | ||
| * permission of MetaStuff, Ltd. DOM4J is a registered | ||
| * trademark of MetaStuff, Ltd. | ||
| * | ||
| * 5. Due credit should be given to the DOM4J Project - | ||
| * http://www.dom4j.org | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. 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 | ||
| * METASTUFF, LTD. 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-2004 (C) MetaStuff, Ltd. All Rights Reserved. | ||
| * | ||
| * $Id: AbstractBranch.java,v 1.41 2004/06/25 08:03:40 maartenc Exp $ | ||
| */ |