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.io.*;
24import java.util.*;
25
26import org.xmlpull.v1.*;
27
28/**
29 * In order to create an element, please use the createElement method
30 * instead of invoking the constructor directly. The right place to
31 * add user defined initialization code is the init method. */
32
33public class Element extends Node {
34
35    protected String namespace;
36    protected String name;
37    protected Vector attributes;
38    protected Node parent;
39    protected Vector prefixes;
40
41    public Element() {
42    }
43
44    /**
45     * called when all properties are set, but before children
46     * are parsed. Please do not use setParent for initialization
47     * code any longer. */
48
49    public void init() {
50    }
51
52    /**
53     * removes all children and attributes */
54
55    public void clear() {
56        attributes = null;
57        children = null;
58    }
59
60    /**
61     * Forwards creation request to parent if any, otherwise
62     * calls super.createElement. */
63
64    public Element createElement(
65            String namespace,
66            String name) {
67
68        return (this.parent == null)
69                ? super.createElement(namespace, name)
70                : this.parent.createElement(namespace, name);
71    }
72
73    /**
74     * Returns the number of attributes of this element. */
75
76    public int getAttributeCount() {
77        return attributes == null ? 0 : attributes.size();
78    }
79
80    public String getAttributeNamespace(int index) {
81        return ((String[]) attributes.elementAt(index))[0];
82    }
83
84    /*    public String getAttributePrefix (int index) {
85            return ((String []) attributes.elementAt (index)) [1];
86        }*/
87
88    public String getAttributeName(int index) {
89        return ((String[]) attributes.elementAt(index))[1];
90    }
91
92    public String getAttributeValue(int index) {
93        return ((String[]) attributes.elementAt(index))[2];
94    }
95
96    public String getAttributeValue(String namespace, String name) {
97        for (int i = 0; i < getAttributeCount(); i++) {
98            if (name.equals(getAttributeName(i))
99                    && (namespace == null || namespace.equals(getAttributeNamespace(i)))) {
100                return getAttributeValue(i);
101            }
102        }
103        return null;
104    }
105
106    /**
107     * Returns the root node, determined by ascending to the
108     * all parents un of the root element. */
109
110    public Node getRoot() {
111
112        Element current = this;
113
114        while (current.parent != null) {
115            if (!(current.parent instanceof Element))
116                return current.parent;
117            current = (Element) current.parent;
118        }
119
120        return current;
121    }
122
123    /**
124     * returns the (local) name of the element */
125
126    public String getName() {
127        return name;
128    }
129
130    /**
131     * returns the namespace of the element */
132
133    public String getNamespace() {
134        return namespace;
135    }
136
137    /**
138     * returns the namespace for the given prefix */
139
140    public String getNamespaceUri(String prefix) {
141        int cnt = getNamespaceCount();
142        for (int i = 0; i < cnt; i++) {
143            if (prefix == getNamespacePrefix(i) ||
144                    (prefix != null && prefix.equals(getNamespacePrefix(i))))
145                return getNamespaceUri(i);
146        }
147        return parent instanceof Element ? ((Element) parent).getNamespaceUri(prefix) : null;
148    }
149
150    /**
151     * returns the number of declared namespaces, NOT including
152     * parent elements */
153
154    public int getNamespaceCount() {
155        return (prefixes == null ? 0 : prefixes.size());
156    }
157
158    public String getNamespacePrefix(int i) {
159        return ((String[]) prefixes.elementAt(i))[0];
160    }
161
162    public String getNamespaceUri(int i) {
163        return ((String[]) prefixes.elementAt(i))[1];
164    }
165
166    /**
167     * Returns the parent node of this element */
168
169    public Node getParent() {
170        return parent;
171    }
172
173    /*
174     * Returns the parent element if available, null otherwise
175
176    public Element getParentElement() {
177        return (parent instanceof Element)
178            ? ((Element) parent)
179            : null;
180    }
181    */
182
183    /**
184     * Builds the child elements from the given Parser. By overwriting
185     * parse, an element can take complete control over parsing its
186     * subtree. */
187
188    public void parse(XmlPullParser parser)
189            throws IOException, XmlPullParserException {
190
191        for (int i = parser.getNamespaceCount(parser.getDepth() - 1); i < parser
192                .getNamespaceCount(parser.getDepth()); i++) {
193            setPrefix(parser.getNamespacePrefix(i), parser.getNamespaceUri(i));
194        }
195
196        for (int i = 0; i < parser.getAttributeCount(); i++)
197            setAttribute(parser.getAttributeNamespace(i),
198                    //                          parser.getAttributePrefix (i),
199                    parser.getAttributeName(i),
200                    parser.getAttributeValue(i));
201
202        //        if (prefixMap == null) throw new RuntimeException ("!!");
203
204        init();
205
206        if (parser.isEmptyElementTag())
207            parser.nextToken();
208        else {
209            parser.nextToken();
210            super.parse(parser);
211
212            if (getChildCount() == 0)
213                addChild(IGNORABLE_WHITESPACE, "");
214        }
215
216        parser.require(
217                XmlPullParser.END_TAG,
218                getNamespace(),
219                getName());
220
221        parser.nextToken();
222    }
223
224    /**
225     * Sets the given attribute; a value of null removes the attribute */
226
227    public void setAttribute(String namespace, String name, String value) {
228        if (attributes == null)
229            attributes = new Vector();
230
231        if (namespace == null)
232            namespace = "";
233
234        for (int i = attributes.size() - 1; i >= 0; i--) {
235            String[] attribut = (String[]) attributes.elementAt(i);
236            if (attribut[0].equals(namespace) &&
237                    attribut[1].equals(name)) {
238
239                if (value == null) {
240                    attributes.removeElementAt(i);
241                }
242                else {
243                    attribut[2] = value;
244                }
245                return;
246            }
247        }
248
249        attributes.addElement
250                (new String[] {
251                        namespace, name, value
252                });
253    }
254
255    /**
256     * Sets the given prefix; a namespace value of null removess the
257     * prefix */
258
259    public void setPrefix(String prefix, String namespace) {
260        if (prefixes == null)
261            prefixes = new Vector();
262        prefixes.addElement(new String[] {
263                prefix, namespace
264        });
265    }
266
267    /**
268     * sets the name of the element */
269
270    public void setName(String name) {
271        this.name = name;
272    }
273
274    /**
275     * sets the namespace of the element. Please note: For no
276     * namespace, please use Xml.NO_NAMESPACE, null is not a legal
277     * value. Currently, null is converted to Xml.NO_NAMESPACE, but
278     * future versions may throw an exception. */
279
280    public void setNamespace(String namespace) {
281        if (namespace == null)
282            throw new NullPointerException("Use \"\" for empty namespace");
283        this.namespace = namespace;
284    }
285
286    /**
287     * Sets the Parent of this element. Automatically called from the
288     * add method.  Please use with care, you can simply
289     * create inconsitencies in the document tree structure using
290     * this method!  */
291
292    protected void setParent(Node parent) {
293        this.parent = parent;
294    }
295
296    /**
297     * Writes this element and all children to the given XmlWriter. */
298
299    public void write(XmlSerializer writer)
300            throws IOException {
301
302        if (prefixes != null) {
303            for (int i = 0; i < prefixes.size(); i++) {
304                writer.setPrefix(getNamespacePrefix(i), getNamespaceUri(i));
305            }
306        }
307
308        writer.startTag(
309                getNamespace(),
310                getName());
311
312        int len = getAttributeCount();
313
314        for (int i = 0; i < len; i++) {
315            writer.attribute(
316                    getAttributeNamespace(i),
317                    getAttributeName(i),
318                    getAttributeValue(i));
319        }
320
321        writeChildren(writer);
322
323        writer.endTag(getNamespace(), getName());
324    }
325}
326