/* kXML 2 * * Copyright (C) 2000, 2001, 2002 * Stefan Haustein * D-46045 Oberhausen (Rhld.), * Germany. All Rights Reserved. * * The contents of this file are subject to the "Common Public * License" (CPL); you may not use this file except in compliance * with the License. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific terms governing rights and limitations * under the License. * * Thanks to Paul Palaszewski, Wilhelm Fitzpatrick, * Eric Foster-Johnson, Michael Angel, and Liam Quinn for providing various * fixes and hints for the KXML 1 parser. * */ package org.kxml2.kdom; import java.util.*; import java.io.*; import org.xmlpull.v1.*; /** A common base class for Document and Element, also used for storing XML fragments. */ public class Node { //implements XmlIO{ public static final int DOCUMENT = 0; public static final int ELEMENT = 2; public static final int TEXT = 4; public static final int CDSECT = 5; public static final int ENTITY_REF = 6; public static final int IGNORABLE_WHITESPACE = 7; public static final int PROCESSING_INSTRUCTION = 8; public static final int COMMENT = 9; public static final int DOCDECL = 10; protected Vector children; protected StringBuffer types; /** inserts the given child object of the given type at the given index. */ public void addChild(int index, int type, Object child) { if (child == null) throw new NullPointerException(); if (children == null) { children = new Vector(); types = new StringBuffer(); } if (type == ELEMENT) { if (!(child instanceof Element)) throw new RuntimeException("Element obj expected)"); ((Element) child).setParent(this); } else if (!(child instanceof String)) throw new RuntimeException("String expected"); children.insertElementAt(child, index); types.insert(index, (char) type); } /** convenience method for addChild (getChildCount (), child) */ public void addChild(int type, Object child) { addChild(getChildCount(), type, child); } /** Builds a default element with the given properties. Elements should always be created using this method instead of the constructor in order to enable construction of specialized subclasses by deriving custom Document classes. Please note: For no namespace, please use Xml.NO_NAMESPACE, null is not a legal value. Currently, null is converted to Xml.NO_NAMESPACE, but future versions may throw an exception. */ public Element createElement( String namespace, String name) { Element e = new Element(); e.namespace = namespace == null ? "" : namespace; e.name = name; return e; } /** Returns the child object at the given index. For child elements, an Element object is returned. For all other child types, a String is returned. */ public Object getChild(int index) { return children.elementAt(index); } /** Returns the number of child objects */ public int getChildCount() { return children == null ? 0 : children.size(); } /** returns the element at the given index. If the node at the given index is a text node, null is returned */ public Element getElement(int index) { Object child = getChild(index); return (child instanceof Element) ? (Element) child : null; } /** Returns the element with the given namespace and name. If the element is not found, or more than one matching elements are found, an exception is thrown. */ public Element getElement(String namespace, String name) { int i = indexOf(namespace, name, 0); int j = indexOf(namespace, name, i + 1); if (i == -1 || j != -1) throw new RuntimeException( "Element {" + namespace + "}" + name + (i == -1 ? " not found in " : " more than once in ") + this); return getElement(i); } /* returns "#document-fragment". For elements, the element name is returned public String getName() { return "#document-fragment"; } /** Returns the namespace of the current element. For Node and Document, Xml.NO_NAMESPACE is returned. public String getNamespace() { return ""; } public int getNamespaceCount () { return 0; } /** returns the text content if the element has text-only content. Throws an exception for mixed content public String getText() { StringBuffer buf = new StringBuffer(); int len = getChildCount(); for (int i = 0; i < len; i++) { if (isText(i)) buf.append(getText(i)); else if (getType(i) == ELEMENT) throw new RuntimeException("not text-only content!"); } return buf.toString(); } */ /** Returns the text node with the given index or null if the node with the given index is not a text node. */ public String getText(int index) { return (isText(index)) ? (String) getChild(index) : null; } /** Returns the type of the child at the given index. Possible types are ELEMENT, TEXT, COMMENT, and PROCESSING_INSTRUCTION */ public int getType(int index) { return types.charAt(index); } /** Convenience method for indexOf (getNamespace (), name, startIndex). public int indexOf(String name, int startIndex) { return indexOf(getNamespace(), name, startIndex); } */ /** Performs search for an element with the given namespace and name, starting at the given start index. A null namespace matches any namespace, please use Xml.NO_NAMESPACE for no namespace). returns -1 if no matching element was found. */ public int indexOf( String namespace, String name, int startIndex) { int len = getChildCount(); for (int i = startIndex; i < len; i++) { Element child = getElement(i); if (child != null && name.equals(child.getName()) && (namespace == null || namespace.equals(child.getNamespace()))) return i; } return -1; } public boolean isText (int i) { int t = getType (i); return t == TEXT || t == IGNORABLE_WHITESPACE || t == CDSECT; } /** Recursively builds the child elements from the given parser until an end tag or end document is found. The end tag is not consumed. */ public void parse(XmlPullParser parser) throws IOException, XmlPullParserException { boolean leave = false; do { int type = parser.getEventType (); switch (type) { case XmlPullParser.START_TAG : { Element child = createElement( parser.getNamespace(), parser.getName()); // child.setAttributes (event.getAttributes ()); addChild(ELEMENT, child); // order is important here since // setparent may perform some init code! child.parse(parser); break; } case XmlPullParser.END_DOCUMENT : case XmlPullParser.END_TAG : leave = true; break; default : if (parser.getText () == null) addChild(ENTITY_REF, parser.getName ()); else addChild(type == XmlPullParser.ENTITY_REF ? TEXT : type, parser.getText()); parser.nextToken(); } } while (!leave); } /** Removes the child object at the given index */ public void removeChild(int idx) { children.removeElementAt(idx); /*** Modification by HHS - start ***/ // types.deleteCharAt (index); /***/ int n = types.length() - 1; for (int i = idx; i < n; i++) types.setCharAt(i, types.charAt(i + 1)); types.setLength(n); /*** Modification by HHS - end ***/ } /* returns a valid XML representation of this Element including attributes and children. public String toString() { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); XmlWriter xw = new XmlWriter(new OutputStreamWriter(bos)); write(xw); xw.close(); return new String(bos.toByteArray()); } catch (IOException e) { throw new RuntimeException(e.toString()); } } */ /** Writes this node to the given XmlWriter. For node and document, this method is identical to writeChildren, except that the stream is flushed automatically. */ public void write(XmlSerializer writer) throws IOException { writeChildren(writer); writer.flush(); } /** Writes the children of this node to the given XmlWriter. */ public void writeChildren(XmlSerializer writer) throws IOException { if (children == null) return; int len = children.size(); for (int i = 0; i < len; i++) { int type = getType(i); Object child = children.elementAt(i); switch (type) { case ELEMENT : ((Element) child).write(writer); break; case TEXT : writer.text((String) child); break; case IGNORABLE_WHITESPACE : writer.ignorableWhitespace((String) child); break; case CDSECT: writer.cdsect((String) child); break; case COMMENT: writer.comment((String) child); break; case ENTITY_REF: writer.entityRef((String) child); break; case PROCESSING_INSTRUCTION: writer.processingInstruction ((String) child); break; case DOCDECL: writer.docdecl((String) child); break; default : throw new RuntimeException ("Illegal type: "+ type); } } } }