001: /* Copyright 2004 The Apache Software Foundation
002: *
003: * Licensed under the Apache License, Version 2.0 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015:
016: package org.apache.xmlbeans.samples.anytype;
017:
018: import org.apache.xmlbeans.*;
019: import org.apache.xmlbeans.samples.any.ListOfStrings;
020: import org.apache.xmlbeans.samples.any.RootDocument;
021: import org.apache.xmlbeans.samples.any.StringelementDocument;
022: import org.apache.xmlbeans.samples.any.RootDocument.Root.Arrayofany;
023: import org.w3c.dom.Element;
024: import org.w3c.dom.Node;
025: import org.w3c.dom.NodeList;
026:
027: import java.io.File;
028: import java.io.IOException;
029: import java.util.ArrayList;
030: import java.util.Iterator;
031:
032: import javax.xml.namespace.QName;
033:
034: /**
035: * A sample that illustrates various ways to manipulate XML whose
036: * schema defines elements as type xs:any. Unlike its treatment of
037: * other schema types, XMLBeans does not generate accessors for the
038: * xs:any particle when compiling schema. Instead, your code
039: * handles instances of this type through any of several alternative
040: * means, including XPath queries, the selectChildren method,
041: * XmlCursor instances and the DOM API. This samples illustrates
042: * these alternative approaches.
043: */
044: public class Any {
045: private static final String m_namespaceUri = "http://xmlbeans.apache.org/samples/any";
046:
047: /**
048: * Receives <root> XML instance, executing methods that
049: * edit the received instance or create a new one.
050: *
051: * @param args An array in which the first item is a
052: * path to the XML instance file.
053: */
054: public static void main(String[] args) {
055: Any this Sample = new Any();
056: System.out.println("Running Any.buildDocFromScratch\n");
057: this Sample.buildDocFromScratch();
058:
059: RootDocument rootDoc = (RootDocument) this Sample
060: .parseXml(args[0]);
061:
062: System.out
063: .println("Running Any.editExistingDocWithSelectChildren\n");
064: this Sample.editExistingDocWithSelectChildren(rootDoc);
065:
066: System.out.println("Running Any.editExistingDocWithDOM\n");
067: this Sample.editExistingDocWithDOM(rootDoc);
068:
069: System.out
070: .println("Running Any.editExistingDocWithSelectPath\n");
071: this Sample.editExistingDocWithSelectPath(rootDoc);
072: }
073:
074: /**
075: * Creates a new <root> document from scratch.
076: *
077: * This method illustrates how you can use XmlCursor instances
078: * to build XML that is defined in schema as xs:any.
079: *
080: * @return <code>true</code> if the new document is valid;
081: * otherwise, <code>false</code>.
082: */
083: public boolean buildDocFromScratch() {
084: // Start by creating a <root> element that will contain
085: // the children built by this method.
086: RootDocument rootDoc = RootDocument.Factory.newInstance();
087: RootDocument.Root root = rootDoc.addNewRoot();
088:
089: // Add the first element, <stringelement>.
090: root.setStringelement("some text");
091:
092: // Create an XmlObject in which to build the second
093: // element in the sequence, <anyfoo>. Here, the
094: // XmlObject instance is simply a kind of incubator
095: // for the XML. Later the XML will be moved into the
096: // document this code is building.
097: XmlObject anyFoo = XmlObject.Factory.newInstance();
098:
099: // 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: return false;
126: }
127:
128: childCursor.dispose();
129: rootCursor.dispose();
130:
131: // Print and validate the result.
132: System.out
133: .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(
151: RootDocument rootDoc) {
152: RootDocument.Root root = rootDoc.getRoot();
153:
154: // Select the <anyfoo> children of <root>.
155: XmlObject[] stringElements = root.selectChildren(new QName(
156: m_namespaceUri, "anyfoo"));
157:
158: // If the element is there, replace it with another element.
159: if (stringElements.length > 0) {
160: XmlCursor editCursor = stringElements[0].newCursor();
161: editCursor.removeXml();
162: editCursor
163: .beginElement(new QName(m_namespaceUri, "anybar"));
164: editCursor.insertChars("some other text");
165: editCursor.dispose();
166: }
167: System.out
168: .println("Output: The <anyfoo> element has been replaced\n"
169: + "by an <anybar> element.\n");
170: System.out.println(rootDoc + "\n");
171: return validateXml(rootDoc);
172: }
173:
174: /**
175: * Adds a new <bar> element between the first and second
176: * children of the <arrayofany> element.
177: *
178: * This method illustrates how you can use DOM methods to
179: * retrieve and edit elements whose type is defined as
180: * xs:any in schema.
181: *
182: * @param rootDoc An instance of the <root> XML document.
183: * @return <code>true</code> if the editing XML is valid;
184: * otherwise, <code>false</code>.
185: */
186: public boolean editExistingDocWithDOM(RootDocument rootDoc) {
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: Node node = childList.item(i);
203: if (node.getNodeType() == Node.ELEMENT_NODE) {
204: if (node.getLocalName().equals("stringelement")) {
205: firstElementChild = (Element) node;
206: break;
207: }
208: }
209: }
210: if (firstElementChild == null) {
211: return false;
212: }
213:
214: // Find the second child element and make sure it's
215: // <someelement>.
216: Node node = firstElementChild.getNextSibling();
217: do {
218: if (node.getNodeType() == Node.ELEMENT_NODE) {
219: if (node.getLocalName().equals("someelement")) {
220: secondElementChild = (Element) node;
221: break;
222: }
223: }
224: node = node.getNextSibling();
225: } while (node != null);
226: if (secondElementChild == null) {
227: return false;
228: }
229:
230: // Create and insert a new <bar> element.
231: Element fooElement = secondElementChild.getOwnerDocument()
232: .createElementNS("http://openuri.org", "bar");
233: Node valueNode = fooElement.getOwnerDocument().createTextNode(
234: "some text");
235: fooElement.appendChild(valueNode);
236: arrayOfAnyNode.insertBefore(fooElement, secondElementChild);
237:
238: System.out
239: .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: String namespaceDecl = "declare namespace any='"
259: + m_namespaceUri + "'; ";
260: XmlCursor selectionCursor = rootDoc.getRoot().getArrayofany()
261: .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
294: .println("Output: <stringlist> has been promoted to replace \n"
295: + "<someelement>, and there's a new <foo> element.\n");
296: System.out.println(rootDoc + "\n");
297: return validateXml(rootDoc);
298: }
299:
300: /**
301: * Like the code in the buildDocFromScratch method, this code
302: * uses the XmlCursor to build XML piece by piece, building
303: * out the Arrayofany instance it receives.
304: *
305: * @return A valid <arrayofany> element bound to an
306: * Arrrayofany instance.
307: */
308: private Arrayofany buildArrayOfAny(Arrayofany arrayOfAny) {
309: // Create a simple <stringelement> and move it into place
310: // under <arrayofany>.
311: StringelementDocument stringElementDoc = StringelementDocument.Factory
312: .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: return null;
341: }
342: stringList.set(stringListValue);
343:
344: // Validate the new XML.
345: if (!validateXml(arrayOfAny)) {
346: return null;
347: }
348:
349: return arrayOfAny;
350: }
351:
352: /**
353: * Creates an instance of the ListOfStrings complex type defined
354: * in the schema. The instance returned by this method can be
355: * inserted using either a set* operation or a cursor, as in
356: * {@link #buildArrayOfAny()}.
357: *
358: * @return A valid instance of ListOfStrings.
359: */
360: private ListOfStrings buildListOfStrings() {
361: // Create an instance of the ListOfStrings complex type.
362: ListOfStrings stringList = ListOfStrings.Factory.newInstance();
363: stringList.setId("001");
364:
365: // Add two children for the instance's root.
366: XmlString stringElement = stringList.addNewStringelement();
367: stringElement.setStringValue("string1");
368: stringElement = stringList.addNewStringelement();
369: stringElement.setStringValue("string2");
370:
371: // Validate the new XML.
372: if (!validateXml(stringList)) {
373: return null;
374: }
375:
376: return stringList;
377: }
378:
379: /**
380: * <p>Validates the XML, printing error messages when the XML is invalid. Note
381: * that this method will properly validate any instance of a compiled schema
382: * type because all of these types extend XmlObject.</p>
383: *
384: * <p>Note that in actual practice, you'll probably want to use an assertion
385: * when validating if you want to ensure that your code doesn't pass along
386: * invalid XML. This sample prints the generated XML whether or not it's
387: * valid so that you can see the result in both cases.</p>
388: *
389: * @param xml The XML to validate.
390: * @return <code>true</code> if the XML is valid; otherwise, <code>false</code>
391: */
392: public static boolean validateXml(XmlObject xml) {
393: boolean isXmlValid = false;
394:
395: // A collection instance to hold validation error messages.
396: ArrayList validationMessages = new ArrayList();
397:
398: // Validate the XML, collecting messages.
399: isXmlValid = xml.validate(new XmlOptions()
400: .setErrorListener(validationMessages));
401:
402: // If the XML isn't valid, print the messages.
403: if (!isXmlValid) {
404: printErrors(validationMessages);
405: }
406: return isXmlValid;
407: }
408:
409: /**
410: * Receives the collection containing errors found during
411: * validation and print the errors to the console.
412: *
413: * @param validationErrors The validation errors.
414: */
415: public static void printErrors(ArrayList validationErrors) {
416: Iterator iter = validationErrors.iterator();
417: while (iter.hasNext()) {
418: System.out.println(">> " + iter.next() + "\n");
419: }
420: }
421:
422: /**
423: * <p>Creates a File from the XML path provided in main arguments, then
424: * parses the file's contents into a type generated from schema.</p>
425: * <p/>
426: * <p>Note that this work might have been done in main. Isolating it here
427: * makes the code separately available from outside this class.</p>
428: *
429: * @param xmlFilePath A path to XML based on the schema in inventory.xsd.
430: * @return An instance of a generated schema type that contains the parsed
431: * XML.
432: */
433: public XmlObject parseXml(String xmlFilePath) {
434: File xmlFile = new File(xmlFilePath);
435: XmlObject xml = null;
436: try {
437: xml = XmlObject.Factory.parse(xmlFile);
438: } catch (XmlException e) {
439: e.printStackTrace();
440: } catch (IOException e) {
441: e.printStackTrace();
442: }
443: return xml;
444: }
445: }
|