source: trunk/docx4j/src/main/java/org/docx4j/openpackaging/parts/XmlPart.java @ 1555

Revision 1555, 6.7 KB checked in by jharrop, 11 months ago (diff)

Apply Dave Brown's "assorted patches" of 28 April, as described at
 http://dev.plutext.org/forums/docx-java-f6/assorted-patches-t712.html

Line 
1/*
2 *  Copyright 2007-2008, Plutext Pty Ltd.
3 *   
4 *  This file is part of docx4j.
5
6    docx4j is licensed under the Apache License, Version 2.0 (the "License");
7    you may not use this file except in compliance with the License.
8
9    You may obtain a copy of the License at
10
11        http://www.apache.org/licenses/LICENSE-2.0
12
13    Unless required by applicable law or agreed to in writing, software
14    distributed under the License is distributed on an "AS IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18
19 */
20
21package org.docx4j.openpackaging.parts;
22
23
24import java.io.ByteArrayOutputStream;
25import java.io.InputStream;
26import java.util.HashMap;
27import java.util.Iterator;
28import java.util.List;
29import java.util.Map;
30
31import javax.xml.XMLConstants;
32import javax.xml.bind.JAXBException;
33import javax.xml.namespace.NamespaceContext;
34import javax.xml.parsers.DocumentBuilder;
35import javax.xml.parsers.DocumentBuilderFactory;
36import javax.xml.parsers.ParserConfigurationException;
37import javax.xml.xpath.XPath;
38import javax.xml.xpath.XPathConstants;
39import javax.xml.xpath.XPathFactory;
40
41import org.apache.commons.lang.text.StrTokenizer;
42import org.docx4j.XmlUtils;
43import org.docx4j.jaxb.NamespacePrefixMappings;
44import org.docx4j.openpackaging.exceptions.Docx4JException;
45import org.docx4j.openpackaging.exceptions.InvalidFormatException;
46import org.w3c.dom.Document;
47import org.w3c.dom.Node;
48import org.w3c.dom.NodeList;
49import org.w3c.dom.Text;
50
51/** OPC Parts are either XML, or binary (or text) documents.
52 *
53 *  Most are XML documents.
54 * 
55 *  docx4j aims to represent XML parts using JAXB.  However,
56 *  at present there are some parts for which we don't have
57 *  JAXB representations.
58 * 
59 *  Until such time as a JAXB representation for an XML Part exists,
60 *  the Part should extend this class.   
61 * 
62 * */
63public abstract class XmlPart extends Part {
64       
65        public XmlPart(PartName partName) throws InvalidFormatException {
66                super(partName);
67        }
68
69        public XmlPart() throws InvalidFormatException {
70                super();
71        }
72
73        /**
74         * This part's XML contents.  Not guaranteed to be up to date.
75         * Whether it is or not will depend on how the class which extends
76         * Part chooses to treat it.  It may be that the class uses some
77         * other internal representation for its data.
78         */
79        protected Document doc;
80        private static XPathFactory xPathFactory;
81        private static XPath xPath;
82
83        private static DocumentBuilderFactory documentFactory;
84
85       
86        static {
87               
88                // Crimson doesn't support setTextContent; this.writeDocument also fails.
89                // We've already worked around the problem with setTextContent,
90                // but rather than do the same for writeDocument,
91                // let's just stop using it.
92               
93                try {
94                        // docx4j is not dependent on Xerces (other than here),
95                        // but Websphere (presumably using IBM JDK) doesn't have
96                        // Sun's Xerces implementation, so use real Xerces if it is
97                        // on the class path
98                        System.setProperty("javax.xml.parsers.DocumentBuilderFactory",
99                                "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
100                        documentFactory = DocumentBuilderFactory.newInstance();
101                } catch (javax.xml.parsers.FactoryConfigurationError fce) {
102                        System.setProperty("javax.xml.parsers.DocumentBuilderFactory",
103                                "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
104                        documentFactory = DocumentBuilderFactory.newInstance();
105                }
106               
107                xPathFactory = XPathFactory.newInstance();
108                xPath = xPathFactory.newXPath();               
109               
110                documentFactory.setNamespaceAware(true);
111
112        }
113       
114       
115        private NamespacePrefixMappings nsContext;
116        private NamespacePrefixMappings getNamespaceContext() {
117                if (nsContext==null) {
118                        nsContext = new NamespacePrefixMappings();
119                        xPath.setNamespaceContext(nsContext);
120                }
121                return nsContext;
122        }
123
124        public void setDocument(InputStream is) throws Docx4JException {
125                try {
126            DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder(); // DocumentBuilder is not thread safe, so it needs to be local
127            doc = documentBuilder.parse(is);
128                } catch (Exception e) {
129                        throw new Docx4JException("Problems parsing InputStream for part " + this.partName.getName(), e);
130                } 
131        }       
132
133        public void setDocument( org.w3c.dom.Document doc ) {
134                this.doc = doc;
135        }
136       
137        public abstract Document getDocument() throws Docx4JException;
138       
139        public String xpathGetString(String xpathString, String prefixMappings)  throws Docx4JException {
140                try {
141                        getNamespaceContext().registerPrefixMappings(prefixMappings);
142                       
143                        String result = xPath.evaluate(xpathString, doc );
144                        log.debug(xpathString + " ---> " + result);
145                        return result;
146                } catch (Exception e) {
147                        throw new Docx4JException("Problems evaluating xpath '" + xpathString + "'", e);
148                }
149        }
150       
151        public List<Node> xpathGetNodes(String xpathString, String prefixMappings) {
152               
153                getNamespaceContext().registerPrefixMappings(prefixMappings);
154               
155                return XmlUtils.xpath(doc, xpathString, 
156                                getNamespaceContext() );
157               
158        }
159       
160       
161        /**
162         * Set the value of the node referenced in the xpath expression.
163         *
164         * @param xpath
165         * @param value
166         * @param prefixMappings a string such as "xmlns:ns0='http://schemas.medchart'"
167         * @return
168         * @throws Docx4JException
169         */
170        public boolean setNodeValueAtXPath(String xpath, String value, String prefixMappings) throws Docx4JException {
171
172                try {
173                        getNamespaceContext().registerPrefixMappings(prefixMappings);
174
175                        Node n = (Node)xPath.evaluate(xpath, doc, XPathConstants.NODE );
176                        if (n==null) {
177                                log.debug("xpath returned null");
178                                return false;
179                        }
180                        log.debug(n.getClass().getName());
181                       
182                        // Method 1: Crimson throws error
183                        // Could avoid with System.setProperty("javax.xml.parsers.DocumentBuilderFactory",
184                        //              "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
185                        //n.setTextContent(value);
186                       
187                        // Method 2: crimson ignores
188                        // n.setNodeValue(value);
189
190                        // Method 3: createTextNode, then append it
191                        // First, need to delete/replace existing text node
192                        if (n.getChildNodes() !=null
193                                        && n.getChildNodes().getLength() > 0) {
194                                NodeList nodes = n.getChildNodes();
195                                for (int i = nodes.getLength(); i>0; i--) {
196                                        n.removeChild( nodes.item(i-1));
197                                }
198                        }
199                        Text t = n.getOwnerDocument().createTextNode(value);
200                        n.appendChild(t);                       
201                       
202                        // cache is now invalid
203                        return true;
204                } catch (Exception e) {
205                        throw new Docx4JException("Problem setting value at xpath " + xpath);
206                } 
207               
208        }
209       
210    public boolean isContentEqual(Part other) throws Docx4JException {
211
212        if (!(other instanceof XmlPart))
213                return false;
214       
215        Document doc1 = getDocument();
216        Document doc2 = ((XmlPart)other).getDocument();
217       
218        return doc1.isEqualNode(doc2);
219
220    }
221               
222}
Note: See TracBrowser for help on using the repository browser.