1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sourceforge.jeuclid.parser;
20
21 import java.io.BufferedInputStream;
22 import java.io.FilterInputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.Reader;
26 import java.lang.ref.Reference;
27 import java.lang.ref.SoftReference;
28 import java.util.Map;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.zip.ZipEntry;
31 import java.util.zip.ZipInputStream;
32
33 import javax.annotation.concurrent.ThreadSafe;
34 import javax.xml.parsers.DocumentBuilder;
35 import javax.xml.parsers.DocumentBuilderFactory;
36 import javax.xml.parsers.ParserConfigurationException;
37 import javax.xml.transform.Source;
38 import javax.xml.transform.Transformer;
39 import javax.xml.transform.TransformerException;
40 import javax.xml.transform.TransformerFactory;
41 import javax.xml.transform.dom.DOMResult;
42 import javax.xml.transform.dom.DOMSource;
43 import javax.xml.transform.stream.StreamSource;
44
45 import net.sourceforge.jeuclid.ResourceEntityResolver;
46
47 import org.apache.commons.logging.Log;
48 import org.apache.commons.logging.LogFactory;
49 import org.apache.xmlgraphics.image.loader.ImageSource;
50 import org.w3c.dom.Document;
51 import org.w3c.dom.Node;
52 import org.xml.sax.ErrorHandler;
53 import org.xml.sax.InputSource;
54 import org.xml.sax.SAXException;
55 import org.xml.sax.SAXParseException;
56
57
58
59
60
61
62
63
64 @ThreadSafe
65 public final class Parser {
66
67
68 private static final class LoggerErrorHandler implements ErrorHandler {
69 public LoggerErrorHandler() {
70
71 }
72
73 public void error(final SAXParseException exception)
74 throws SAXException {
75 Parser.LOGGER.warn(exception);
76 }
77
78 public void fatalError(final SAXParseException exception)
79 throws SAXException {
80 throw exception;
81 }
82
83 public void warning(final SAXParseException exception)
84 throws SAXException {
85 Parser.LOGGER.debug(exception);
86 }
87 }
88
89 private static final class UnclosableInputStream extends FilterInputStream {
90 protected UnclosableInputStream(final InputStream in) {
91 super(in);
92 }
93
94 @Override
95 public void close() throws IOException {
96
97 }
98 }
99
100
101
102
103
104 private static final int DETECTION_BUFFER_SIZE = 128;
105
106 private static final String BAD_STREAM_SOURCE = "Bad StreamSource: ";
107
108 private static final String CONTENT_XML = "content.xml";
109
110 private static final String CANNOT_HANDLE_SOURCE = "Cannot handle Source: ";
111
112 private static final class SingletonHolder {
113 private static final Parser INSTANCE = new Parser();
114
115 private SingletonHolder() {
116 }
117 }
118
119
120
121
122 private static final Log LOGGER = LogFactory.getLog(Parser.class);
123
124 private final Map<Long, Reference<DocumentBuilder>> builders;
125
126
127
128
129 protected Parser() {
130 this.builders = new ConcurrentHashMap<Long, Reference<DocumentBuilder>>();
131 }
132
133 private DocumentBuilder createDocumentBuilder() {
134 DocumentBuilder documentBuilder;
135 try {
136 try {
137 documentBuilder = this.tryCreateDocumentBuilder(true);
138 } catch (final UnsupportedOperationException uoe) {
139 Parser.LOGGER.debug("Unsupported Operation: "
140 + uoe.getMessage());
141 documentBuilder = this.tryCreateDocumentBuilder(false);
142 } catch (final ParserConfigurationException pce) {
143 Parser.LOGGER.debug("ParserConfigurationException: "
144 + pce.getMessage());
145 documentBuilder = this.tryCreateDocumentBuilder(false);
146 }
147 documentBuilder.setEntityResolver(new ResourceEntityResolver());
148 documentBuilder.setErrorHandler(new LoggerErrorHandler());
149 } catch (final ParserConfigurationException pce2) {
150 Parser.LOGGER.warn("Could not create Parser: " + pce2.getMessage());
151 assert false : "Could not create Parser";
152 documentBuilder = null;
153 }
154 return documentBuilder;
155 }
156
157 private DocumentBuilder tryCreateDocumentBuilder(final boolean xinclude)
158 throws ParserConfigurationException {
159 final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
160 .newInstance();
161 documentBuilderFactory.setNamespaceAware(true);
162 if (xinclude) {
163 documentBuilderFactory.setXIncludeAware(true);
164 }
165 final DocumentBuilder documentBuilder = documentBuilderFactory
166 .newDocumentBuilder();
167 return documentBuilder;
168 }
169
170
171
172
173
174
175 public static Parser getInstance() {
176 return Parser.SingletonHolder.INSTANCE;
177 }
178
179
180
181
182
183
184
185
186
187 @Deprecated
188 public static Parser getParser() throws ParserConfigurationException {
189 return Parser.getInstance();
190 }
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206 public Document parseStreamSource(final StreamSource streamSource)
207 throws SAXException, IOException {
208 Document retVal = null;
209 InputStream inputStream = streamSource.getInputStream();
210 if (inputStream != null) {
211
212
213 if (!inputStream.markSupported()) {
214 inputStream = new BufferedInputStream(inputStream);
215 }
216 final InputStream filterInput = new UnclosableInputStream(
217 inputStream);
218 filterInput.mark(Parser.DETECTION_BUFFER_SIZE);
219 try {
220 retVal = this.parseStreamSourceAsXml(new StreamSource(
221 filterInput));
222 inputStream.close();
223 } catch (final SAXParseException se) {
224 filterInput.reset();
225 try {
226 retVal = this.parseStreamSourceAsOdf(new StreamSource(
227 filterInput));
228 } catch (final IOException io) {
229 throw se;
230 }
231 inputStream.close();
232 }
233
234
235
236
237
238
239
240
241
242
243
244
245 }
246 if (retVal == null) {
247 retVal = this.parseStreamSourceAsXml(streamSource);
248 }
249 return retVal;
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263 public Document parseStreamSourceAsOdf(final StreamSource streamSource)
264 throws IOException, SAXException {
265 final InputStream is = streamSource.getInputStream();
266 if (is == null) {
267 throw new IllegalArgumentException(Parser.BAD_STREAM_SOURCE
268 + streamSource);
269 }
270 final ZipInputStream zipStream = new ZipInputStream(is);
271 Document document = null;
272 ZipEntry entry = zipStream.getNextEntry();
273 while (entry != null) {
274 if (Parser.CONTENT_XML.equals(entry.getName())) {
275 document = this.getDocumentBuilder().parse(zipStream);
276 entry = null;
277 } else {
278 entry = zipStream.getNextEntry();
279 }
280 }
281 return document;
282 }
283
284
285
286
287
288
289
290
291
292
293
294
295 public Document parseStreamSourceAsXml(final StreamSource streamSource)
296 throws SAXException, IOException {
297 InputSource inp = null;
298 final String systemId = streamSource.getSystemId();
299 if (systemId != null) {
300 inp = new InputSource(systemId);
301 }
302 final InputStream is = streamSource.getInputStream();
303 if ((inp == null) && (is != null)) {
304 inp = new InputSource(is);
305 }
306 final Reader ir = streamSource.getReader();
307 if ((inp == null) && (ir != null)) {
308 inp = new InputSource(ir);
309 }
310
311 if (inp == null) {
312 throw new IllegalArgumentException(Parser.BAD_STREAM_SOURCE
313 + streamSource);
314 }
315
316 return this.getDocumentBuilder().parse(inp);
317 }
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332 public DocumentBuilder getDocumentBuilder() {
333
334
335 final long id = Thread.currentThread().getId();
336 final Reference<DocumentBuilder> builderRef = this.builders.get(id);
337 if (builderRef != null) {
338 final DocumentBuilder builder = builderRef.get();
339 if (builder != null) {
340 return builder;
341 }
342 }
343 final DocumentBuilder builder = this.createDocumentBuilder();
344 this.builders.put(id, new SoftReference<DocumentBuilder>(builder));
345 return builder;
346 }
347
348
349
350
351
352
353
354
355
356
357
358
359
360 public Node parse(final Source source) throws SAXException, IOException {
361 final Node retVal;
362 if (source instanceof StreamSource) {
363 final StreamSource streamSource = (StreamSource) source;
364 retVal = this.parseStreamSource(streamSource);
365 } else if (source instanceof ImageSource) {
366 final ImageSource imageSource = (ImageSource) source;
367 final StreamSource streamSource = new StreamSource(imageSource
368 .getInputStream());
369 retVal = this.parseStreamSource(streamSource);
370 } else if (source instanceof DOMSource) {
371 final DOMSource domSource = (DOMSource) source;
372 retVal = domSource.getNode();
373 } else {
374 try {
375 final Transformer t = TransformerFactory.newInstance()
376 .newTransformer();
377 final DOMResult r = new DOMResult();
378 t.transform(source, r);
379 retVal = r.getNode();
380 } catch (final TransformerException e) {
381 Parser.LOGGER.warn(e.getMessage());
382 throw new IllegalArgumentException(Parser.CANNOT_HANDLE_SOURCE
383 + source, e);
384 }
385 }
386 return retVal;
387 }
388 }