Home | History | Annotate | Download | only in kdom
      1 /* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
      2  *
      3  * Permission is hereby granted, free of charge, to any person obtaining a copy
      4  * of this software and associated documentation files (the "Software"), to deal
      5  * in the Software without restriction, including without limitation the rights
      6  * to use, copy, modify, merge, publish, distribute, sublicense, and/or
      7  * sell copies of the Software, and to permit persons to whom the Software is
      8  * furnished to do so, subject to the following conditions:
      9  *
     10  * The  above copyright notice and this permission notice shall be included in
     11  * all copies or substantial portions of the Software.
     12  *
     13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
     19  * IN THE SOFTWARE. */
     20 
     21 package org.kxml2.kdom;
     22 
     23 import java.util.*;
     24 import java.io.*;
     25 import org.xmlpull.v1.*;
     26 /** A common base class for Document and Element, also used for
     27     storing XML fragments. */
     28 
     29 public class Node { //implements XmlIO{
     30 
     31     public static final int DOCUMENT = 0;
     32     public static final int ELEMENT = 2;
     33     public static final int TEXT = 4;
     34     public static final int CDSECT = 5;
     35     public static final int ENTITY_REF = 6;
     36     public static final int IGNORABLE_WHITESPACE = 7;
     37     public static final int PROCESSING_INSTRUCTION = 8;
     38     public static final int COMMENT = 9;
     39     public static final int DOCDECL = 10;
     40 
     41     protected Vector children;
     42     protected StringBuffer types;
     43 
     44     /** inserts the given child object of the given type at the
     45     given index. */
     46 
     47     public void addChild(int index, int type, Object child) {
     48 
     49         if (child == null)
     50             throw new NullPointerException();
     51 
     52         if (children == null) {
     53             children = new Vector();
     54             types = new StringBuffer();
     55         }
     56 
     57         if (type == ELEMENT) {
     58             if (!(child instanceof Element))
     59                 throw new RuntimeException("Element obj expected)");
     60 
     61             ((Element) child).setParent(this);
     62         }
     63         else if (!(child instanceof String))
     64             throw new RuntimeException("String expected");
     65 
     66         children.insertElementAt(child, index);
     67         types.insert(index, (char) type);
     68     }
     69 
     70     /** convenience method for addChild (getChildCount (), child) */
     71 
     72     public void addChild(int type, Object child) {
     73         addChild(getChildCount(), type, child);
     74     }
     75 
     76     /** Builds a default element with the given properties. Elements
     77     should always be created using this method instead of the
     78     constructor in order to enable construction of specialized
     79     subclasses by deriving custom Document classes. Please note:
     80     For no namespace, please use Xml.NO_NAMESPACE, null is not a
     81     legal value. Currently, null is converted to Xml.NO_NAMESPACE,
     82     but future versions may throw an exception. */
     83 
     84     public Element createElement(String namespace, String name) {
     85 
     86         Element e = new Element();
     87         e.namespace = namespace == null ? "" : namespace;
     88         e.name = name;
     89         return e;
     90     }
     91 
     92     /** Returns the child object at the given index.  For child
     93         elements, an Element object is returned. For all other child
     94         types, a String is returned. */
     95 
     96     public Object getChild(int index) {
     97         return children.elementAt(index);
     98     }
     99 
    100     /** Returns the number of child objects */
    101 
    102     public int getChildCount() {
    103         return children == null ? 0 : children.size();
    104     }
    105 
    106     /** returns the element at the given index. If the node at the
    107     given index is a text node, null is returned */
    108 
    109     public Element getElement(int index) {
    110         Object child = getChild(index);
    111         return (child instanceof Element) ? (Element) child : null;
    112     }
    113 
    114     /** Returns the element with the given namespace and name. If the
    115         element is not found, or more than one matching elements are
    116         found, an exception is thrown. */
    117 
    118     public Element getElement(String namespace, String name) {
    119 
    120         int i = indexOf(namespace, name, 0);
    121         int j = indexOf(namespace, name, i + 1);
    122 
    123         if (i == -1 || j != -1)
    124             throw new RuntimeException(
    125                 "Element {"
    126                     + namespace
    127                     + "}"
    128                     + name
    129                     + (i == -1 ? " not found in " : " more than once in ")
    130                     + this);
    131 
    132         return getElement(i);
    133     }
    134 
    135     /* returns "#document-fragment". For elements, the element name is returned
    136 
    137     public String getName() {
    138         return "#document-fragment";
    139     }
    140 
    141     /** Returns the namespace of the current element. For Node
    142         and Document, Xml.NO_NAMESPACE is returned.
    143 
    144     public String getNamespace() {
    145         return "";
    146     }
    147 
    148     public int getNamespaceCount () {
    149         return 0;
    150     }
    151 
    152     /** returns the text content if the element has text-only
    153     content. Throws an exception for mixed content
    154 
    155     public String getText() {
    156 
    157         StringBuffer buf = new StringBuffer();
    158         int len = getChildCount();
    159 
    160         for (int i = 0; i < len; i++) {
    161             if (isText(i))
    162                 buf.append(getText(i));
    163             else if (getType(i) == ELEMENT)
    164                 throw new RuntimeException("not text-only content!");
    165         }
    166 
    167         return buf.toString();
    168     }
    169     */
    170 
    171     /** Returns the text node with the given index or null if the node
    172         with the given index is not a text node. */
    173 
    174     public String getText(int index) {
    175         return (isText(index)) ? (String) getChild(index) : null;
    176     }
    177 
    178     /** Returns the type of the child at the given index. Possible
    179     types are ELEMENT, TEXT, COMMENT, and PROCESSING_INSTRUCTION */
    180 
    181     public int getType(int index) {
    182         return types.charAt(index);
    183     }
    184 
    185     /** Convenience method for indexOf (getNamespace (), name,
    186         startIndex).
    187 
    188     public int indexOf(String name, int startIndex) {
    189         return indexOf(getNamespace(), name, startIndex);
    190     }
    191     */
    192 
    193     /** Performs search for an element with the given namespace and
    194     name, starting at the given start index. A null namespace
    195     matches any namespace, please use Xml.NO_NAMESPACE for no
    196     namespace).  returns -1 if no matching element was found. */
    197 
    198     public int indexOf(String namespace, String name, int startIndex) {
    199 
    200         int len = getChildCount();
    201 
    202         for (int i = startIndex; i < len; i++) {
    203 
    204             Element child = getElement(i);
    205 
    206             if (child != null
    207                 && name.equals(child.getName())
    208                 && (namespace == null || namespace.equals(child.getNamespace())))
    209                 return i;
    210         }
    211         return -1;
    212     }
    213 
    214     public boolean isText(int i) {
    215         int t = getType(i);
    216         return t == TEXT || t == IGNORABLE_WHITESPACE || t == CDSECT;
    217     }
    218 
    219     /** Recursively builds the child elements from the given parser
    220     until an end tag or end document is found.
    221         The end tag is not consumed. */
    222 
    223     public void parse(XmlPullParser parser)
    224         throws IOException, XmlPullParserException {
    225 
    226         boolean leave = false;
    227 
    228         do {
    229             int type = parser.getEventType();
    230 
    231    //         System.out.println(parser.getPositionDescription());
    232 
    233             switch (type) {
    234 
    235                 case XmlPullParser.START_TAG :
    236                     {
    237                         Element child =
    238                             createElement(
    239                                 parser.getNamespace(),
    240                                 parser.getName());
    241                         //    child.setAttributes (event.getAttributes ());
    242                         addChild(ELEMENT, child);
    243 
    244                         // order is important here since
    245                         // setparent may perform some init code!
    246 
    247                         child.parse(parser);
    248                         break;
    249                     }
    250 
    251                 case XmlPullParser.END_DOCUMENT :
    252                 case XmlPullParser.END_TAG :
    253                     leave = true;
    254                     break;
    255 
    256                 default :
    257                     if (parser.getText() != null)
    258                         addChild(
    259                             type == XmlPullParser.ENTITY_REF ? TEXT : type,
    260                             parser.getText());
    261                     else if (
    262                         type == XmlPullParser.ENTITY_REF
    263                             && parser.getName() != null) {
    264                         addChild(ENTITY_REF, parser.getName());
    265                     }
    266                     parser.nextToken();
    267             }
    268         }
    269         while (!leave);
    270     }
    271 
    272     /** Removes the child object at the given index */
    273 
    274     public void removeChild(int idx) {
    275         children.removeElementAt(idx);
    276 
    277         /***  Modification by HHS - start ***/
    278         //      types.deleteCharAt (index);
    279         /***/
    280         int n = types.length() - 1;
    281 
    282         for (int i = idx; i < n; i++)
    283             types.setCharAt(i, types.charAt(i + 1));
    284 
    285         types.setLength(n);
    286 
    287         /***  Modification by HHS - end   ***/
    288     }
    289 
    290     /* returns a valid XML representation of this Element including
    291         attributes and children.
    292     public String toString() {
    293         try {
    294             ByteArrayOutputStream bos =
    295                 new ByteArrayOutputStream();
    296             XmlWriter xw =
    297                 new XmlWriter(new OutputStreamWriter(bos));
    298             write(xw);
    299             xw.close();
    300             return new String(bos.toByteArray());
    301         }
    302         catch (IOException e) {
    303             throw new RuntimeException(e.toString());
    304         }
    305     }
    306     */
    307 
    308     /** Writes this node to the given XmlWriter. For node and document,
    309         this method is identical to writeChildren, except that the
    310         stream is flushed automatically. */
    311 
    312     public void write(XmlSerializer writer) throws IOException {
    313         writeChildren(writer);
    314         writer.flush();
    315     }
    316 
    317     /** Writes the children of this node to the given XmlWriter. */
    318 
    319     public void writeChildren(XmlSerializer writer) throws IOException {
    320         if (children == null)
    321             return;
    322 
    323         int len = children.size();
    324 
    325         for (int i = 0; i < len; i++) {
    326             int type = getType(i);
    327             Object child = children.elementAt(i);
    328             switch (type) {
    329                 case ELEMENT :
    330                      ((Element) child).write(writer);
    331                     break;
    332 
    333                 case TEXT :
    334                     writer.text((String) child);
    335                     break;
    336 
    337                 case IGNORABLE_WHITESPACE :
    338                     writer.ignorableWhitespace((String) child);
    339                     break;
    340 
    341                 case CDSECT :
    342                     writer.cdsect((String) child);
    343                     break;
    344 
    345                 case COMMENT :
    346                     writer.comment((String) child);
    347                     break;
    348 
    349                 case ENTITY_REF :
    350                     writer.entityRef((String) child);
    351                     break;
    352 
    353                 case PROCESSING_INSTRUCTION :
    354                     writer.processingInstruction((String) child);
    355                     break;
    356 
    357                 case DOCDECL :
    358                     writer.docdecl((String) child);
    359                     break;
    360 
    361                 default :
    362                     throw new RuntimeException("Illegal type: " + type);
    363             }
    364         }
    365     }
    366 }
    367