001: package uk.org.ponder.saxalizer;
002:
003: import java.io.InputStream;
004: import java.io.Reader;
005:
006: import org.xml.sax.AttributeList;
007: import org.xml.sax.HandlerBase;
008: import org.xml.sax.InputSource;
009: import org.xml.sax.Locator;
010: import org.xml.sax.Parser;
011: import org.xml.sax.SAXException;
012: import org.xml.sax.SAXParseException;
013:
014: import uk.org.ponder.saxalizer.mapping.ClassNameManager;
015: import uk.org.ponder.streamutil.StreamCloseUtil;
016: import uk.org.ponder.util.UniversalRuntimeException;
017:
018: /** This useful helper class can be used if the XML
019: * root tag itself is required to be the root of the deserialised
020: * object tree. In this case, we can avoid direct use of the SAXalizer and
021: * the SAX API itself, and merely pass in an InputStream attached to the
022: * XML document. The SAXalizerHelper class parses the stream and
023: * returns the single object corresponding to the XML root node,
024: * at the head of the tree of deserialised Java objects.
025: */
026:
027: public class SAXalizerHelper extends HandlerBase {
028: private Object rootobjstash;
029: private Parser parserstash;
030: private EntityResolverStash entityresolverstash;
031: private SAXalizer saxer;
032: private ClassNameManager classnamemanager;
033:
034: public SAXalizerHelper() {
035: this (SAXalizerMappingContext.instance());
036: }
037:
038: public SAXalizerHelper(SAXalizerMappingContext mappingcontext) {
039: saxer = new SAXalizer(mappingcontext);
040: parserstash = SAXParserFactory.newParser();
041: classnamemanager = mappingcontext.classnamemanager;
042: }
043:
044: private ParseCompleteCallback callback;
045: private int callbackindex;
046:
047: void setParseCompleteCallback(ParseCompleteCallback callback,
048: int index) {
049: this .callback = callback;
050: this .callbackindex = index;
051: }
052:
053: public void setEntityResolverStash(
054: EntityResolverStash entityresolverstash) {
055: this .entityresolverstash = entityresolverstash;
056: saxer.setEntityResolverStash(entityresolverstash);
057: }
058:
059: public Object produceSubtree(Object rootobj, Reader reader)
060: throws SAXException {
061: InputSource i = new InputSource(reader);
062: //i.setSystemId("SAXalizing page");
063: try {
064: return produceSubtreeInternal(rootobj, i);
065: } finally {
066: StreamCloseUtil.closeReader(reader);
067: }
068: }
069:
070: /** This method parses a stream attached to an XML document, and returns
071: * a deserialised object tree corresponding to its root node.
072: * @param rootobj The required root object to receive the SAXalized subtree. This
073: * class must implement (at least) the SAXalizable interface.
074: * @param stream A stream attached to an XML document.
075: This stream WILL be closed by this call.
076: * @exception SAXException if the stream could not be parsed as XML.
077: * @return An object representing the XML root node.
078: */
079: // Currently closes the stream
080: public Object produceSubtree(Object rootobj, InputStream stream)
081: throws SAXException {
082: InputSource i = new InputSource(stream);
083: //i.setSystemId("SAXalizing page");
084: try {
085: return produceSubtreeInternal(rootobj, i);
086: } finally {
087: StreamCloseUtil.closeInputStream(stream);
088: }
089: }
090:
091: private Object produceSubtreeInternal(Object rootobj, InputSource i)
092: throws SAXException {
093: parserstash.setDocumentHandler(this );
094: parserstash.setEntityResolver(this );
095: try {
096: rootobjstash = rootobj;
097: parserstash.parse(i); // begin to parse at this point, asynchronous events arrive
098: } catch (SAXParseException spe) {
099: throw UniversalRuntimeException.accumulate(spe,
100: "SaxParseException occured at line "
101: + spe.getLineNumber() + " column number "
102: + spe.getColumnNumber());
103: }
104:
105: catch (Exception e) {
106: throw UniversalRuntimeException.accumulate(e,
107: "Error parsing XML document");
108: } finally {
109: saxer.blastState();
110: if (callback != null) {
111: callback.parseComplete(callbackindex);
112: callback = null;
113: }
114: }
115: return rootobjstash;
116: }
117:
118: public void setDocumentLocator(Locator l) {
119: saxer.setDocumentLocator(l);
120: }
121:
122: /** Implements the DocumentHandler interface. This method is only implemented to
123: * intercept the very first opening tag for the root object, at which point
124: * handling is forwarded to the internal SAXalizer object.
125: * @param tagname The tag name for the element just seen in the SAX stream.
126: * @param attrlist The attribute list of the tag just seen in the SAX stream.
127: * @exception SAXException If any exception requires to be propagated from this
128: * interface.
129: */
130:
131: public void startElement(String tagname, AttributeList attrlist)
132: throws SAXException {
133: // System.out.println("ELEMENT received: "+tagname);
134: if (rootobjstash == null) {
135: try {
136: Class objclass = classnamemanager.findClazz(tagname);
137: rootobjstash = objclass.newInstance();
138: } catch (Throwable t) {
139: throw UniversalRuntimeException
140: .accumulate(
141: t,
142: "Tag name "
143: + tagname
144: + " has not been mapped onto a default constructible object");
145: }
146: }
147: // remember that the following production occurs asynchronously, driven
148: // by events from the SAX stream.
149: saxer.produceSubtree(rootobjstash, attrlist, null);
150: // hand over ownership of the SAX stream to the SAXalizer
151: parserstash.setDocumentHandler(saxer);
152: }
153:
154: public InputSource resolveEntity(String publicID, String systemID) {
155: // System.out.println("Helper was asked to resolve public ID "+publicID+" systemID "+systemID);
156: return entityresolverstash == null ? null : entityresolverstash
157: .resolve(publicID);
158: }
159:
160: }
|