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.ksoap2.kdom; 22 23 import java.util.*; 24 import java.io.*; 25 import org.xmlpull.v1.*; 26 27 /** A common base class for Document and Element, also used for 28 storing XML fragments. */ 29 30 public 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 addChild(int index, int type, Object child)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 addChild(int type, Object child)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 createElement(String namespace, String name)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 getChild(int index)97 public Object getChild(int index) { 98 return children.elementAt(index); 99 } 100 101 /** Returns the number of child objects */ 102 getChildCount()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 getElement(int index)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 getElement(String namespace, String name)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 getText(int index)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 getType(int index)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 indexOf(String namespace, String name, int startIndex)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 isText(int i)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 parse(XmlPullParser parser)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 removeChild(int idx)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 write(XmlSerializer writer)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 writeChildren(XmlSerializer writer)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