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