Source code: com/arranger/jarl/util/XMLUtil.java
1 package com.arranger.jarl.util;
2
3
4 import org.apache.xpath.XPathAPI;
5 import org.apache.xml.serialize.XMLSerializer;
6 import org.apache.xml.serialize.OutputFormat;
7 import org.w3c.dom.*;
8 import org.w3c.dom.traversal.NodeIterator;
9 import org.xml.sax.*;
10
11 import javax.xml.parsers.DocumentBuilder;
12 import javax.xml.parsers.DocumentBuilderFactory;
13 import javax.xml.transform.TransformerException;
14 import java.io.*;
15 import java.util.Stack;
16 import java.util.regex.Pattern;
17 import java.util.regex.Matcher;
18
19 /**
20 * XMLUtil created on Jan 18, 2003
21 */
22 public class XMLUtil {
23
24 public static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
25 public static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
26
27 protected static DocumentBuilderFactory s_docBuilderFactory = null;
28 protected static boolean s_useValidation = true;
29
30 public static DocumentBuilderFactory getDocBuilderFactory() {
31 return s_docBuilderFactory;
32 }
33
34 public static void setDocBuilderFactory(DocumentBuilderFactory docBuilderFactory) {
35 s_docBuilderFactory = docBuilderFactory;
36 }
37
38 public static boolean isUseValidation() {
39 return s_useValidation;
40 }
41
42 public static void setUseValidation(boolean useValidation) {
43 s_useValidation = useValidation;
44 }
45
46 public static Document loadDocument(String location) throws Exception {
47 InputStream inputStream = IOUtil.getInputStream(location);
48 Document doc = loadDocument(inputStream);
49 inputStream.close();
50 return doc;
51 }
52
53 public static Document loadDocument(String location, boolean doInclude) throws Exception {
54 if (!doInclude) {
55 return loadDocument(location);
56 } else {
57 String text = preprocesXML(new File(location), new Stack());
58 return loadDocumentFromXML(text);
59 }
60 }
61
62 /**
63 * returns a node from the suplied node that meets the xpath criteria.
64 * @param node is the tree starting point.
65 * @param xpath is a valid xpath statement.
66 */
67 public static Node selectSingleNode(Node node, String xpath) {
68 try {
69 return XPathAPI.selectSingleNode(node, xpath);
70 } catch (TransformerException te) {
71 Debug.warn(te.getMessage());
72 }
73
74 return null;
75 }
76
77 /**
78 * returns a node from the suplied node that meets the xpath criteria.
79 * @param node is the tree starting point.
80 * @param xpath is a valid xpath statement.
81 */
82 public static Element selectElement(Node node, String xpath) {
83 try {
84 return (Element)XPathAPI.selectSingleNode(node, xpath);
85 } catch (TransformerException te) {
86 Debug.warn(te.getMessage());
87 }
88
89 return null;
90 }
91
92
93 /**
94 * returns a node list from the suplied node that meets the xpath criteria.
95 * @param node is the tree starting point.
96 * @param xpath is a valid xpath statement.
97 */
98 public static NodeList selectNodeList(Node node, String xpath) {
99 try {
100 return XPathAPI.selectNodeList(node, xpath);
101 } catch (TransformerException te) {
102 Debug.warn(te.getMessage());
103 }
104 return null;
105 }
106
107 /**
108 * returns a node iterator from the suplied node that meets the xpath criteria.
109 * @param node is the tree starting point.
110 * @param xpath is a valid xpath statement.
111 */
112 public static NodeIterator selectNodeIterator(Node node, String xpath) {
113 try {
114 return XPathAPI.selectNodeIterator(node, xpath);
115 } catch (TransformerException te) {
116 Debug.warn(te.getMessage());
117 }
118
119 return null;
120 }
121
122 public static Document loadDocument(InputStream inputStream) throws Exception {
123 return _loadDocument(new InputSource(inputStream));
124 }
125
126 public static Document loadDocument(Reader reader) throws Exception {
127 return _loadDocument(new InputSource(reader));
128 }
129
130 public static Document loadDocumentFromXML(String xml) throws Exception {
131 return loadDocument(new StringReader(xml));
132 }
133
134 public static Document newDocument() {
135 try {
136 return getBuilderFactory().newDocumentBuilder().newDocument();
137 } catch (Exception e) {
138 Debug.warn(e.getMessage());
139 }
140
141 return null;
142 }
143
144 /**
145 * Creates and returns document builder
146 */
147 public static DocumentBuilder newDocumentBuilder() {
148 try {
149 return getBuilderFactory().newDocumentBuilder();
150 } catch (Exception e) {
151 Debug.warn(e.getMessage());
152 }
153 return null;
154 }
155
156 public static DocumentBuilderFactory getBuilderFactory() throws Exception {
157 if (s_docBuilderFactory == null) {
158 s_docBuilderFactory = DocumentBuilderFactory.newInstance();
159 s_docBuilderFactory.setNamespaceAware(true);
160 s_docBuilderFactory.setIgnoringElementContentWhitespace(true);
161 }
162 return s_docBuilderFactory;
163 }
164
165 /**
166 * Get the text value for the given node.
167 *
168 * @param node the node to examine
169 * @return the text value of the node, or null if not available
170 */
171 public static String getNodeText(Node node) {
172 if (node == null) {
173 return null;
174 }
175 String textValue = null;
176 //if we are already a text node, just get the value
177 if (node instanceof Text)
178 textValue = node.getNodeValue();
179 else {
180 Node child = node.getFirstChild();
181 if (child instanceof Text) {
182 Text textNode = (Text)child;
183 textValue = textNode.getNodeValue();
184 }
185
186 if (child instanceof CharacterData) {
187 CharacterData cdataNode = (CharacterData)child;
188 textValue = cdataNode.getNodeValue();
189 }
190 }
191 return textValue;
192 }
193
194 protected static Document _loadDocument(InputSource inputSource) throws Exception {
195 DocumentBuilderFactory documentBuilderFactory = getBuilderFactory();
196 if (s_useValidation) {
197 documentBuilderFactory.setValidating(true);
198 documentBuilderFactory.setNamespaceAware(true);
199 documentBuilderFactory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
200 } else {
201 documentBuilderFactory.setValidating(false);
202 documentBuilderFactory.setNamespaceAware(false);
203 }
204
205 DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
206 if (s_useValidation) {
207 CustomErrorHandler customErrorHandler = new CustomErrorHandler();
208 builder.setErrorHandler(customErrorHandler);
209 builder.setEntityResolver(new CustomEntityResolver());
210 Document document = builder.parse(inputSource);
211 if (customErrorHandler.m_numErrors > 0) {
212 throw new Exception(customErrorHandler.m_buffer.toString());
213 }
214 return document;
215
216 } else {
217 return builder.parse(inputSource);
218 }
219 }
220
221 /**
222 * Serializes document node and returns its content as a string
223 */
224 public static String toString(Node node) {
225 return toString(node, false);
226 }
227
228 /**
229 * Serializes document node and returns its content as a string
230 * @param node the xml DOM node
231 * @param minimizeXMLSize if true then omit comments, identing, etc to minimize overall xml size
232 */
233 public static String toString(Node node, boolean minimizeXMLSize) {
234 Element element = (Element)node;
235 try {
236 StringWriter out = new StringWriter();
237 getXMLSerializer(out, minimizeXMLSize).serialize(element);
238 return out.toString();
239 } catch (Exception e) {
240 Debug.warn(e.getMessage());
241 return null;
242 }
243 }
244
245 private static XMLSerializer getXMLSerializer(Writer out, boolean minimizeXMLSize) {
246 OutputFormat xmlFormat = new OutputFormat("text/xml", "UTF-8", false);
247 xmlFormat.setNonEscapingElements(new String[]{"script"});
248 xmlFormat.setOmitXMLDeclaration(true);
249 if (minimizeXMLSize) {
250 xmlFormat.setPreserveSpace(false);
251 xmlFormat.setOmitComments(true);
252 xmlFormat.setIndenting(false);
253 } else {
254 xmlFormat.setPreserveSpace(true);
255 // xmlFormat.setIndenting(true);
256 // xmlFormat.setIndent(3);
257 }
258 return new XMLSerializer(out, xmlFormat);
259 }
260
261 public static String preprocesXML(File file, Stack stack) throws Exception {
262 if (stack.contains(file)) {
263 throw new Exception("Recursive include of a file");
264 }
265 stack.push(file);
266
267 String text = IOUtil.toString(file.getAbsolutePath());
268
269 //look for <xml:include file='
270 Pattern pattern = Pattern.compile("<xml:include\\s+file=.+>");
271 Matcher matcher = pattern.matcher(text);
272
273 StringBuffer buffer = new StringBuffer();
274 while (matcher.find()) {
275 String includeDirective = matcher.group();
276 String replacementText = replaceText(includeDirective, stack);
277 matcher.appendReplacement(buffer, replacementText);
278 }
279 matcher.appendTail(buffer);
280 stack.pop();
281 return buffer.toString();
282 }
283
284 protected static String replaceText(String includeDirective, Stack stack) throws Exception {
285 Pattern pattern = Pattern.compile("\\w*/*[a-zA-Z]+\\.xml|\\w*/*[a-zA-Z]+\\.jarlml");
286 Matcher matcher = pattern.matcher(includeDirective);
287 if (matcher.find()) {
288 String includedFile = matcher.group();
289
290 if (!(includedFile.startsWith("/") || ":".equals(includedFile.substring(1, 2)))) {
291 //fix up the included file
292 File parentFile = (File)stack.peek();
293 includedFile = StringTools.appendPath(parentFile.getParent(), includedFile);
294 }
295
296 return preprocesXML(new File(includedFile), stack);
297 } else {
298 return null;
299 }
300 }
301
302 protected static class CustomErrorHandler implements ErrorHandler {
303
304 protected int m_numErrors = 0;
305 protected StringBuffer m_buffer = new StringBuffer();
306
307 protected void report(SAXParseException exception) {
308 m_buffer.append(exception.getSystemId() + " " +
309 exception.getLineNumber() + ":" +
310 exception.getColumnNumber() + " --> " +
311 exception.getMessage() + "\r\n");
312 m_numErrors++;
313 }
314
315 public void warning(SAXParseException exception)
316 throws SAXException {
317 report(exception);
318 }
319
320 public void error(SAXParseException exception)
321 throws SAXException {
322 report(exception);
323 }
324
325 public void fatalError(SAXParseException exception)
326 throws SAXException {
327 report(exception);
328 }
329 }
330
331 protected static class CustomEntityResolver implements EntityResolver {
332
333 public InputSource resolveEntity(String publicId, String systemId)
334 throws SAXException, IOException {
335 InputStream is = getClass().getResourceAsStream("/com/arranger/jarl/schema/jarl.xsd");
336 ByteArrayOutputStream baos = new ByteArrayOutputStream();
337 IOUtil.copyStream(is, baos);
338 is.close();
339 InputSource inputSource = new InputSource(new ByteArrayInputStream(baos.toByteArray()));
340 inputSource.setSystemId("jarl.xsd");
341 inputSource.setPublicId("jarl.xsd");
342 return inputSource;
343 }
344 }
345 }
346