blob: 058836122ddc59a16c4c0faf853d66d58e434e78 [file] [log] [blame]
/* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.xmlbeans.samples.anytype;
import org.apache.xmlbeans.*;
import org.apache.xmlbeans.samples.any.ListOfStrings;
import org.apache.xmlbeans.samples.any.RootDocument;
import org.apache.xmlbeans.samples.any.StringelementDocument;
import org.apache.xmlbeans.samples.any.RootDocument.Root.Arrayofany;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import javax.xml.namespace.QName;
/**
* A sample that illustrates various ways to manipulate XML whose
* schema defines elements as type xs:any. Unlike its treatment of
* other schema types, XMLBeans does not generate accessors for the
* xs:any particle when compiling schema. Instead, your code
* handles instances of this type through any of several alternative
* means, including XPath queries, the selectChildren method,
* XmlCursor instances and the DOM API. This samples illustrates
* these alternative approaches.
*/
public class Any
{
private static final String m_namespaceUri = "http://xmlbeans.apache.org/samples/any";
/**
* Receives <root> XML instance, executing methods that
* edit the received instance or create a new one.
*
* @param args An array in which the first item is a
* path to the XML instance file.
*/
public static void main(String[] args)
{
Any thisSample = new Any();
System.out.println("Running Any.buildDocFromScratch\n");
thisSample.buildDocFromScratch();
RootDocument rootDoc = (RootDocument)thisSample.parseXml(args[0]);
System.out.println("Running Any.editExistingDocWithSelectChildren\n");
thisSample.editExistingDocWithSelectChildren(rootDoc);
System.out.println("Running Any.editExistingDocWithDOM\n");
thisSample.editExistingDocWithDOM(rootDoc);
System.out.println("Running Any.editExistingDocWithSelectPath\n");
thisSample.editExistingDocWithSelectPath(rootDoc);
}
/**
* Creates a new <root> document from scratch.
*
* This method illustrates how you can use XmlCursor instances
* to build XML that is defined in schema as xs:any.
*
* @return <code>true</code> if the new document is valid;
* otherwise, <code>false</code>.
*/
public boolean buildDocFromScratch()
{
// Start by creating a <root> element that will contain
// the children built by this method.
RootDocument rootDoc = RootDocument.Factory.newInstance();
RootDocument.Root root = rootDoc.addNewRoot();
// Add the first element, <stringelement>.
root.setStringelement("some text");
// Create an XmlObject in which to build the second
// element in the sequence, <anyfoo>. Here, the
// XmlObject instance is simply a kind of incubator
// for the XML. Later the XML will be moved into the
// document this code is building.
XmlObject anyFoo = XmlObject.Factory.newInstance();
// Add a cursor to do the work of building the XML.
XmlCursor childCursor = anyFoo.newCursor();
childCursor.toNextToken();
// Add the element in the schema's namespace, then add
// element content.
childCursor.beginElement(new QName(m_namespaceUri, "anyfoo"));
childCursor.insertChars("some text");
// Move the cursor back to the new element's top, where
// it can grab the element's XML.
childCursor.toStartDoc();
childCursor.toNextToken();
// Move the XML into the <root> document by moving it
// from a position at one cursor to a position at
// another.
XmlCursor rootCursor = root.newCursor();
rootCursor.toEndToken();
childCursor.moveXml(rootCursor);
// Add the fourth element, <arrayofany>, by building it
// elsewhere, then moving the new XML into place under
// <root>.
Arrayofany arrayOfAny = root.addNewArrayofany();
if (buildArrayOfAny(arrayOfAny) == null)
{
return false;
}
childCursor.dispose();
rootCursor.dispose();
// Print and validate the result.
System.out.println("Output: The <root> document built from scratch.\n");
System.out.println(rootDoc + "\n");
return validateXml(rootDoc);
}
/**
* Replaces the <anyfoo> element with an <anybar> element in the
* incoming XML.
*
* This method illustrates how you can use the XmlCursor.selectChildren
* method to retrieve child elements whose type is defined as
* xs:any in schema.
*
* @param rootDoc An instance of the <root> XML document.
* @return <code>true</code> if the editing XML is valid;
* otherwise, <code>false</code>.
*/
public boolean editExistingDocWithSelectChildren(RootDocument rootDoc)
{
RootDocument.Root root = rootDoc.getRoot();
// Select the <anyfoo> children of <root>.
XmlObject[] stringElements =
root.selectChildren(new QName(m_namespaceUri, "anyfoo"));
// If the element is there, replace it with another element.
if (stringElements.length > 0)
{
XmlCursor editCursor = stringElements[0].newCursor();
editCursor.removeXml();
editCursor.beginElement(new QName(m_namespaceUri, "anybar"));
editCursor.insertChars("some other text");
editCursor.dispose();
}
System.out.println("Output: The <anyfoo> element has been replaced\n" +
"by an <anybar> element.\n");
System.out.println(rootDoc + "\n");
return validateXml(rootDoc);
}
/**
* Adds a new <bar> element between the first and second
* children of the <arrayofany> element.
*
* This method illustrates how you can use DOM methods to
* retrieve and edit elements whose type is defined as
* xs:any in schema.
*
* @param rootDoc An instance of the <root> XML document.
* @return <code>true</code> if the editing XML is valid;
* otherwise, <code>false</code>.
*/
public boolean editExistingDocWithDOM(RootDocument rootDoc)
{
RootDocument.Root root = rootDoc.getRoot();
// Get the DOM nodes for the <arrayofany> element's children.
Node arrayOfAnyNode = root.getArrayofany().getDomNode();
// You don't have get* accessors for any of the <arrayofany>
// element's children, so use DOM to identify the first
// and second elements while looping through the child list.
NodeList childList = arrayOfAnyNode.getChildNodes();
Element firstElementChild = null;
Element secondElementChild = null;
// Find the first child element and make sure it's
// <stringelement>.
for (int i = 0; i < childList.getLength(); i++)
{
Node node = childList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE)
{
if (node.getLocalName().equals("stringelement"))
{
firstElementChild = (Element)node;
break;
}
}
}
if (firstElementChild == null) {return false;}
// Find the second child element and make sure it's
// <someelement>.
Node node = firstElementChild.getNextSibling();
do {
if (node.getNodeType() == Node.ELEMENT_NODE)
{
if (node.getLocalName().equals("someelement"))
{
secondElementChild = (Element)node;
break;
}
}
node = node.getNextSibling();
} while (node != null);
if (secondElementChild == null) {return false;}
// Create and insert a new <bar> element.
Element fooElement =
secondElementChild.getOwnerDocument().createElementNS("http://openuri.org","bar");
Node valueNode =
fooElement.getOwnerDocument().createTextNode("some text");
fooElement.appendChild(valueNode);
arrayOfAnyNode.insertBefore(fooElement, secondElementChild);
System.out.println("Output: <arrayofany> has a new <bar> child element.\n");
System.out.println(rootDoc + "\n");
return validateXml(rootDoc);
}
/**
* Edits incoming <root> XML to make the following changes: replace
* <somelement> with its <stringlist> child; add a new <foo>
* element as the second child of <arrayofany>.
*
* This method illustrates how you can use the selectPath method
* to find an element defined as xs:any in schema, then use
* XmlCursor instances to edit the XML.
*
* @param rootDoc An instance of the <root> XML document.
* @return <code>true</code> if the editing XML is valid;
* otherwise, <code>false</code>.
*/
public boolean editExistingDocWithSelectPath(RootDocument rootDoc)
{
String namespaceDecl = "declare namespace any='" +
m_namespaceUri + "'; ";
XmlCursor selectionCursor = rootDoc.getRoot().getArrayofany().newCursor();
// Save the cursor's position for later, then use XPath
// and cursor movement to position the cursor at
// the <stringlist> element.
selectionCursor.push();
selectionCursor.selectPath(namespaceDecl +
"$this//any:someelement/any:stringlist");
selectionCursor.toNextSelection();
// Create a new cursor and move it to the selection
// cursor's <someelement> parent. Moving the
// <stringlist> element to this position, displacing
// the <someelement> downward, then removing the
// <someelement> XML effectively replaces <someelement>
// with <stringlist>.
XmlCursor editCursor = selectionCursor.newCursor();
editCursor.toParent();
selectionCursor.moveXml(editCursor);
editCursor.removeXml();
editCursor.dispose();
// Return the cursor to the <arrayofany> element so you
// can do more editing. Then move the cursor to the second
// child and insert a new element as second child.
selectionCursor.pop();
selectionCursor.toFirstChild();
selectionCursor.toNextSibling();
selectionCursor.beginElement("foo", "http://openuri.org");
selectionCursor.insertChars("some text");
selectionCursor.dispose();
System.out.println("Output: <stringlist> has been promoted to replace \n" +
"<someelement>, and there's a new <foo> element.\n");
System.out.println(rootDoc + "\n");
return validateXml(rootDoc);
}
/**
* Like the code in the buildDocFromScratch method, this code
* uses the XmlCursor to build XML piece by piece, building
* out the Arrayofany instance it receives.
*
* @return A valid <arrayofany> element bound to an
* Arrrayofany instance.
*/
private Arrayofany buildArrayOfAny(Arrayofany arrayOfAny)
{
// Create a simple <stringelement> and move it into place
// under <arrayofany>.
StringelementDocument stringElementDoc =
StringelementDocument.Factory.newInstance();
stringElementDoc.setStringelement("some text");
XmlCursor childCursor = stringElementDoc.newCursor();
childCursor.toFirstContentToken();
// Add a cursor to mark the position at which the new child
// XML will be moved.
XmlCursor arrayCursor = arrayOfAny.newCursor();
arrayCursor.toNextToken();
childCursor.moveXml(arrayCursor);
childCursor.dispose();
// Create a <someelement> that contains a <stringlist>
// child element, then get the XmlObject representing the new
// <stringlist>. Note that the XmlCursor.beginElement method
// leaves the cursor between START and END tokens -- where
// content can be placed.
arrayCursor.beginElement("someelement", m_namespaceUri);
arrayCursor.beginElement("stringlist", m_namespaceUri);
arrayCursor.toPrevToken();
XmlObject stringList = arrayCursor.getObject();
// The cursor's no longer needed.
arrayCursor.dispose();
// Create the <stringlist> element's value and set it.
ListOfStrings stringListValue = buildListOfStrings();
if (stringListValue == null)
{
return null;
}
stringList.set(stringListValue);
// Validate the new XML.
if (!validateXml(arrayOfAny))
{
return null;
}
return arrayOfAny;
}
/**
* Creates an instance of the ListOfStrings complex type defined
* in the schema. The instance returned by this method can be
* inserted using either a set* operation or a cursor, as in
* {@link #buildArrayOfAny()}.
*
* @return A valid instance of ListOfStrings.
*/
private ListOfStrings buildListOfStrings()
{
// Create an instance of the ListOfStrings complex type.
ListOfStrings stringList = ListOfStrings.Factory.newInstance();
stringList.setId("001");
// Add two children for the instance's root.
XmlString stringElement = stringList.addNewStringelement();
stringElement.setStringValue("string1");
stringElement = stringList.addNewStringelement();
stringElement.setStringValue("string2");
// Validate the new XML.
if (!validateXml(stringList))
{
return null;
}
return stringList;
}
/**
* <p>Validates the XML, printing error messages when the XML is invalid. Note
* that this method will properly validate any instance of a compiled schema
* type because all of these types extend XmlObject.</p>
*
* <p>Note that in actual practice, you'll probably want to use an assertion
* when validating if you want to ensure that your code doesn't pass along
* invalid XML. This sample prints the generated XML whether or not it's
* valid so that you can see the result in both cases.</p>
*
* @param xml The XML to validate.
* @return <code>true</code> if the XML is valid; otherwise, <code>false</code>
*/
public static boolean validateXml(XmlObject xml)
{
boolean isXmlValid = false;
// A collection instance to hold validation error messages.
ArrayList validationMessages = new ArrayList();
// Validate the XML, collecting messages.
isXmlValid = xml.validate(
new XmlOptions().setErrorListener(validationMessages));
// If the XML isn't valid, print the messages.
if (!isXmlValid)
{
printErrors(validationMessages);
}
return isXmlValid;
}
/**
* Receives the collection containing errors found during
* validation and print the errors to the console.
*
* @param validationErrors The validation errors.
*/
public static void printErrors(ArrayList validationErrors)
{
Iterator iter = validationErrors.iterator();
while (iter.hasNext())
{
System.out.println(">> " + iter.next() + "\n");
}
}
/**
* <p>Creates a File from the XML path provided in main arguments, then
* parses the file's contents into a type generated from schema.</p>
* <p/>
* <p>Note that this work might have been done in main. Isolating it here
* makes the code separately available from outside this class.</p>
*
* @param xmlFilePath A path to XML based on the schema in inventory.xsd.
* @return An instance of a generated schema type that contains the parsed
* XML.
*/
public XmlObject parseXml(String xmlFilePath)
{
File xmlFile = new File(xmlFilePath);
XmlObject xml = null;
try
{
xml = XmlObject.Factory.parse(xmlFile);
} catch (XmlException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
}
return xml;
}
}