source: trunk/docx4j/src/main/java/org/docx4j/utils/XPathAwareCloner.java @ 1752

Revision 1752, 5.8 KB checked in by jharrop, 4 months ago (diff)

Without generics, since this returned a spurious 11th result when tested on w:t

Line 
1package org.docx4j.utils;
2
3import java.util.List;
4
5import javax.xml.bind.Binder;
6import javax.xml.bind.JAXBContext;
7import javax.xml.bind.JAXBElement;
8import javax.xml.bind.JAXBException;
9import javax.xml.namespace.QName;
10
11import org.apache.log4j.Logger;
12import org.docx4j.XmlUtils;
13import org.docx4j.jaxb.Context;
14import org.docx4j.jaxb.JaxbValidationEventHandler;
15import org.docx4j.wml.P;
16import org.w3c.dom.Node;
17
18/**
19 * Some users wish to be able to use an XPath on the result
20 * of cloning a JAXB object.  This variant on XmlUtils.deepCopy
21 * allows that.
22 *
23 * Note that if you use this object's deepCopy method more
24 * than once, the results returned by getJAXBNodesViaXPath
25 * will only be on your last deepCopy.
26 */
27public class XPathAwareCloner {
28       
29        private static Logger log = Logger.getLogger(XPathAwareCloner.class);   
30                       
31        /** Clone this JAXB object, using default JAXBContext. */ 
32        public Object deepCopy(Object o) {             
33                return deepCopy(o, Context.jc);         
34        }       
35       
36        Object jaxbElement;
37       
38        /** Clone this JAXB object
39         * @param value
40         * @param jc
41         * @return
42         */
43        public Object deepCopy(Object o, JAXBContext jc) {
44               
45                if (o==null) {
46                        throw new IllegalArgumentException("Can't clone a null argument");
47                }
48               
49                try {
50                        // To be XPath aware, we need a binder.
51                        // But to unmarshall using a binder, we need to unmarshal a node.
52                        // So, our marshall should be to a W3C document
53                        org.w3c.dom.Document doc = XmlUtils.marshaltoW3CDomDocument(o, jc);
54                       
55                        // OK, unmarshall to binder
56                        binder = jc.createBinder();
57                        JaxbValidationEventHandler eventHandler = new JaxbValidationEventHandler();
58                        eventHandler.setContinue(false);
59                        binder.setEventHandler(eventHandler);
60                        jaxbElement =  binder.unmarshal( doc);
61                       
62                        //log.debug("Clone: " + XmlUtils.marshaltoString(jaxbElement, true, true));
63                       
64                        return jaxbElement;
65                } catch (JAXBException ex) {
66                        throw new IllegalArgumentException(ex);
67                }
68        }
69       
70
71        private Binder<Node> binder;
72       
73       
74        /**
75         * Enables synchronization between XML infoset nodes and JAXB objects
76         * representing same XML document.
77         *
78         * An instance of this class maintains the association between XML nodes
79         * of an infoset preserving view and a JAXB representation of an XML document.
80         * Navigation between the two views is provided by the methods
81         * getXMLNode(Object) and getJAXBNode(Object) .
82         *
83         * In theory, modifications can be made to either the infoset preserving view or
84         * the JAXB representation of the document while the other view remains
85         * unmodified. The binder ought to be able to synchronize the changes made in
86         * the modified view back into the other view using the appropriate
87         * Binder update methods, #updateXML(Object, Object) or #updateJAXB(Object).
88         *
89         * But JAXB doesn't currently work as advertised .. access to this
90         * object is offered for advanced users on an experimental basis only.
91         */
92        public Binder<Node> getBinder() {
93               
94                return binder;
95        }
96       
97        /**
98         * Fetch JAXB Nodes matching an XPath (for example "//w:p").
99         *
100         * If you have modified your JAXB objects (eg added or changed a
101         * w:p paragraph), you need to update the association. The problem
102         * is that this can only be done ONCE, owing to a bug in JAXB:
103         * see https://jaxb.dev.java.net/issues/show_bug.cgi?id=459
104         *
105         * So this is left for you to choose to do via the refreshXmlFirst parameter.   
106         *
107         * @param xpathExpr
108         * @param refreshXmlFirst
109         * @return
110         * @throws JAXBException
111         */     
112        public List<Object> getJAXBNodesViaXPath(String xpathExpr, boolean refreshXmlFirst) 
113                        throws JAXBException {
114               
115                return XmlUtils.getJAXBNodesViaXPath(binder, jaxbElement, xpathExpr, refreshXmlFirst);
116        }       
117       
118        /**
119         * @param args
120         * @throws JAXBException
121         */
122        public static void main(String[] args) throws JAXBException {
123               
124            String pString = "<w:p xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\">"
125                      +"<w:r>"
126                        +"<w:t xml:space=\"preserve\">Here is some text.</w:t>"
127                      +"</w:r>"
128                      +"<w:r>"
129                        +"<w:rPr>"
130                          +"<w:i/>"
131                        +"</w:rPr>"
132                        +"<w:t>An italic run.</w:t>"
133                      +"</w:r>"
134                      +"<w:r>"
135                        +"<w:rPr>"
136                          +"<w:i/>"
137                        +"</w:rPr>"
138                        +"<w:t xml:space=\"preserve\">"  +"</w:t>"
139                      +"</w:r>"
140                      +"<w:r>"
141                        +"<w:t>More stuff.</w:t>"
142                      +"</w:r>"
143                      +"<w:r>"
144                        +"<w:t xml:space=\"preserve\">"  +"</w:t>"
145                      +"</w:r>"
146                      +"<w:r>"
147                        +"<w:rPr>"
148                          +"<w:b/>"
149                        +"</w:rPr>"
150                        +"<w:t>More stuff.</w:t>"
151                      +"</w:r>"
152                      +"<w:r>"
153                        +"<w:t xml:space=\"preserve\">" +"</w:t>"
154                      +"</w:r>"
155                      +"<w:r>"
156                        +"<w:t xml:space=\"preserve\">The run we are seeking.</w:t>"
157                      +"</w:r>"
158                      +"<w:r>"
159                        +"<w:rPr>"
160                          +"<w:b/>"
161                        +"</w:rPr>"
162                        +"<w:t>More stuff.</w:t>"
163                      +"</w:r>"
164                      +"<w:r>"
165                        +"<w:t xml:space=\"preserve\">" +"</w:t>"
166                      +"</w:r>"
167                      +"<w:r>"
168                        +"<w:t>More stuff.</w:t>"
169                      +"</w:r>"
170                    +"</w:p>";
171           
172            P pIn = (P)XmlUtils.unmarshalString(pString);
173           
174            XPathAwareCloner cloner = new XPathAwareCloner();
175            P clonedP = (P)cloner.deepCopy(pIn);
176           
177            List<Object> results = cloner.getJAXBNodesViaXPath("//w:r[contains( w:t, 'seeking')]", false);
178            //List<Object> results = cloner.getJAXBNodesViaXPath("//w:r", false);
179           
180            int i=1;
181            for (Object result: results) {
182                System.out.println("\n\r" + i + ": " + XmlUtils.marshaltoString(result, true, true));
183                i++;
184            }
185               
186
187        }
188
189}
Note: See TracBrowser for help on using the repository browser.