001: /* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
002: *
003: * Permission is hereby granted, free of charge, to any person obtaining a copy
004: * of this software and associated documentation files (the "Software"), to deal
005: * in the Software without restriction, including without limitation the rights
006: * to use, copy, modify, merge, publish, distribute, sublicense, and/or
007: * sell copies of the Software, and to permit persons to whom the Software is
008: * furnished to do so, subject to the following conditions:
009: *
010: * The above copyright notice and this permission notice shall be included in
011: * all copies or substantial portions of the Software.
012: *
013: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
014: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
015: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
016: * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
017: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
018: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
019: * IN THE SOFTWARE. */
020:
021: package org.kxml2.kdom;
022:
023: import java.util.*;
024: import java.io.*;
025: import org.xmlpull.v1.*;
026:
027: /** A common base class for Document and Element, also used for
028: storing XML fragments. */
029:
030: public class Node { //implements XmlIO{
031:
032: public static final int DOCUMENT = 0;
033: public static final int ELEMENT = 2;
034: public static final int TEXT = 4;
035: public static final int CDSECT = 5;
036: public static final int ENTITY_REF = 6;
037: public static final int IGNORABLE_WHITESPACE = 7;
038: public static final int PROCESSING_INSTRUCTION = 8;
039: public static final int COMMENT = 9;
040: public static final int DOCDECL = 10;
041:
042: protected Vector children;
043: protected StringBuffer types;
044:
045: /** inserts the given child object of the given type at the
046: given index. */
047:
048: public void addChild(int index, int type, Object child) {
049:
050: if (child == null)
051: throw new NullPointerException();
052:
053: if (children == null) {
054: children = new Vector();
055: types = new StringBuffer();
056: }
057:
058: if (type == ELEMENT) {
059: if (!(child instanceof Element))
060: throw new RuntimeException("Element obj expected)");
061:
062: ((Element) child).setParent(this );
063: } else if (!(child instanceof String))
064: throw new RuntimeException("String expected");
065:
066: children.insertElementAt(child, index);
067: types.insert(index, (char) type);
068: }
069:
070: /** convenience method for addChild (getChildCount (), child) */
071:
072: public void addChild(int type, Object child) {
073: addChild(getChildCount(), type, child);
074: }
075:
076: /** Builds a default element with the given properties. Elements
077: should always be created using this method instead of the
078: constructor in order to enable construction of specialized
079: subclasses by deriving custom Document classes. Please note:
080: For no namespace, please use Xml.NO_NAMESPACE, null is not a
081: legal value. Currently, null is converted to Xml.NO_NAMESPACE,
082: but future versions may throw an exception. */
083:
084: public Element createElement(String namespace, String name) {
085:
086: Element e = new Element();
087: e.namespace = namespace == null ? "" : namespace;
088: e.name = name;
089: return e;
090: }
091:
092: /** Returns the child object at the given index. For child
093: elements, an Element object is returned. For all other child
094: types, a String is returned. */
095:
096: public Object getChild(int index) {
097: return children.elementAt(index);
098: }
099:
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("Element {"
125: + namespace
126: + "}"
127: + name
128: + (i == -1 ? " not found in "
129: : " more than once in ") + this );
130:
131: return getElement(i);
132: }
133:
134: /* returns "#document-fragment". For elements, the element name is returned
135:
136: public String getName() {
137: return "#document-fragment";
138: }
139:
140: /** Returns the namespace of the current element. For Node
141: and Document, Xml.NO_NAMESPACE is returned.
142:
143: public String getNamespace() {
144: return "";
145: }
146:
147: public int getNamespaceCount () {
148: return 0;
149: }
150:
151: /** returns the text content if the element has text-only
152: content. Throws an exception for mixed content
153:
154: public String getText() {
155:
156: StringBuffer buf = new StringBuffer();
157: int len = getChildCount();
158:
159: for (int i = 0; i < len; i++) {
160: if (isText(i))
161: buf.append(getText(i));
162: else if (getType(i) == ELEMENT)
163: throw new RuntimeException("not text-only content!");
164: }
165:
166: return buf.toString();
167: }
168: */
169:
170: /** Returns the text node with the given index or null if the node
171: with the given index is not a text node. */
172:
173: public String getText(int index) {
174: return (isText(index)) ? (String) getChild(index) : null;
175: }
176:
177: /** Returns the type of the child at the given index. Possible
178: types are ELEMENT, TEXT, COMMENT, and PROCESSING_INSTRUCTION */
179:
180: public int getType(int index) {
181: return types.charAt(index);
182: }
183:
184: /** Convenience method for indexOf (getNamespace (), name,
185: startIndex).
186:
187: public int indexOf(String name, int startIndex) {
188: return indexOf(getNamespace(), name, startIndex);
189: }
190: */
191:
192: /** Performs search for an element with the given namespace and
193: name, starting at the given start index. A null namespace
194: matches any namespace, please use Xml.NO_NAMESPACE for no
195: namespace). returns -1 if no matching element was found. */
196:
197: public int indexOf(String namespace, String name, int startIndex) {
198:
199: int len = getChildCount();
200:
201: for (int i = startIndex; i < len; i++) {
202:
203: Element child = getElement(i);
204:
205: if (child != null
206: && name.equals(child.getName())
207: && (namespace == null || namespace.equals(child
208: .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) throws IOException,
224: 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: Element child = createElement(parser.getNamespace(),
237: parser.getName());
238: // child.setAttributes (event.getAttributes ());
239: addChild(ELEMENT, child);
240:
241: // order is important here since
242: // setparent may perform some init code!
243:
244: child.parse(parser);
245: break;
246: }
247:
248: case XmlPullParser.END_DOCUMENT:
249: case XmlPullParser.END_TAG:
250: leave = true;
251: break;
252:
253: default:
254: if (parser.getText() != null)
255: addChild(type == XmlPullParser.ENTITY_REF ? TEXT
256: : type, parser.getText());
257: else if (type == XmlPullParser.ENTITY_REF
258: && parser.getName() != null) {
259: addChild(ENTITY_REF, parser.getName());
260: }
261: parser.nextToken();
262: }
263: } while (!leave);
264: }
265:
266: /** Removes the child object at the given index */
267:
268: public void removeChild(int idx) {
269: children.removeElementAt(idx);
270:
271: /*** Modification by HHS - start ***/
272: // types.deleteCharAt (index);
273: /***/
274: int n = types.length() - 1;
275:
276: for (int i = idx; i < n; i++)
277: types.setCharAt(i, types.charAt(i + 1));
278:
279: types.setLength(n);
280:
281: /*** Modification by HHS - end ***/
282: }
283:
284: /* returns a valid XML representation of this Element including
285: attributes and children.
286: public String toString() {
287: try {
288: ByteArrayOutputStream bos =
289: new ByteArrayOutputStream();
290: XmlWriter xw =
291: new XmlWriter(new OutputStreamWriter(bos));
292: write(xw);
293: xw.close();
294: return new String(bos.toByteArray());
295: }
296: catch (IOException e) {
297: throw new RuntimeException(e.toString());
298: }
299: }
300: */
301:
302: /** Writes this node to the given XmlWriter. For node and document,
303: this method is identical to writeChildren, except that the
304: stream is flushed automatically. */
305:
306: public void write(XmlSerializer writer) throws IOException {
307: writeChildren(writer);
308: writer.flush();
309: }
310:
311: /** Writes the children of this node to the given XmlWriter. */
312:
313: public void writeChildren(XmlSerializer writer) throws IOException {
314: if (children == null)
315: return;
316:
317: int len = children.size();
318:
319: for (int i = 0; i < len; i++) {
320: int type = getType(i);
321: Object child = children.elementAt(i);
322: switch (type) {
323: case ELEMENT:
324: ((Element) child).write(writer);
325: break;
326:
327: case TEXT:
328: writer.text((String) child);
329: break;
330:
331: case IGNORABLE_WHITESPACE:
332: writer.ignorableWhitespace((String) child);
333: break;
334:
335: case CDSECT:
336: writer.cdsect((String) child);
337: break;
338:
339: case COMMENT:
340: writer.comment((String) child);
341: break;
342:
343: case ENTITY_REF:
344: writer.entityRef((String) child);
345: break;
346:
347: case PROCESSING_INSTRUCTION:
348: writer.processingInstruction((String) child);
349: break;
350:
351: case DOCDECL:
352: writer.docdecl((String) child);
353: break;
354:
355: default:
356: throw new RuntimeException("Illegal type: " + type);
357: }
358: }
359: }
360: }
|