1 /* Copyright 2004 The Apache Software Foundation 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package org.apache.xmlbeans.samples.anytype; 17 18 import org.apache.xmlbeans; 19 import org.apache.xmlbeans.samples.any.ListOfStrings; 20 import org.apache.xmlbeans.samples.any.RootDocument; 21 import org.apache.xmlbeans.samples.any.StringelementDocument; 22 import org.apache.xmlbeans.samples.any.RootDocument.Root.Arrayofany; 23 import org.w3c.dom.Element; 24 import org.w3c.dom.Node; 25 import org.w3c.dom.NodeList; 26 27 import java.io.File; 28 import java.io.IOException; 29 import java.util.ArrayList; 30 import java.util.Iterator; 31 32 import javax.xml.namespace.QName; 33 34 /** 35 * A sample that illustrates various ways to manipulate XML whose 36 * schema defines elements as type xs:any. Unlike its treatment of 37 * other schema types, XMLBeans does not generate accessors for the 38 * xs:any particle when compiling schema. Instead, your code 39 * handles instances of this type through any of several alternative 40 * means, including XPath queries, the selectChildren method, 41 * XmlCursor instances and the DOM API. This samples illustrates 42 * these alternative approaches. 43 */ 44 public class Any 45 { 46 private static final String m_namespaceUri = "http://xmlbeans.apache.org/samples/any"; 47 48 /** 49 * Receives <root> XML instance, executing methods that 50 * edit the received instance or create a new one. 51 * 52 * @param args An array in which the first item is a 53 * path to the XML instance file. 54 */ 55 public static void main(String[] args) 56 { 57 Any thisSample = new Any(); 58 System.out.println("Running Any.buildDocFromScratch\n"); 59 thisSample.buildDocFromScratch(); 60 61 RootDocument rootDoc = (RootDocument)thisSample.parseXml(args[0]); 62 63 System.out.println("Running Any.editExistingDocWithSelectChildren\n"); 64 thisSample.editExistingDocWithSelectChildren(rootDoc); 65 66 System.out.println("Running Any.editExistingDocWithDOM\n"); 67 thisSample.editExistingDocWithDOM(rootDoc); 68 69 System.out.println("Running Any.editExistingDocWithSelectPath\n"); 70 thisSample.editExistingDocWithSelectPath(rootDoc); 71 } 72 73 /** 74 * Creates a new <root> document from scratch. 75 * 76 * This method illustrates how you can use XmlCursor instances 77 * to build XML that is defined in schema as xs:any. 78 * 79 * @return <code>true</code> if the new document is valid; 80 * otherwise, <code>false</code>. 81 */ 82 public boolean buildDocFromScratch() 83 { 84 // Start by creating a <root> element that will contain 85 // the children built by this method. 86 RootDocument rootDoc = RootDocument.Factory.newInstance(); 87 RootDocument.Root root = rootDoc.addNewRoot(); 88 89 // Add the first element, <stringelement>. 90 root.setStringelement("some text"); 91 92 // Create an XmlObject in which to build the second 93 // element in the sequence, <anyfoo>. Here, the 94 // XmlObject instance is simply a kind of incubator 95 // for the XML. Later the XML will be moved into the 96 // document this code is building. 97 XmlObject anyFoo = XmlObject.Factory.newInstance(); 98 99 // Add a cursor to do the work of building the XML. 100 XmlCursor childCursor = anyFoo.newCursor(); 101 childCursor.toNextToken(); 102 103 // Add the element in the schema's namespace, then add 104 // element content. 105 childCursor.beginElement(new QName(m_namespaceUri, "anyfoo")); 106 childCursor.insertChars("some text"); 107 108 // Move the cursor back to the new element's top, where 109 // it can grab the element's XML. 110 childCursor.toStartDoc(); 111 childCursor.toNextToken(); 112 113 // Move the XML into the <root> document by moving it 114 // from a position at one cursor to a position at 115 // another. 116 XmlCursor rootCursor = root.newCursor(); 117 rootCursor.toEndToken(); 118 childCursor.moveXml(rootCursor); 119 120 // Add the fourth element, <arrayofany>, by building it 121 // elsewhere, then moving the new XML into place under 122 // <root>. 123 Arrayofany arrayOfAny = root.addNewArrayofany(); 124 if (buildArrayOfAny(arrayOfAny) == null) 125 { 126 return false; 127 } 128 129 childCursor.dispose(); 130 rootCursor.dispose(); 131 132 // Print and validate the result. 133 System.out.println("Output: The <root> document built from scratch.\n"); 134 System.out.println(rootDoc + "\n"); 135 return validateXml(rootDoc); 136 } 137 138 /** 139 * Replaces the <anyfoo> element with an <anybar> element in the 140 * incoming XML. 141 * 142 * This method illustrates how you can use the XmlCursor.selectChildren 143 * method to retrieve child elements whose type is defined as 144 * xs:any in schema. 145 * 146 * @param rootDoc An instance of the <root> XML document. 147 * @return <code>true</code> if the editing XML is valid; 148 * otherwise, <code>false</code>. 149 */ 150 public boolean editExistingDocWithSelectChildren(RootDocument rootDoc) 151 { 152 RootDocument.Root root = rootDoc.getRoot(); 153 154 // Select the <anyfoo> children of <root>. 155 XmlObject[] stringElements = 156 root.selectChildren(new QName(m_namespaceUri, "anyfoo")); 157 158 // If the element is there, replace it with another element. 159 if (stringElements.length > 0) 160 { 161 XmlCursor editCursor = stringElements[0].newCursor(); 162 editCursor.removeXml(); 163 editCursor.beginElement(new QName(m_namespaceUri, "anybar")); 164 editCursor.insertChars("some other text"); 165 editCursor.dispose(); 166 } 167 System.out.println("Output: The <anyfoo> element has been replaced\n" + 168 "by an <anybar> element.\n"); 169 System.out.println(rootDoc + "\n"); 170 return validateXml(rootDoc); 171 } 172 173 /** 174 * Adds a new <bar> element between the first and second 175 * children of the <arrayofany> element. 176 * 177 * This method illustrates how you can use DOM methods to 178 * retrieve and edit elements whose type is defined as 179 * xs:any in schema. 180 * 181 * @param rootDoc An instance of the <root> XML document. 182 * @return <code>true</code> if the editing XML is valid; 183 * otherwise, <code>false</code>. 184 */ 185 public boolean editExistingDocWithDOM(RootDocument rootDoc) 186 { 187 RootDocument.Root root = rootDoc.getRoot(); 188 189 // Get the DOM nodes for the <arrayofany> element's children. 190 Node arrayOfAnyNode = root.getArrayofany().getDomNode(); 191 192 // You don't have get* accessors for any of the <arrayofany> 193 // element's children, so use DOM to identify the first 194 // and second elements while looping through the child list. 195 NodeList childList = arrayOfAnyNode.getChildNodes(); 196 Element firstElementChild = null; 197 Element secondElementChild = null; 198 199 // Find the first child element and make sure it's 200 // <stringelement>. 201 for (int i = 0; i < childList.getLength(); i++) 202 { 203 Node node = childList.item(i); 204 if (node.getNodeType() == Node.ELEMENT_NODE) 205 { 206 if (node.getLocalName().equals("stringelement")) 207 { 208 firstElementChild = (Element)node; 209 break; 210 } 211 } 212 } 213 if (firstElementChild == null) {return false;} 214 215 // Find the second child element and make sure it's 216 // <someelement>. 217 Node node = firstElementChild.getNextSibling(); 218 do { 219 if (node.getNodeType() == Node.ELEMENT_NODE) 220 { 221 if (node.getLocalName().equals("someelement")) 222 { 223 secondElementChild = (Element)node; 224 break; 225 } 226 } 227 node = node.getNextSibling(); 228 } while (node != null); 229 if (secondElementChild == null) {return false;} 230 231 // Create and insert a new <bar> element. 232 Element fooElement = 233 secondElementChild.getOwnerDocument().createElementNS("http://openuri.org","bar"); 234 Node valueNode = 235 fooElement.getOwnerDocument().createTextNode("some text"); 236 fooElement.appendChild(valueNode); 237 arrayOfAnyNode.insertBefore(fooElement, secondElementChild); 238 239 System.out.println("Output: <arrayofany> has a new <bar> child element.\n"); 240 System.out.println(rootDoc + "\n"); 241 return validateXml(rootDoc); 242 } 243 244 /** 245 * Edits incoming <root> XML to make the following changes: replace 246 * <somelement> with its <stringlist> child; add a new <foo> 247 * element as the second child of <arrayofany>. 248 * 249 * This method illustrates how you can use the selectPath method 250 * to find an element defined as xs:any in schema, then use 251 * XmlCursor instances to edit the XML. 252 * 253 * @param rootDoc An instance of the <root> XML document. 254 * @return <code>true</code> if the editing XML is valid; 255 * otherwise, <code>false</code>. 256 */ 257 public boolean editExistingDocWithSelectPath(RootDocument rootDoc) 258 { 259 String namespaceDecl = "declare namespace any='" + 260 m_namespaceUri + "'; "; 261 XmlCursor selectionCursor = rootDoc.getRoot().getArrayofany().newCursor(); 262 263 // Save the cursor's position for later, then use XPath 264 // and cursor movement to position the cursor at 265 // the <stringlist> element. 266 selectionCursor.push(); 267 selectionCursor.selectPath(namespaceDecl + 268 "$this//any:someelement/any:stringlist"); 269 selectionCursor.toNextSelection(); 270 271 // Create a new cursor and move it to the selection 272 // cursor's <someelement> parent. Moving the 273 // <stringlist> element to this position, displacing 274 // the <someelement> downward, then removing the 275 // <someelement> XML effectively replaces <someelement> 276 // with <stringlist>. 277 XmlCursor editCursor = selectionCursor.newCursor(); 278 editCursor.toParent(); 279 selectionCursor.moveXml(editCursor); 280 editCursor.removeXml(); 281 editCursor.dispose(); 282 283 // Return the cursor to the <arrayofany> element so you 284 // can do more editing. Then move the cursor to the second 285 // child and insert a new element as second child. 286 selectionCursor.pop(); 287 selectionCursor.toFirstChild(); 288 selectionCursor.toNextSibling(); 289 selectionCursor.beginElement("foo", "http://openuri.org"); 290 selectionCursor.insertChars("some text"); 291 selectionCursor.dispose(); 292 293 System.out.println("Output: <stringlist> has been promoted to replace \n" + 294 "<someelement>, and there's a new <foo> element.\n"); 295 System.out.println(rootDoc + "\n"); 296 return validateXml(rootDoc); 297 } 298 299 /** 300 * Like the code in the buildDocFromScratch method, this code 301 * uses the XmlCursor to build XML piece by piece, building 302 * out the Arrayofany instance it receives. 303 * 304 * @return A valid <arrayofany> element bound to an 305 * Arrrayofany instance. 306 */ 307 private Arrayofany buildArrayOfAny(Arrayofany arrayOfAny) 308 { 309 // Create a simple <stringelement> and move it into place 310 // under <arrayofany>. 311 StringelementDocument stringElementDoc = 312 StringelementDocument.Factory.newInstance(); 313 stringElementDoc.setStringelement("some text"); 314 XmlCursor childCursor = stringElementDoc.newCursor(); 315 childCursor.toFirstContentToken(); 316 317 // Add a cursor to mark the position at which the new child 318 // XML will be moved. 319 XmlCursor arrayCursor = arrayOfAny.newCursor(); 320 arrayCursor.toNextToken(); 321 childCursor.moveXml(arrayCursor); 322 childCursor.dispose(); 323 324 // Create a <someelement> that contains a <stringlist> 325 // child element, then get the XmlObject representing the new 326 // <stringlist>. Note that the XmlCursor.beginElement method 327 // leaves the cursor between START and END tokens -- where 328 // content can be placed. 329 arrayCursor.beginElement("someelement", m_namespaceUri); 330 arrayCursor.beginElement("stringlist", m_namespaceUri); 331 arrayCursor.toPrevToken(); 332 XmlObject stringList = arrayCursor.getObject(); 333 334 // The cursor's no longer needed. 335 arrayCursor.dispose(); 336 337 // Create the <stringlist> element's value and set it. 338 ListOfStrings stringListValue = buildListOfStrings(); 339 if (stringListValue == null) 340 { 341 return null; 342 } 343 stringList.set(stringListValue); 344 345 // Validate the new XML. 346 if (!validateXml(arrayOfAny)) 347 { 348 return null; 349 } 350 351 return arrayOfAny; 352 } 353 354 /** 355 * Creates an instance of the ListOfStrings complex type defined 356 * in the schema. The instance returned by this method can be 357 * inserted using either a set* operation or a cursor, as in 358 * {@link #buildArrayOfAny()}. 359 * 360 * @return A valid instance of ListOfStrings. 361 */ 362 private ListOfStrings buildListOfStrings() 363 { 364 // Create an instance of the ListOfStrings complex type. 365 ListOfStrings stringList = ListOfStrings.Factory.newInstance(); 366 stringList.setId("001"); 367 368 // Add two children for the instance's root. 369 XmlString stringElement = stringList.addNewStringelement(); 370 stringElement.setStringValue("string1"); 371 stringElement = stringList.addNewStringelement(); 372 stringElement.setStringValue("string2"); 373 374 // Validate the new XML. 375 if (!validateXml(stringList)) 376 { 377 return null; 378 } 379 380 return stringList; 381 } 382 383 /** 384 * <p>Validates the XML, printing error messages when the XML is invalid. Note 385 * that this method will properly validate any instance of a compiled schema 386 * type because all of these types extend XmlObject.</p> 387 * 388 * <p>Note that in actual practice, you'll probably want to use an assertion 389 * when validating if you want to ensure that your code doesn't pass along 390 * invalid XML. This sample prints the generated XML whether or not it's 391 * valid so that you can see the result in both cases.</p> 392 * 393 * @param xml The XML to validate. 394 * @return <code>true</code> if the XML is valid; otherwise, <code>false</code> 395 */ 396 public static boolean validateXml(XmlObject xml) 397 { 398 boolean isXmlValid = false; 399 400 // A collection instance to hold validation error messages. 401 ArrayList validationMessages = new ArrayList(); 402 403 // Validate the XML, collecting messages. 404 isXmlValid = xml.validate( 405 new XmlOptions().setErrorListener(validationMessages)); 406 407 // If the XML isn't valid, print the messages. 408 if (!isXmlValid) 409 { 410 printErrors(validationMessages); 411 } 412 return isXmlValid; 413 } 414 415 /** 416 * Receives the collection containing errors found during 417 * validation and print the errors to the console. 418 * 419 * @param validationErrors The validation errors. 420 */ 421 public static void printErrors(ArrayList validationErrors) 422 { 423 Iterator iter = validationErrors.iterator(); 424 while (iter.hasNext()) 425 { 426 System.out.println(">> " + iter.next() + "\n"); 427 } 428 } 429 430 /** 431 * <p>Creates a File from the XML path provided in main arguments, then 432 * parses the file's contents into a type generated from schema.</p> 433 * <p/> 434 * <p>Note that this work might have been done in main. Isolating it here 435 * makes the code separately available from outside this class.</p> 436 * 437 * @param xmlFilePath A path to XML based on the schema in inventory.xsd. 438 * @return An instance of a generated schema type that contains the parsed 439 * XML. 440 */ 441 public XmlObject parseXml(String xmlFilePath) 442 { 443 File xmlFile = new File(xmlFilePath); 444 XmlObject xml = null; 445 try 446 { 447 xml = XmlObject.Factory.parse(xmlFile); 448 } catch (XmlException e) 449 { 450 e.printStackTrace(); 451 } catch (IOException e) 452 { 453 e.printStackTrace(); 454 } 455 return xml; 456 } 457 }