blob: e48963f0e70b9fb9d47ac71eb76f796d9d557ea7 [file] [log] [blame]
/*
* Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import jakarta.xml.bind.Binder;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.JAXBException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import binder.*;
/**
* Illustrate Binder use cases from JAXB 2.0 specification.
*/
public class UpdateablePartialBinding {
public static void main(String[] args) throws Exception {
File input = new File("po.xml");
File output = new File("processedpo.xml");
// create a JAXBContext capable of handling classes generated into
// the binder package
JAXBContext jc = JAXBContext.newInstance("binder");
// Load XML document into DOM
Document doc = loadDocument(input);
System.out.println("Using binder to perform xpath that returns JAXB objects");
xpathUsingBinder(jc.createBinder(), doc);
System.out.println("Completed jaxbXpath");
System.out.println();
System.out.println("Updateable partial binding allowing for schema evolution and preservations of comments in XML document");
updateablePartialBind(jc.createBinder(), doc);
saveDocument(doc, output);
System.out.println("Wrote updated DOM representation to file: " + output);
}
/**
* JAXB xpath implemented leveraging Binder and JAXP 1.3 XPath.
*
* KNOWN Limitations:
* xpath resolving to an XML attribute can not work since Binder.getJAXBNode() only works for Element, not Attribute.
*/
static public class JAXBXpath {
private final Document document;
private final Binder<Node> binder;
private Object jaxbRootObject;
public Document getDocument() {
return document;
}
public Binder getBinder() {
return binder;
}
public Object getJaxbRootObject() {
return jaxbRootObject;
}
public JAXBXpath(org.w3c.dom.Document doc, Binder<Node> b) throws JAXBException {
binder = b;
document = doc;
// bind entire DOM document to JAXB
jaxbRootObject = binder.unmarshal(document);
}
/**
* @param xpathExpr can match zero, one or more nodes in document.
* @return List of JAXB objects matching xpathExpr.
*/
private List<Object> evaluateToMany(String xpathExpr) {
List<Object> resultList = new ArrayList<Object>();
for( Node node : xpath(document, xpathExpr) ) {
resultList.add(binder.getJAXBNode(node));
}
return resultList;
}
/**
* Return JAXB object representing first match of <code>xpathExpr</code> over {@link getDocument()}
* @param xpathExpr should only match one node in document.
* @return JAXB object matching xpathExpr.
*/
private Object evaluate(String xpathExpr) {
List<Node> nodes = xpath(document, xpathExpr);
if(nodes.size()>0)
return binder.getJAXBNode(nodes.get(0));
else
return null;
}
}
static public void xpathUsingBinder(Binder<Node> binder, Document doc) throws JAXBException {
JAXBXpath jaxbXpath = new JAXBXpath(doc, binder);
//use xpath over DOM to find specific JAXB bound objects.
System.out.println("Ship Address found by xpath");
displayAddress((USAddress)
jaxbXpath.evaluate("/purchaseOrder/shipTo"));
System.out.println("Bill Address found by xpath");
displayAddress((USAddress)
jaxbXpath.evaluate("/purchaseOrder/billTo"));
System.out.println("items over $25 found by xpath");
for (Object item : jaxbXpath.evaluateToMany(".//item[USPrice>=25.00]")) {
displayItem((Items.Item) item);
}
}
public static void displayAddress(USAddress address) {
// display the address
System.out.println("\t" + address.getName());
System.out.println("\t" + address.getStreet());
System.out.printf("\t%1s, %2s %3s\n",
address.getCity(), address.getState(), address.getZip());
System.out.println("\t" + address.getCountry() + "\n");
}
public static void displayItem(Items.Item item) {
String result =
"\t" + item.getQuantity() +
" copies of \"" + item.getProductName() +
"\"" + " price= " + item.getUSPrice().toString();
if (item.getComment() != null) {
result += " comment: " + item.getComment();
}
System.out.println(result);
}
static public void updateablePartialBind(Binder<Node> binder, Document doc) throws JAXBException {
// Partially bind XML document to Java.
// Only Nodes matching XPath are bound to JAXB objects.
// Binder retains association between DOM and JAXB views.
for( Node node : xpath(doc, "//item[USPrice>=25.00]") ) {
// Unmarshal by declared type since element Item corresponds with a local element declaration in schema.
// Partial Bind DOM Element"item" to instance of JAXB mapped binder.Items.Item class
JAXBElement<Items.Item> itemE = binder.unmarshal(node, Items.Item.class);
binder.Items.Item item = itemE.getValue();
// Modify in Java
item.setComment("qualifies for free shipping");
// Sync changes made in Java back to XML document
binder.updateXML(item);
}
// Add $2 shipping per item under $25.
for( Node node : xpath(doc, "//item[USPrice<25.00]") ) {
// Unmarshal by declared type since element Item corresponds with a local element declaration in schema.
// Partial Bind DOM Element"item" to instance of JAXB mapped binder.Items.Item class
JAXBElement<Items.Item> itemE =
binder.unmarshal(node, Items.Item.class);
binder.Items.Item item = itemE.getValue();
// Modify in Java
item.setUSPrice(item.getUSPrice().add(new BigDecimal("2.00")));
// Sync changes made in Java back to XML document
binder.updateXML(item);
}
}
static Document loadDocument(File input) throws IOException, SAXException {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
return dbf.newDocumentBuilder().parse(input);
} catch (ParserConfigurationException e) {
throw new Error(e); // impossible
}
}
static public void saveDocument(Document document, File output) throws TransformerException, IOException {
Transformer t = TransformerFactory.newInstance().newTransformer();
t.transform(new DOMSource(document),new StreamResult(new FileOutputStream(output)));
}
static List<Node> xpath(Document document, String xpathExpression) {
// create XPath
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
try {
List<Node> result = new ArrayList<Node>();
NodeList nl = (NodeList) xpath.evaluate(xpathExpression, document, XPathConstants.NODESET);
for( int i=0; i<nl.getLength(); i++ )
result.add(nl.item(i));
return result;
} catch (XPathExpressionException e) {
e.printStackTrace();
return Collections.emptyList();
}
}
}