001/* 002 * Copyright 2001 (C) MetaStuff, Ltd. All Rights Reserved. 003 * 004 * This software is open source. 005 * See the bottom of this file for the licence. 006 * 007 * $Id: AbstractBranch.java,v 1.36 2001/07/25 10:51:11 jstrachan Exp $ 008 */ 009 010package org.dom4j.tree; 011 012import java.io.IOException; 013import java.io.StringWriter; 014import java.io.PrintWriter; 015import java.util.ArrayList; 016import java.util.HashMap; 017import java.util.Iterator; 018import java.util.LinkedList; 019import java.util.List; 020import java.util.Map; 021import java.util.StringTokenizer; 022 023import org.dom4j.Branch; 024import org.dom4j.CDATA; 025import org.dom4j.CharacterData; 026import org.dom4j.Comment; 027import org.dom4j.Element; 028import org.dom4j.Entity; 029import org.dom4j.IllegalAddException; 030import org.dom4j.Node; 031import org.dom4j.Namespace; 032import org.dom4j.QName; 033import org.dom4j.ProcessingInstruction; 034import org.dom4j.Text; 035import org.dom4j.io.OutputFormat; 036 037import org.xml.sax.Attributes; 038 039/** <p><code>AbstractBranch</code> is an abstract base class for 040 * tree implementors to use for implementation inheritence.</p> 041 * 042 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> 043 * @version $Revision: 1.36 $ 044 */ 045public abstract class AbstractBranch extends AbstractNode implements Branch { 046 047 /** The output format used by default */ 048 protected static final OutputFormat outputFormat = new OutputFormat(); 049 050 protected static final int DEFAULT_CONTENT_LIST_SIZE = 5; 051 052 053 public AbstractBranch() { 054 } 055 056 057 public boolean isReadOnly() { 058 return false; 059 } 060 061 public boolean hasContent() { 062 return nodeCount() > 0; 063 } 064 065 public List content() { 066 List backingList = contentList(); 067 return new ContentListFacade(this, backingList); 068 } 069 070 public String getText() { 071 List content = contentList(); 072 if (content != null) { 073 int size = content.size(); 074 if (size >= 1) { 075 Object first = content.get(0); 076 String firstText = getContentAsText( first ); 077 if (size == 1) { 078 // optimised to avoid StringBuffer creation 079 return firstText; 080 } 081 else { 082 StringBuffer buffer = new StringBuffer( firstText ); 083 for ( int i = 1; i < size; i++ ) { 084 Object node = content.get(i); 085 buffer.append( getContentAsText( node ) ); 086 } 087 return buffer.toString(); 088 } 089 } 090 } 091 return ""; 092 } 093 094 /** @return the text value of the given content object 095 * as text which returns the text value of CDATA, Entity or Text nodes 096 */ 097 protected String getContentAsText(Object content) { 098 if ( content instanceof Node) { 099 Node node = (Node) content; 100 switch ( node.getNodeType() ) { 101 case CDATA_SECTION_NODE: 102 //case ENTITY_NODE: 103 case ENTITY_REFERENCE_NODE: 104 case TEXT_NODE: 105 return node.getText(); 106 } 107 } 108 else if ( content instanceof String) { 109 return (String) content; 110 } 111 return ""; 112 } 113 114 /** @return the XPath defined string-value of the given content object 115 */ 116 protected String getContentAsStringValue(Object content) { 117 if ( content instanceof Node) { 118 Node node = (Node) content; 119 switch ( node.getNodeType() ) { 120 case CDATA_SECTION_NODE: 121 //case ENTITY_NODE: 122 case ENTITY_REFERENCE_NODE: 123 case TEXT_NODE: 124 case ELEMENT_NODE: 125 return node.getStringValue(); 126 } 127 } 128 else if ( content instanceof String) { 129 return (String) content; 130 } 131 return ""; 132 } 133 134 135 public String getTextTrim() { 136 String text = getText(); 137 138 StringBuffer textContent = new StringBuffer(); 139 StringTokenizer tokenizer = new StringTokenizer(text); 140 while (tokenizer.hasMoreTokens()) { 141 String str = tokenizer.nextToken(); 142 textContent.append(str); 143 if (tokenizer.hasMoreTokens()) { 144 textContent.append(" "); // separator 145 } 146 } 147 148 return textContent.toString(); 149 } 150 151 public void setProcessingInstructions(List listOfPIs) { 152 for ( Iterator iter = listOfPIs.iterator(); iter.hasNext(); ) { 153 ProcessingInstruction pi = (ProcessingInstruction) iter.next(); 154 addNode(pi); 155 } 156 } 157 158 public Element addElement(String name) { 159 Element node = getDocumentFactory().createElement( name ); 160 add( node ); 161 return node; 162 } 163 164 public Element addElement(String qualifiedName, String namespaceURI) { 165 Element node = getDocumentFactory().createElement( qualifiedName, namespaceURI ); 166 add( node ); 167 return node; 168 } 169 170 public Element addElement(QName qname) { 171 Element node = getDocumentFactory().createElement( qname ); 172 add( node ); 173 return node; 174 } 175 176 public Element addElement(String name, String prefix, String uri) { 177 Namespace namespace = Namespace.get( prefix, uri ); 178 QName qName = getDocumentFactory().createQName( name, namespace ); 179 return addElement( qName ); 180 } 181 182 // polymorphic node methods 183 184 public void add(Node node) { 185 switch ( node.getNodeType() ) { 186 case ELEMENT_NODE: 187 add((Element) node); 188 break; 189 case COMMENT_NODE: 190 add((Comment) node); 191 break; 192 case PROCESSING_INSTRUCTION_NODE: 193 add((ProcessingInstruction) node); 194 break; 195 default: 196 invalidNodeTypeAddException(node); 197 } 198 } 199 200 public boolean remove(Node node) { 201 switch ( node.getNodeType() ) { 202 case ELEMENT_NODE: 203 return remove((Element) node); 204 case COMMENT_NODE: 205 return remove((Comment) node); 206 case PROCESSING_INSTRUCTION_NODE: 207 return remove((ProcessingInstruction) node); 208 default: 209 invalidNodeTypeAddException(node); 210 return false; 211 } 212 } 213 214 // typesafe versions using node classes 215 216 public void add(Comment comment) { 217 addNode(comment); 218 } 219 220 public void add(Element element) { 221 addNode(element); 222 } 223 224 public void add(ProcessingInstruction pi) { 225 addNode(pi); 226 } 227 228 public boolean remove(Comment comment) { 229 return removeNode(comment); 230 } 231 232 public boolean remove(Element element) { 233 return removeNode(element); 234 } 235 236 public boolean remove(ProcessingInstruction pi) { 237 return removeNode(pi); 238 } 239 240 241 public Element elementByID(String elementID) { 242 for ( int i = 0, size = nodeCount(); i < size; i++ ) { 243 Node node = node(i); 244 if ( node instanceof Element ) { 245 Element element = (Element) node; 246 String id = elementID(element); 247 if ( id != null && id.equals( elementID ) ) { 248 return element; 249 } 250 else { 251 element = element.elementByID( elementID ); 252 if ( element != null ) { 253 return element; 254 } 255 } 256 } 257 } 258 return null; 259 } 260 261 public void appendContent(Branch branch) { 262 for ( int i = 0, size = branch.nodeCount(); i < size; i++ ) { 263 Node node = branch.node(i); 264 add( (Node) node.clone() ); 265 } 266 } 267 268 269 public Node node(int index) { 270 Object object = contentList().get(index); 271 if (object instanceof Node) { 272 return (Node) object; 273 } 274 if (object instanceof String) { 275 return getDocumentFactory().createText(object.toString()); 276 } 277 return null; 278 } 279 280 public int nodeCount() { 281 return contentList().size(); 282 } 283 284 public int indexOf(Node node) { 285 return contentList().indexOf( node ); 286 } 287 288 public Iterator nodeIterator() { 289 return contentList().iterator(); 290 } 291 292 293 // Implementation methods 294 295 /** @return the ID of the given <code>Element</code> 296 */ 297 protected String elementID(Element element) { 298 // XXX: there will be other ways of finding the ID 299 // XXX: should probably have an IDResolver or something 300 return element.attributeValue( "ID" ); 301 } 302 303 /** @return the internal List used to manage the content */ 304 protected abstract List contentList(); 305 306 /** A Factory Method pattern which creates 307 * a List implementation used to store content 308 */ 309 protected List createContentList() { 310 return new ArrayList( DEFAULT_CONTENT_LIST_SIZE ); 311 } 312 313 /** A Factory Method pattern which creates 314 * a List implementation used to store content 315 */ 316 protected List createContentList(int size) { 317 return new ArrayList( size ); 318 } 319 320 321 /** A Factory Method pattern which creates 322 * a BackedList implementation used to store results of 323 * a filtered content query. */ 324 protected BackedList createResultList() { 325 return new BackedList( this, contentList() ); 326 } 327 328 /** A Factory Method pattern which creates 329 * a BackedList implementation which contains a single result 330 */ 331 protected List createSingleResultList( Object result ) { 332 BackedList list = new BackedList( this, contentList(), 1 ); 333 list.addLocal( result ); 334 return list; 335 } 336 337 /** A Factory Method pattern which creates an empty 338 * a BackedList implementation 339 */ 340 protected List createEmptyList() { 341 return new BackedList( this, contentList(), 0 ); 342 } 343 344 345 protected abstract void addNode(Node node); 346 347 protected abstract boolean removeNode(Node node); 348 349 350 /** Called when a new child node has been added to me 351 * to allow any parent relationships to be created or 352 * events to be fired. 353 */ 354 protected abstract void childAdded(Node node); 355 356 /** Called when a child node has been removed 357 * to allow any parent relationships to be deleted or 358 * events to be fired. 359 */ 360 protected abstract void childRemoved(Node node); 361 362 /** Called when the given List content has been removed so 363 * each node should have its parent and document relationships 364 * cleared 365 */ 366 protected void contentRemoved() { 367 List content = contentList(); 368 for ( int i = 0, size = content.size(); i < size; i++ ) { 369 Object object = content.get(i); 370 if ( object instanceof Node ) { 371 childRemoved( (Node) object ); 372 } 373 } 374 } 375 376 /** Called when an invalid node has been added. 377 * Throws an {@link IllegalAddException}. 378 */ 379 protected void invalidNodeTypeAddException(Node node) { 380 throw new IllegalAddException( "Invalid node type. Cannot add node: " + node + " to this branch: " + this ); 381 } 382 383 384} 385 386 387 388 389/* 390 * Redistribution and use of this software and associated documentation 391 * ("Software"), with or without modification, are permitted provided 392 * that the following conditions are met: 393 * 394 * 1. Redistributions of source code must retain copyright 395 * statements and notices. Redistributions must also contain a 396 * copy of this document. 397 * 398 * 2. Redistributions in binary form must reproduce the 399 * above copyright notice, this list of conditions and the 400 * following disclaimer in the documentation and/or other 401 * materials provided with the distribution. 402 * 403 * 3. The name "DOM4J" must not be used to endorse or promote 404 * products derived from this Software without prior written 405 * permission of MetaStuff, Ltd. For written permission, 406 * please contact dom4j-info@metastuff.com. 407 * 408 * 4. Products derived from this Software may not be called "DOM4J" 409 * nor may "DOM4J" appear in their names without prior written 410 * permission of MetaStuff, Ltd. DOM4J is a registered 411 * trademark of MetaStuff, Ltd. 412 * 413 * 5. Due credit should be given to the DOM4J Project 414 * (http://dom4j.org/). 415 * 416 * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS 417 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 418 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 419 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 420 * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 421 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 422 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 423 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 424 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 425 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 426 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 427 * OF THE POSSIBILITY OF SUCH DAMAGE. 428 * 429 * Copyright 2001 (C) MetaStuff, Ltd. All Rights Reserved. 430 * 431 * $Id: AbstractBranch.java,v 1.36 2001/07/25 10:51:11 jstrachan Exp $ 432 */