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
21package org.ksoap2.kdom;
22
23import java.util.*;
24import java.io.*;
25import org.xmlpull.v1.*;
26
27/** A common base class for Document and Element, also used for
28    storing XML fragments. */
29
30public class Node { //implements XmlIO{
31
32    public static final int DOCUMENT = 0;
33    public static final int ELEMENT = 2;
34    public static final int TEXT = 4;
35    public static final int CDSECT = 5;
36    public static final int ENTITY_REF = 6;
37    public static final int IGNORABLE_WHITESPACE = 7;
38    public static final int PROCESSING_INSTRUCTION = 8;
39    public static final int COMMENT = 9;
40    public static final int DOCDECL = 10;
41
42    protected Vector children;
43    protected StringBuffer types;
44
45    /** inserts the given child object of the given type at the
46    given index. */
47
48    public void addChild(int index, int type, Object child) {
49
50        if (child == null)
51            throw new NullPointerException();
52
53        if (children == null) {
54            children = new Vector();
55            types = new StringBuffer();
56        }
57
58        if (type == ELEMENT) {
59            if (!(child instanceof Element))
60                throw new RuntimeException("Element obj expected)");
61
62            ((Element) child).setParent(this);
63        }
64        else if (!(child instanceof String))
65            throw new RuntimeException("String expected");
66
67        children.insertElementAt(child, index);
68        types.insert(index, (char) type);
69    }
70
71    /** convenience method for addChild (getChildCount (), child) */
72
73    public void addChild(int type, Object child) {
74        addChild(getChildCount(), type, child);
75    }
76
77    /** Builds a default element with the given properties. Elements
78    should always be created using this method instead of the
79    constructor in order to enable construction of specialized
80    subclasses by deriving custom Document classes. Please note:
81    For no namespace, please use Xml.NO_NAMESPACE, null is not a
82    legal value. Currently, null is converted to Xml.NO_NAMESPACE,
83    but future versions may throw an exception. */
84
85    public Element createElement(String namespace, String name) {
86
87        Element e = new Element();
88        e.namespace = namespace == null ? "" : namespace;
89        e.name = name;
90        return e;
91    }
92
93    /** Returns the child object at the given index.  For child
94        elements, an Element object is returned. For all other child
95        types, a String is returned. */
96
97    public Object getChild(int index) {
98        return children.elementAt(index);
99    }
100
101    /** Returns the number of child objects */
102
103    public int getChildCount() {
104        return children == null ? 0 : children.size();
105    }
106
107    /** returns the element at the given index. If the node at the
108    given index is a text node, null is returned */
109
110    public Element getElement(int index) {
111        Object child = getChild(index);
112        return (child instanceof Element) ? (Element) child : null;
113    }
114
115    /** Returns the element with the given namespace and name. If the
116        element is not found, or more than one matching elements are
117        found, an exception is thrown. */
118
119    public Element getElement(String namespace, String name) {
120
121        int i = indexOf(namespace, name, 0);
122        int j = indexOf(namespace, name, i + 1);
123
124        if (i == -1 || j != -1)
125            throw new RuntimeException(
126                    "Element {"
127                            + namespace
128                            + "}"
129                            + name
130                            + (i == -1 ? " not found in " : " more than once in ")
131                            + this);
132
133        return getElement(i);
134    }
135
136    /* returns "#document-fragment". For elements, the element name is returned
137
138    public String getName() {
139        return "#document-fragment";
140    }
141
142    /** Returns the namespace of the current element. For Node
143        and Document, Xml.NO_NAMESPACE is returned.
144
145    public String getNamespace() {
146        return "";
147    }
148
149    public int getNamespaceCount () {
150        return 0;
151    }
152
153    /** returns the text content if the element has text-only
154    content. Throws an exception for mixed content
155
156    public String getText() {
157
158        StringBuffer buf = new StringBuffer();
159        int len = getChildCount();
160
161        for (int i = 0; i < len; i++) {
162            if (isText(i))
163                buf.append(getText(i));
164            else if (getType(i) == ELEMENT)
165                throw new RuntimeException("not text-only content!");
166        }
167
168        return buf.toString();
169    }
170    */
171
172    /** Returns the text node with the given index or null if the node
173        with the given index is not a text node. */
174
175    public String getText(int index) {
176        return (isText(index)) ? (String) getChild(index) : null;
177    }
178
179    /** Returns the type of the child at the given index. Possible
180    types are ELEMENT, TEXT, COMMENT, and PROCESSING_INSTRUCTION */
181
182    public int getType(int index) {
183        return types.charAt(index);
184    }
185
186    /** Convenience method for indexOf (getNamespace (), name,
187        startIndex).
188
189    public int indexOf(String name, int startIndex) {
190        return indexOf(getNamespace(), name, startIndex);
191    }
192    */
193
194    /** Performs search for an element with the given namespace and
195    name, starting at the given start index. A null namespace
196    matches any namespace, please use Xml.NO_NAMESPACE for no
197    namespace).  returns -1 if no matching element was found. */
198
199    public int indexOf(String namespace, String name, int startIndex) {
200
201        int len = getChildCount();
202
203        for (int i = startIndex; i < len; i++) {
204
205            Element child = getElement(i);
206
207            if (child != null
208                    && name.equals(child.getName())
209                    && (namespace == null || namespace.equals(child.getNamespace())))
210                return i;
211        }
212        return -1;
213    }
214
215    public boolean isText(int i) {
216        int t = getType(i);
217        return t == TEXT || t == IGNORABLE_WHITESPACE || t == CDSECT;
218    }
219
220    /** Recursively builds the child elements from the given parser
221    until an end tag or end document is found.
222        The end tag is not consumed. */
223
224    public void parse(XmlPullParser parser)
225            throws IOException, XmlPullParserException {
226
227        boolean leave = false;
228
229        do {
230            int type = parser.getEventType();
231
232            //         System.out.println(parser.getPositionDescription());
233
234            switch (type) {
235
236                case XmlPullParser.START_TAG: {
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 (type == XmlPullParser.ENTITY_REF
262                            && parser.getName() != null) {
263                        addChild(ENTITY_REF, parser.getName());
264                    }
265                    parser.nextToken();
266            }
267        } while (!leave);
268    }
269
270    /** Removes the child object at the given index */
271
272    public void removeChild(int idx) {
273        children.removeElementAt(idx);
274
275        /***  Modification by HHS - start ***/
276        //      types.deleteCharAt (index);
277        /***/
278        int n = types.length() - 1;
279
280        for (int i = idx; i < n; i++)
281            types.setCharAt(i, types.charAt(i + 1));
282
283        types.setLength(n);
284
285        /***  Modification by HHS - end   ***/
286    }
287
288    /* returns a valid XML representation of this Element including
289        attributes and children.
290    public String toString() {
291        try {
292            ByteArrayOutputStream bos =
293                new ByteArrayOutputStream();
294            XmlWriter xw =
295                new XmlWriter(new OutputStreamWriter(bos));
296            write(xw);
297            xw.close();
298            return new String(bos.toByteArray());
299        }
300        catch (IOException e) {
301            throw new RuntimeException(e.toString());
302        }
303    }
304    */
305
306    /** Writes this node to the given XmlWriter. For node and document,
307        this method is identical to writeChildren, except that the
308        stream is flushed automatically. */
309
310    public void write(XmlSerializer writer) throws IOException {
311        writeChildren(writer);
312        writer.flush();
313    }
314
315    /** Writes the children of this node to the given XmlWriter. */
316
317    public void writeChildren(XmlSerializer writer) throws IOException {
318        if (children == null)
319            return;
320
321        int len = children.size();
322
323        for (int i = 0; i < len; i++) {
324            int type = getType(i);
325            Object child = children.elementAt(i);
326            switch (type) {
327                case ELEMENT:
328                    ((Element) child).write(writer);
329                    break;
330
331                case TEXT:
332                    writer.text((String) child);
333                    break;
334
335                case IGNORABLE_WHITESPACE:
336                    writer.ignorableWhitespace((String) child);
337                    break;
338
339                case CDSECT:
340                    writer.cdsect((String) child);
341                    break;
342
343                case COMMENT:
344                    writer.comment((String) child);
345                    break;
346
347                case ENTITY_REF:
348                    writer.entityRef((String) child);
349                    break;
350
351                case PROCESSING_INSTRUCTION:
352                    writer.processingInstruction((String) child);
353                    break;
354
355                case DOCDECL:
356                    writer.docdecl((String) child);
357                    break;
358
359                default:
360                    throw new RuntimeException("Illegal type: " + type);
361            }
362        }
363    }
364}
365