View Javadoc

    1   /*
    2    * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
    3    *
    4    * This software is open source.
    5    * See the bottom of this file for the licence.
    6    */
    7
    8   package org.dom4j.tree;
    9
    10  import java.util.ArrayList;
    11  import java.util.Iterator;
    12  import java.util.List;
    13  import java.util.StringTokenizer;
    14
    15  import org.dom4j.Branch;
    16  import org.dom4j.Comment;
    17  import org.dom4j.Element;
    18  import org.dom4j.IllegalAddException;
    19  import org.dom4j.Namespace;
    20  import org.dom4j.Node;
    21  import org.dom4j.ProcessingInstruction;
    22  import org.dom4j.QName;
    23
    24  /***
    25   * <p>
    26   * <code>AbstractBranch</code> is an abstract base class for tree implementors
    27   * to use for implementation inheritence.
    28   * </p>
    29   *
    30   * @author <a href="mailto:jstrachan@apache.org">James Strachan </a>
    31   * @version $Revision: 1.44 $
    32   */
    33  public abstract class AbstractBranch extends AbstractNode implements Branch {
    34      protected static final int DEFAULT_CONTENT_LIST_SIZE = 5;
    35
    36      public AbstractBranch() {
    37      }
    38
    39      public boolean isReadOnly() {
    40          return false;
    41      }
    42
    43      public boolean hasContent() {
    44          return nodeCount() > 0;
    45      }
    46
    47      public List content() {
    48          List backingList = contentList();
    49
    50          return new ContentListFacade(this, backingList);
    51      }
    52
    53      public String getText() {
    54          List content = contentList();
    55
    56          if (content != null) {
    57              int size = content.size();
    58
    59              if (size >= 1) {
    60                  Object first = content.get(0);
    61                  String firstText = getContentAsText(first);
    62
    63                  if (size == 1) {
    64                      // optimised to avoid StringBuffer creation
    65                      return firstText;
    66                  } else {
    67                      StringBuffer buffer = new StringBuffer(firstText);
    68
    69                      for (int i = 1; i < size; i++) {
    70                          Object node = content.get(i);
    71                          buffer.append(getContentAsText(node));
    72                      }
    73
    74                      return buffer.toString();
    75                  }
    76              }
    77          }
    78
    79          return "";
    80      }
    81
    82      /***
    83       * DOCUMENT ME!
    84       *
    85       * @param content
    86       *            DOCUMENT ME!
    87       *
    88       * @return the text value of the given content object as text which returns
    89       *         the text value of CDATA, Entity or Text nodes
    90       */
    91      protected String getContentAsText(Object content) {
    92          if (content instanceof Node) {
    93              Node node = (Node) content;
    94
    95              switch (node.getNodeType()) {
    96                  case CDATA_SECTION_NODE:
    97
    98                  // case ENTITY_NODE:
    99                  case ENTITY_REFERENCE_NODE:
    100                 case TEXT_NODE:
    101                     return node.getText();
    102
    103                 default:
    104                     break;
    105             }
    106         } else if (content instanceof String) {
    107             return (String) content;
    108         }
    109
    110         return "";
    111     }
    112
    113     /***
    114      * DOCUMENT ME!
    115      *
    116      * @param content
    117      *            DOCUMENT ME!
    118      *
    119      * @return the XPath defined string-value of the given content object
    120      */
    121     protected String getContentAsStringValue(Object content) {
    122         if (content instanceof Node) {
    123             Node node = (Node) content;
    124
    125             switch (node.getNodeType()) {
    126                 case CDATA_SECTION_NODE:
    127
    128                 // case ENTITY_NODE:
    129                 case ENTITY_REFERENCE_NODE:
    130                 case TEXT_NODE:
    131                 case ELEMENT_NODE:
    132                     return node.getStringValue();
    133
    134                 default:
    135                     break;
    136             }
    137         } else if (content instanceof String) {
    138             return (String) content;
    139         }
    140
    141         return "";
    142     }
    143
    144     public String getTextTrim() {
    145         String text = getText();
    146
    147         StringBuffer textContent = new StringBuffer();
    148         StringTokenizer tokenizer = new StringTokenizer(text);
    149
    150         while (tokenizer.hasMoreTokens()) {
    151             String str = tokenizer.nextToken();
    152             textContent.append(str);
    153
    154             if (tokenizer.hasMoreTokens()) {
    155                 textContent.append(" "); // separator
    156             }
    157         }
    158
    159         return textContent.toString();
    160     }
    161
    162     public void setProcessingInstructions(List listOfPIs) {
    163         for (Iterator iter = listOfPIs.iterator(); iter.hasNext();) {
    164             ProcessingInstruction pi = (ProcessingInstruction) iter.next();
    165             addNode(pi);
    166         }
    167     }
    168
    169     public Element addElement(String name) {
    170         Element node = getDocumentFactory().createElement(name);
    171         add(node);
    172
    173         return node;
    174     }
    175
    176     public Element addElement(String qualifiedName, String namespaceURI) {
    177         Element node = getDocumentFactory().createElement(qualifiedName,
    178                 namespaceURI);
    179         add(node);
    180
    181         return node;
    182     }
    183
    184     public Element addElement(QName qname) {
    185         Element node = getDocumentFactory().createElement(qname);
    186         add(node);
    187
    188         return node;
    189     }
    190
    191     public Element addElement(String name, String prefix, String uri) {
    192         Namespace namespace = Namespace.get(prefix, uri);
    193         QName qName = getDocumentFactory().createQName(name, namespace);
    194
    195         return addElement(qName);
    196     }
    197
    198     // polymorphic node methods
    199     public void add(Node node) {
    200         switch (node.getNodeType()) {
    201             case ELEMENT_NODE:
    202                 add((Element) node);
    203
    204                 break;
    205
    206             case COMMENT_NODE:
    207                 add((Comment) node);
    208
    209                 break;
    210
    211             case PROCESSING_INSTRUCTION_NODE:
    212                 add((ProcessingInstruction) node);
    213
    214                 break;
    215
    216             default:
    217                 invalidNodeTypeAddException(node);
    218         }
    219     }
    220
    221     public boolean remove(Node node) {
    222         switch (node.getNodeType()) {
    223             case ELEMENT_NODE:
    224                 return remove((Element) node);
    225
    226             case COMMENT_NODE:
    227                 return remove((Comment) node);
    228
    229             case PROCESSING_INSTRUCTION_NODE:
    230                 return remove((ProcessingInstruction) node);
    231
    232             default:
    233                 invalidNodeTypeAddException(node);
    234
    235                 return false;
    236         }
    237     }
    238
    239     // typesafe versions using node classes
    240     public void add(Comment comment) {
    241         addNode(comment);
    242     }
    243
    244     public void add(Element element) {
    245         addNode(element);
    246     }
    247
    248     public void add(ProcessingInstruction pi) {
    249         addNode(pi);
    250     }
    251
    252     public boolean remove(Comment comment) {
    253         return removeNode(comment);
    254     }
    255
    256     public boolean remove(Element element) {
    257         return removeNode(element);
    258     }
    259
    260     public boolean remove(ProcessingInstruction pi) {
    261         return removeNode(pi);
    262     }
    263
    264     public Element elementByID(String elementID) {
    265         for (int i = 0, size = nodeCount(); i < size; i++) {
    266             Node node = node(i);
    267
    268             if (node instanceof Element) {
    269                 Element element = (Element) node;
    270                 String id = elementID(element);
    271
    272                 if ((id != null) && id.equals(elementID)) {
    273                     return element;
    274                 } else {
    275                     element = element.elementByID(elementID);
    276
    277                     if (element != null) {
    278                         return element;
    279                     }
    280                 }
    281             }
    282         }
    283
    284         return null;
    285     }
    286
    287     public void appendContent(Branch branch) {
    288         for (int i = 0, size = branch.nodeCount(); i < size; i++) {
    289             Node node = branch.node(i);
    290             add((Node) node.clone());
    291         }
    292     }
    293
    294     public Node node(int index) {
    295         Object object = contentList().get(index);
    296
    297         if (object instanceof Node) {
    298             return (Node) object;
    299         }
    300
    301         if (object instanceof String) {
    302             return getDocumentFactory().createText(object.toString());
    303         }
    304
    305         return null;
    306     }
    307
    308     public int nodeCount() {
    309         return contentList().size();
    310     }
    311
    312     public int indexOf(Node node) {
    313         return contentList().indexOf(node);
    314     }
    315
    316     public Iterator nodeIterator() {
    317         return contentList().iterator();
    318     }
    319
    320     // Implementation methods
    321
    322     /***
    323      * DOCUMENT ME!
    324      *
    325      * @param element
    326      *            DOCUMENT ME!
    327      *
    328      * @return the ID of the given <code>Element</code>
    329      */
    330     protected String elementID(Element element) {
    331         // XXX: there will be other ways of finding the ID
    332         // XXX: should probably have an IDResolver or something
    333         return element.attributeValue("ID");
    334     }
    335
    336     /***
    337      * DOCUMENT ME!
    338      *
    339      * @return the internal List used to manage the content
    340      */
    341     protected abstract List contentList();
    342
    343     /***
    344      * A Factory Method pattern which creates a List implementation used to
    345      * store content
    346      *
    347      * @return DOCUMENT ME!
    348      */
    349     protected List createContentList() {
    350         return new ArrayList(DEFAULT_CONTENT_LIST_SIZE);
    351     }
    352
    353     /***
    354      * A Factory Method pattern which creates a List implementation used to
    355      * store content
    356      *
    357      * @param size
    358      *            DOCUMENT ME!
    359      *
    360      * @return DOCUMENT ME!
    361      */
    362     protected List createContentList(int size) {
    363         return new ArrayList(size);
    364     }
    365
    366     /***
    367      * A Factory Method pattern which creates a BackedList implementation used
    368      * to store results of a filtered content query.
    369      *
    370      * @return DOCUMENT ME!
    371      */
    372     protected BackedList createResultList() {
    373         return new BackedList(this, contentList());
    374     }
    375
    376     /***
    377      * A Factory Method pattern which creates a BackedList implementation which
    378      * contains a single result
    379      *
    380      * @param result
    381      *            DOCUMENT ME!
    382      *
    383      * @return DOCUMENT ME!
    384      */
    385     protected List createSingleResultList(Object result) {
    386         BackedList list = new BackedList(this, contentList(), 1);
    387         list.addLocal(result);
    388
    389         return list;
    390     }
    391
    392     /***
    393      * A Factory Method pattern which creates an empty a BackedList
    394      * implementation
    395      *
    396      * @return DOCUMENT ME!
    397      */
    398     protected List createEmptyList() {
    399         return new BackedList(this, contentList(), 0);
    400     }
    401
    402     protected abstract void addNode(Node node);
    403
    404     protected abstract void addNode(int index, Node node);
    405
    406     protected abstract boolean removeNode(Node node);
    407
    408     /***
    409      * Called when a new child node has been added to me to allow any parent
    410      * relationships to be created or events to be fired.
    411      *
    412      * @param node
    413      *            DOCUMENT ME!
    414      */
    415     protected abstract void childAdded(Node node);
    416
    417     /***
    418      * Called when a child node has been removed to allow any parent
    419      * relationships to be deleted or events to be fired.
    420      *
    421      * @param node
    422      *            DOCUMENT ME!
    423      */
    424     protected abstract void childRemoved(Node node);
    425
    426     /***
    427      * Called when the given List content has been removed so each node should
    428      * have its parent and document relationships cleared
    429      */
    430     protected void contentRemoved() {
    431         List content = contentList();
    432
    433         for (int i = 0, size = content.size(); i < size; i++) {
    434             Object object = content.get(i);
    435
    436             if (object instanceof Node) {
    437                 childRemoved((Node) object);
    438             }
    439         }
    440     }
    441
    442     /***
    443      * Called when an invalid node has been added. Throws an {@link
    444      * IllegalAddException}.
    445      *
    446      * @param node
    447      *            DOCUMENT ME!
    448      *
    449      * @throws IllegalAddException
    450      *             DOCUMENT ME!
    451      */
    452     protected void invalidNodeTypeAddException(Node node) {
    453         throw new IllegalAddException("Invalid node type. Cannot add node: "
    454                 + node + " to this branch: " + this);
    455     }
    456 }
    457
    458 /*
    459  * Redistribution and use of this software and associated documentation
    460  * ("Software"), with or without modification, are permitted provided that the
    461  * following conditions are met:
    462  *
    463  * 1. Redistributions of source code must retain copyright statements and
    464  * notices. Redistributions must also contain a copy of this document.
    465  *
    466  * 2. Redistributions in binary form must reproduce the above copyright notice,
    467  * this list of conditions and the following disclaimer in the documentation
    468  * and/or other materials provided with the distribution.
    469  *
    470  * 3. The name "DOM4J" must not be used to endorse or promote products derived
    471  * from this Software without prior written permission of MetaStuff, Ltd. For
    472  * written permission, please contact dom4j-info@metastuff.com.
    473  *
    474  * 4. Products derived from this Software may not be called "DOM4J" nor may
    475  * "DOM4J" appear in their names without prior written permission of MetaStuff,
    476  * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
    477  *
    478  * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
    479  *
    480  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
    481  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    482  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    483  * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
    484  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    485  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    486  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    487  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    488  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    489  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    490  * POSSIBILITY OF SUCH DAMAGE.
    491  *
    492  * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
    493  */