| /* |
| * 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(); |
| } |
| |
| } |
| } |