1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.xml;
15
16 import java.io.File;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.net.URL;
20 import java.util.AbstractList;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.NoSuchElementException;
26 import java.util.Stack;
27 import java.util.StringTokenizer;
28
29 import javax.xml.parsers.SAXParser;
30 import javax.xml.parsers.SAXParserFactory;
31
32 import org.eclipse.jetty.util.LazyList;
33 import org.eclipse.jetty.util.log.Log;
34 import org.xml.sax.Attributes;
35 import org.xml.sax.ContentHandler;
36 import org.xml.sax.InputSource;
37 import org.xml.sax.SAXException;
38 import org.xml.sax.SAXParseException;
39 import org.xml.sax.XMLReader;
40 import org.xml.sax.helpers.DefaultHandler;
41
42
43
44
45
46
47
48
49
50
51
52 public class XmlParser
53 {
54 private Map _redirectMap = new HashMap();
55 private SAXParser _parser;
56 private Map _observerMap;
57 private Stack _observers = new Stack();
58 private String _xpath;
59 private Object _xpaths;
60 private String _dtd;
61
62
63
64
65
66 public XmlParser()
67 {
68 SAXParserFactory factory = SAXParserFactory.newInstance();
69 boolean validating_dft = factory.getClass().toString().startsWith("org.apache.xerces.");
70 String validating_prop = System.getProperty("org.eclipse.jetty.xml.XmlParser.Validating", validating_dft ? "true" : "false");
71 boolean validating = Boolean.valueOf(validating_prop).booleanValue();
72
73 setValidating(validating);
74 }
75
76
77
78
79
80 public XmlParser(boolean validating)
81 {
82 setValidating(validating);
83 }
84
85
86 public void setValidating(boolean validating)
87 {
88 try
89 {
90 SAXParserFactory factory = SAXParserFactory.newInstance();
91 factory.setValidating(validating);
92 _parser = factory.newSAXParser();
93
94 try
95 {
96 if (validating)
97 _parser.getXMLReader().setFeature("http://apache.org/xml/features/validation/schema", validating);
98 }
99 catch (Exception e)
100 {
101 if (validating)
102 Log.warn("Schema validation may not be supported: ", e);
103 else
104 Log.ignore(e);
105 }
106
107 _parser.getXMLReader().setFeature("http://xml.org/sax/features/validation", validating);
108 _parser.getXMLReader().setFeature("http://xml.org/sax/features/namespaces", true);
109 _parser.getXMLReader().setFeature("http://xml.org/sax/features/namespace-prefixes", false);
110 }
111 catch (Exception e)
112 {
113 Log.warn(Log.EXCEPTION, e);
114 throw new Error(e.toString());
115 }
116 }
117
118
119
120
121
122
123 public synchronized void redirectEntity(String name, URL entity)
124 {
125 if (entity != null)
126 _redirectMap.put(name, entity);
127 }
128
129
130
131
132
133
134 public String getXpath()
135 {
136 return _xpath;
137 }
138
139
140
141
142
143
144
145
146 public void setXpath(String xpath)
147 {
148 _xpath = xpath;
149 StringTokenizer tok = new StringTokenizer(xpath, "| ");
150 while (tok.hasMoreTokens())
151 _xpaths = LazyList.add(_xpaths, tok.nextToken());
152 }
153
154
155 public String getDTD()
156 {
157 return _dtd;
158 }
159
160
161
162
163
164
165
166
167
168
169 public synchronized void addContentHandler(String trigger, ContentHandler observer)
170 {
171 if (_observerMap == null)
172 _observerMap = new HashMap();
173 _observerMap.put(trigger, observer);
174 }
175
176
177 public synchronized Node parse(InputSource source) throws IOException, SAXException
178 {
179 _dtd=null;
180 Handler handler = new Handler();
181 XMLReader reader = _parser.getXMLReader();
182 reader.setContentHandler(handler);
183 reader.setErrorHandler(handler);
184 reader.setEntityResolver(handler);
185 if (Log.isDebugEnabled())
186 Log.debug("parsing: sid=" + source.getSystemId() + ",pid=" + source.getPublicId());
187 _parser.parse(source, handler);
188 if (handler._error != null)
189 throw handler._error;
190 Node doc = (Node) handler._top.get(0);
191 handler.clear();
192 return doc;
193 }
194
195
196
197
198
199 public synchronized Node parse(String url) throws IOException, SAXException
200 {
201 if (Log.isDebugEnabled())
202 Log.debug("parse: " + url);
203 return parse(new InputSource(url));
204 }
205
206
207
208
209
210 public synchronized Node parse(File file) throws IOException, SAXException
211 {
212 if (Log.isDebugEnabled())
213 Log.debug("parse: " + file);
214 return parse(new InputSource(file.toURL().toString()));
215 }
216
217
218
219
220
221 public synchronized Node parse(InputStream in) throws IOException, SAXException
222 {
223 _dtd=null;
224 Handler handler = new Handler();
225 XMLReader reader = _parser.getXMLReader();
226 reader.setContentHandler(handler);
227 reader.setErrorHandler(handler);
228 reader.setEntityResolver(handler);
229 _parser.parse(new InputSource(in), handler);
230 if (handler._error != null)
231 throw handler._error;
232 Node doc = (Node) handler._top.get(0);
233 handler.clear();
234 return doc;
235 }
236
237
238
239 private class NoopHandler extends DefaultHandler
240 {
241 Handler _next;
242 int _depth;
243
244 NoopHandler(Handler next)
245 {
246 this._next = next;
247 }
248
249
250 public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException
251 {
252 _depth++;
253 }
254
255
256 public void endElement(String uri, String localName, String qName) throws SAXException
257 {
258 if (_depth == 0)
259 _parser.getXMLReader().setContentHandler(_next);
260 else
261 _depth--;
262 }
263 }
264
265
266
267 private class Handler extends DefaultHandler
268 {
269 Node _top = new Node(null, null, null);
270 SAXParseException _error;
271 private Node _context = _top;
272 private NoopHandler _noop;
273
274 Handler()
275 {
276 _noop = new NoopHandler(this);
277 }
278
279
280 void clear()
281 {
282 _top = null;
283 _error = null;
284 _context = null;
285 }
286
287
288 public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException
289 {
290 String name = (uri == null || uri.equals("")) ? qName : localName;
291 Node node = new Node(_context, name, attrs);
292
293
294
295 if (_xpaths != null)
296 {
297 String path = node.getPath();
298 boolean match = false;
299 for (int i = LazyList.size(_xpaths); !match && i-- > 0;)
300 {
301 String xpath = (String) LazyList.get(_xpaths, i);
302
303 match = path.equals(xpath) || xpath.startsWith(path) && xpath.length() > path.length() && xpath.charAt(path.length()) == '/';
304 }
305
306 if (match)
307 {
308 _context.add(node);
309 _context = node;
310 }
311 else
312 {
313 _parser.getXMLReader().setContentHandler(_noop);
314 }
315 }
316 else
317 {
318 _context.add(node);
319 _context = node;
320 }
321
322 ContentHandler observer = null;
323 if (_observerMap != null)
324 observer = (ContentHandler) _observerMap.get(name);
325 _observers.push(observer);
326
327 for (int i = 0; i < _observers.size(); i++)
328 if (_observers.get(i) != null)
329 ((ContentHandler) _observers.get(i)).startElement(uri, localName, qName, attrs);
330 }
331
332
333 public void endElement(String uri, String localName, String qName) throws SAXException
334 {
335 _context = _context._parent;
336 for (int i = 0; i < _observers.size(); i++)
337 if (_observers.get(i) != null)
338 ((ContentHandler) _observers.get(i)).endElement(uri, localName, qName);
339 _observers.pop();
340 }
341
342
343 public void ignorableWhitespace(char buf[], int offset, int len) throws SAXException
344 {
345 for (int i = 0; i < _observers.size(); i++)
346 if (_observers.get(i) != null)
347 ((ContentHandler) _observers.get(i)).ignorableWhitespace(buf, offset, len);
348 }
349
350
351 public void characters(char buf[], int offset, int len) throws SAXException
352 {
353 _context.add(new String(buf, offset, len));
354 for (int i = 0; i < _observers.size(); i++)
355 if (_observers.get(i) != null)
356 ((ContentHandler) _observers.get(i)).characters(buf, offset, len);
357 }
358
359
360 public void warning(SAXParseException ex)
361 {
362 Log.debug(Log.EXCEPTION, ex);
363 Log.warn("WARNING@" + getLocationString(ex) + " : " + ex.toString());
364 }
365
366
367 public void error(SAXParseException ex) throws SAXException
368 {
369
370 if (_error == null)
371 _error = ex;
372 Log.debug(Log.EXCEPTION, ex);
373 Log.warn("ERROR@" + getLocationString(ex) + " : " + ex.toString());
374 }
375
376
377 public void fatalError(SAXParseException ex) throws SAXException
378 {
379 _error = ex;
380 Log.debug(Log.EXCEPTION, ex);
381 Log.warn("FATAL@" + getLocationString(ex) + " : " + ex.toString());
382 throw ex;
383 }
384
385
386 private String getLocationString(SAXParseException ex)
387 {
388 return ex.getSystemId() + " line:" + ex.getLineNumber() + " col:" + ex.getColumnNumber();
389 }
390
391
392 public InputSource resolveEntity(String pid, String sid)
393 {
394 if (Log.isDebugEnabled())
395 Log.debug("resolveEntity(" + pid + ", " + sid + ")");
396
397 if (sid!=null && sid.endsWith(".dtd"))
398 _dtd=sid;
399
400 URL entity = null;
401 if (pid != null)
402 entity = (URL) _redirectMap.get(pid);
403 if (entity == null)
404 entity = (URL) _redirectMap.get(sid);
405 if (entity == null)
406 {
407 String dtd = sid;
408 if (dtd.lastIndexOf('/') >= 0)
409 dtd = dtd.substring(dtd.lastIndexOf('/') + 1);
410
411 if (Log.isDebugEnabled())
412 Log.debug("Can't exact match entity in redirect map, trying " + dtd);
413 entity = (URL) _redirectMap.get(dtd);
414 }
415
416 if (entity != null)
417 {
418 try
419 {
420 InputStream in = entity.openStream();
421 if (Log.isDebugEnabled())
422 Log.debug("Redirected entity " + sid + " --> " + entity);
423 InputSource is = new InputSource(in);
424 is.setSystemId(sid);
425 return is;
426 }
427 catch (IOException e)
428 {
429 Log.ignore(e);
430 }
431 }
432 return null;
433 }
434 }
435
436
437
438
439
440
441 public static class Attribute
442 {
443 private String _name;
444 private String _value;
445
446 Attribute(String n, String v)
447 {
448 _name = n;
449 _value = v;
450 }
451
452 public String getName()
453 {
454 return _name;
455 }
456
457 public String getValue()
458 {
459 return _value;
460 }
461 }
462
463
464
465
466
467
468 public static class Node extends AbstractList
469 {
470 Node _parent;
471 private ArrayList _list;
472 private String _tag;
473 private Attribute[] _attrs;
474 private boolean _lastString = false;
475 private String _path;
476
477
478 Node(Node parent, String tag, Attributes attrs)
479 {
480 _parent = parent;
481 _tag = tag;
482
483 if (attrs != null)
484 {
485 _attrs = new Attribute[attrs.getLength()];
486 for (int i = 0; i < attrs.getLength(); i++)
487 {
488 String name = attrs.getLocalName(i);
489 if (name == null || name.equals(""))
490 name = attrs.getQName(i);
491 _attrs[i] = new Attribute(name, attrs.getValue(i));
492 }
493 }
494 }
495
496
497 public Node getParent()
498 {
499 return _parent;
500 }
501
502
503 public String getTag()
504 {
505 return _tag;
506 }
507
508
509 public String getPath()
510 {
511 if (_path == null)
512 {
513 if (getParent() != null && getParent().getTag() != null)
514 _path = getParent().getPath() + "/" + _tag;
515 else
516 _path = "/" + _tag;
517 }
518 return _path;
519 }
520
521
522
523
524
525 public Attribute[] getAttributes()
526 {
527 return _attrs;
528 }
529
530
531
532
533
534
535
536 public String getAttribute(String name)
537 {
538 return getAttribute(name, null);
539 }
540
541
542
543
544
545
546
547 public String getAttribute(String name, String dft)
548 {
549 if (_attrs == null || name == null)
550 return dft;
551 for (int i = 0; i < _attrs.length; i++)
552 if (name.equals(_attrs[i].getName()))
553 return _attrs[i].getValue();
554 return dft;
555 }
556
557
558
559
560
561 public int size()
562 {
563 if (_list != null)
564 return _list.size();
565 return 0;
566 }
567
568
569
570
571
572
573
574 public Object get(int i)
575 {
576 if (_list != null)
577 return _list.get(i);
578 return null;
579 }
580
581
582
583
584
585
586
587
588 public Node get(String tag)
589 {
590 if (_list != null)
591 {
592 for (int i = 0; i < _list.size(); i++)
593 {
594 Object o = _list.get(i);
595 if (o instanceof Node)
596 {
597 Node n = (Node) o;
598 if (tag.equals(n._tag))
599 return n;
600 }
601 }
602 }
603 return null;
604 }
605
606
607 @Override
608 public void add(int i, Object o)
609 {
610 if (_list == null)
611 _list = new ArrayList();
612 if (o instanceof String)
613 {
614 if (_lastString)
615 {
616 int last = _list.size() - 1;
617 _list.set(last, (String) _list.get(last) + o);
618 }
619 else
620 _list.add(i, o);
621 _lastString = true;
622 }
623 else
624 {
625 _lastString = false;
626 _list.add(i, o);
627 }
628 }
629
630
631 public void clear()
632 {
633 if (_list != null)
634 _list.clear();
635 _list = null;
636 }
637
638
639
640
641
642
643
644
645
646
647 public String getString(String tag, boolean tags, boolean trim)
648 {
649 Node node = get(tag);
650 if (node == null)
651 return null;
652 String s = node.toString(tags);
653 if (s != null && trim)
654 s = s.trim();
655 return s;
656 }
657
658
659 public synchronized String toString()
660 {
661 return toString(true);
662 }
663
664
665
666
667
668
669
670 public synchronized String toString(boolean tag)
671 {
672 StringBuilder buf = new StringBuilder();
673 toString(buf, tag);
674 return buf.toString();
675 }
676
677
678
679
680
681
682
683 public synchronized String toString(boolean tag, boolean trim)
684 {
685 String s = toString(tag);
686 if (s != null && trim)
687 s = s.trim();
688 return s;
689 }
690
691
692 private synchronized void toString(StringBuilder buf, boolean tag)
693 {
694 if (tag)
695 {
696 buf.append("<");
697 buf.append(_tag);
698
699 if (_attrs != null)
700 {
701 for (int i = 0; i < _attrs.length; i++)
702 {
703 buf.append(' ');
704 buf.append(_attrs[i].getName());
705 buf.append("=\"");
706 buf.append(_attrs[i].getValue());
707 buf.append("\"");
708 }
709 }
710 }
711
712 if (_list != null)
713 {
714 if (tag)
715 buf.append(">");
716 for (int i = 0; i < _list.size(); i++)
717 {
718 Object o = _list.get(i);
719 if (o == null)
720 continue;
721 if (o instanceof Node)
722 ((Node) o).toString(buf, tag);
723 else
724 buf.append(o.toString());
725 }
726 if (tag)
727 {
728 buf.append("</");
729 buf.append(_tag);
730 buf.append(">");
731 }
732 }
733 else if (tag)
734 buf.append("/>");
735 }
736
737
738
739
740
741
742
743
744 public Iterator<Node> iterator(final String tag)
745 {
746 return new Iterator<Node>()
747 {
748 int c = 0;
749 Node _node;
750
751
752 public boolean hasNext()
753 {
754 if (_node != null)
755 return true;
756 while (_list != null && c < _list.size())
757 {
758 Object o = _list.get(c);
759 if (o instanceof Node)
760 {
761 Node n = (Node) o;
762 if (tag.equals(n._tag))
763 {
764 _node = n;
765 return true;
766 }
767 }
768 c++;
769 }
770 return false;
771 }
772
773
774 public Node next()
775 {
776 try
777 {
778 if (hasNext())
779 return _node;
780 throw new NoSuchElementException();
781 }
782 finally
783 {
784 _node = null;
785 c++;
786 }
787 }
788
789
790 public void remove()
791 {
792 throw new UnsupportedOperationException("Not supported");
793 }
794 };
795 }
796 }
797 }