1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.ibatis.common.xml;
17
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.io.Reader;
21 import java.util.*;
22
23 import javax.xml.XMLConstants;
24 import javax.xml.parsers.DocumentBuilder;
25 import javax.xml.parsers.DocumentBuilderFactory;
26 import javax.xml.parsers.FactoryConfigurationError;
27 import javax.xml.parsers.ParserConfigurationException;
28
29 import org.w3c.dom.*;
30 import org.xml.sax.*;
31
32
33
34
35
36
37 public class NodeletParser {
38
39
40 private Map letMap = new HashMap();
41
42
43 private boolean validation;
44
45
46 private EntityResolver entityResolver;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 public void addNodelet(String xpath, Nodelet nodelet) {
63 letMap.put(xpath, nodelet);
64 }
65
66
67
68
69
70
71
72
73
74
75 public void parse(Reader reader) throws NodeletException {
76 try {
77 Document doc = createDocument(reader);
78 parse(doc.getLastChild());
79 } catch (Exception e) {
80 throw new NodeletException("Error parsing XML. Cause: " + e, e);
81 }
82 }
83
84
85
86
87
88
89
90
91
92
93 public void parse(InputStream inputStream) throws NodeletException {
94 try {
95 Document doc = createDocument(inputStream);
96 parse(doc.getLastChild());
97 } catch (Exception e) {
98 throw new NodeletException("Error parsing XML. Cause: " + e, e);
99 }
100 }
101
102
103
104
105
106
107
108 public void parse(Node node) {
109 Path path = new Path();
110 processNodelet(node, "/");
111 process(node, path);
112 }
113
114
115
116
117
118
119
120
121
122 private void process(Node node, Path path) {
123 if (node instanceof Element) {
124
125 String elementName = node.getNodeName();
126 path.add(elementName);
127 processNodelet(node, path.toString());
128 processNodelet(node, new StringBuilder("//").append(elementName).toString());
129
130
131 NamedNodeMap attributes = node.getAttributes();
132 int n = attributes.getLength();
133 for (int i = 0; i < n; i++) {
134 Node att = attributes.item(i);
135 String attrName = att.getNodeName();
136 path.add("@" + attrName);
137 processNodelet(att, path.toString());
138 processNodelet(node, new StringBuilder("//@").append(attrName).toString());
139 path.remove();
140 }
141
142
143 NodeList children = node.getChildNodes();
144 for (int i = 0; i < children.getLength(); i++) {
145 process(children.item(i), path);
146 }
147 path.add("end()");
148 processNodelet(node, path.toString());
149 path.remove();
150 path.remove();
151 } else if (node instanceof Text) {
152
153 path.add("text()");
154 processNodelet(node, path.toString());
155 processNodelet(node, "//text()");
156 path.remove();
157 }
158 }
159
160
161
162
163
164
165
166
167
168 private void processNodelet(Node node, String pathString) {
169 Nodelet nodelet = (Nodelet) letMap.get(pathString);
170 if (nodelet != null) {
171 try {
172 nodelet.process(node);
173 } catch (Exception e) {
174 throw new RuntimeException("Error parsing XPath '" + pathString + "'. Cause: " + e, e);
175 }
176 }
177 }
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196 private Document createDocument(Reader reader)
197 throws ParserConfigurationException, FactoryConfigurationError, SAXException, IOException {
198 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
199 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
200 factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
201 factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
202 factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
203 factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
204 factory.setValidating(validation);
205
206 factory.setNamespaceAware(false);
207 factory.setIgnoringComments(true);
208 factory.setIgnoringElementContentWhitespace(false);
209 factory.setCoalescing(false);
210 factory.setExpandEntityReferences(false);
211
212 DocumentBuilder builder = factory.newDocumentBuilder();
213 builder.setEntityResolver(entityResolver);
214 builder.setErrorHandler(new ErrorHandler() {
215 public void error(SAXParseException exception) throws SAXException {
216 throw exception;
217 }
218
219 public void fatalError(SAXParseException exception) throws SAXException {
220 throw exception;
221 }
222
223 public void warning(SAXParseException exception) throws SAXException {
224 }
225 });
226
227 return builder.parse(new InputSource(reader));
228 }
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247 private Document createDocument(InputStream inputStream)
248 throws ParserConfigurationException, FactoryConfigurationError, SAXException, IOException {
249 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
250 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
251 factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
252 factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
253 factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
254 factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
255 factory.setValidating(validation);
256
257 factory.setNamespaceAware(false);
258 factory.setIgnoringComments(true);
259 factory.setIgnoringElementContentWhitespace(false);
260 factory.setCoalescing(false);
261 factory.setExpandEntityReferences(false);
262
263 DocumentBuilder builder = factory.newDocumentBuilder();
264 builder.setEntityResolver(entityResolver);
265 builder.setErrorHandler(new ErrorHandler() {
266 public void error(SAXParseException exception) throws SAXException {
267 throw exception;
268 }
269
270 public void fatalError(SAXParseException exception) throws SAXException {
271 throw exception;
272 }
273
274 public void warning(SAXParseException exception) throws SAXException {
275 }
276 });
277
278 return builder.parse(new InputSource(inputStream));
279 }
280
281
282
283
284
285
286
287 public void setValidation(boolean validation) {
288 this.validation = validation;
289 }
290
291
292
293
294
295
296
297 public void setEntityResolver(EntityResolver resolver) {
298 this.entityResolver = resolver;
299 }
300
301
302
303
304
305
306 private static class Path {
307
308
309 private List nodeList = new ArrayList();
310
311
312
313
314 public Path() {
315 }
316
317
318
319
320
321
322
323 public Path(String path) {
324 StringTokenizer parser = new StringTokenizer(path, "/", false);
325 while (parser.hasMoreTokens()) {
326 nodeList.add(parser.nextToken());
327 }
328 }
329
330
331
332
333
334
335
336 public void add(String node) {
337 nodeList.add(node);
338 }
339
340
341
342
343 public void remove() {
344 nodeList.remove(nodeList.size() - 1);
345 }
346
347 @Override
348 public String toString() {
349 StringBuilder builder = new StringBuilder("/");
350 for (int i = 0; i < nodeList.size(); i++) {
351 builder.append(nodeList.get(i));
352 if (i < nodeList.size() - 1) {
353 builder.append("/");
354 }
355 }
356 return builder.toString();
357 }
358 }
359
360 }