1 // Copyright (C) 2004, 2014 Christian Jaekl
3 // Abstract class representing the result of parsing an XML Element.
4 // A class derived from this one will know how to parse a subtree inside an XML file, and
5 // will contain the result of that parse within itself when the parse has completed.
7 // Note that this code will need to be augmented and fixed if XML namespace support is desired.
9 package net.jaekl.qd.xml;
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.HashSet;
14 import java.util.Iterator;
15 import java.util.Stack;
17 import org.xml.sax.Attributes;
19 public abstract class ParseResult
21 Stack<String> m_current; // Name of the element that we're currently inside
22 StringBuilder m_chars; // character content of m_current.peek()
23 ArrayList<ParseResult> m_childParsers; // Set of all child parsers
24 boolean m_haveSeenMyTag; // Have I encountered my own (root) tag yet?
26 String m_tagName; // Name of the (root) element tag that I'm parsing
27 HashSet<String> m_internal; // Tags that we will store as members of this class instance
28 HashMap<String,Class<? extends ParseResult>> m_external; // Tags that we will store as child ParseResult-derived objects
30 @SuppressWarnings("unchecked")
31 public ParseResult(String tagName, String[] internalMemberTags, Object[][] externalParserTags)
33 m_current = new Stack<String>();
34 m_chars = new StringBuilder();
35 m_childParsers = new ArrayList<ParseResult>();
36 m_haveSeenMyTag = false;
39 m_internal = new HashSet<String>();
40 m_external = new HashMap<String, Class<? extends ParseResult>>();
42 for (String internalTag : internalMemberTags) {
43 m_internal.add(internalTag);
46 for (int idx = 0; idx < externalParserTags.length; ++idx) {
47 String externalTag = (String)externalParserTags[idx][0];
48 Class<? extends ParseResult> parserClass = (Class<? extends ParseResult>)externalParserTags[idx][1];
49 m_external.put(externalTag, parserClass);
53 public abstract void endContents(String uri, String localName, String qName, String chars) throws XmlParseException;
54 public abstract void endExternal(String uri, String localName, String qName) throws XmlParseException;
56 public String getTagName() { return m_tagName; }
57 public boolean haveSeenMyTag() { return m_haveSeenMyTag; }
59 public void characters(char[] ch, int start, int length) throws XmlParseException
61 m_chars.append(ch, start, length);
64 protected ParseResult[] collectParsedChildren(Class<? extends ParseResult> cls) {
65 ArrayList<ParseResult> collection = new ArrayList<ParseResult>();
66 Iterator<ParseResult> iter = m_childParsers.iterator();
67 while (iter.hasNext()) {
68 ParseResult pr = iter.next();
69 if (pr.getClass().isAssignableFrom(cls)) {
75 ParseResult[] result = new ParseResult[collection.size()];
76 return collection.toArray(result);
79 // returns true if this ParseResult's context has ended with this endElement() call
80 public boolean endElement(String uri, String localName, String qName) throws XmlParseException
82 assert (null != localName);
84 boolean isInternal = m_internal.contains(localName);
86 if (! m_haveSeenMyTag) {
87 // We're in some unrecognised prologue. Ignore it and move on.
91 if (m_tagName.equals(localName)) {
97 // Unrecognized tag. Ignore it.
101 String tag = m_current.pop();
102 if ( ! tag.equals(localName) ) {
103 throw new MismatchedTagsException(tag, localName);
106 String chars = m_chars.toString();
107 endContents(uri, localName, qName, chars);
112 // returns either itself, or a new ParseResult-derived object, whichever should handle parsing the inside of this element
113 public ParseResult startElement(String uri, String localName, String qName, Attributes attributes)
114 throws XmlParseException
116 assert (null != localName);
118 m_chars.setLength(0);
120 if (! m_haveSeenMyTag) {
121 // Have we opened our own (root) tag yet?
123 if (m_tagName.equals(localName)) {
124 m_haveSeenMyTag = true;
128 // One of two things has happened here.
129 // Either (a) we've got some sort of wrapper here, and have not yet reach our own tag,
130 // or (b) we're parsing XML that doesn't match expectations.
131 // In either case, we're going to ignore this tag, and scan forward looking for our own root.
136 if (m_internal.contains(localName)) {
137 m_current.push(localName);
141 Class<? extends ParseResult> parserClass = m_external.get(localName);
142 if (null != parserClass) {
144 ParseResult childParser = (ParseResult) parserClass.newInstance();
145 m_childParsers.add(childParser);
146 return childParser.startElement(uri, localName, qName, attributes);
148 catch (IllegalAccessException iae) {
149 throw new XmlParseException(iae);
151 catch (InstantiationException ie) {
152 throw new XmlParseException(ie);
156 // Not a recognized tag. Ignore it, rather than complaining.
160 public void validate() throws XmlParseException
162 // Default implementation is a no-op.
163 // Override if you want to validate on endElement()