Author: skitching
Date: Tue May 24 19:03:02 2005
New Revision: 178343

URL: http://svn.apache.org/viewcvs?rev=178343&view=rev
Log:
Add setCustomContentHandler general feature to digester so Rule classes
can redirect SAX events without fiddling with the digester's parser directly. 
And use it in NodeCreateRule so that NodeCreateRule works with the Aelfred
xml parser that doesn't like setContentHandler being called during parsing.
See bugzilla #34819

Modified:
    
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/Digester.java
    
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/NodeCreateRule.java

Modified: 
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/Digester.java
URL: 
http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/Digester.java?rev=178343&r1=178342&r2=178343&view=diff
==============================================================================
--- 
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/Digester.java
 (original)
+++ 
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/Digester.java
 Tue May 24 19:03:02 2005
@@ -40,6 +40,7 @@
 import org.apache.commons.collections.ArrayStack;
 
 import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
 import org.xml.sax.EntityResolver;
 import org.xml.sax.ErrorHandler;
 import org.xml.sax.InputSource;
@@ -326,6 +327,17 @@
     /** Stacks used for interrule communication, indexed by name String */
     private HashMap stacksByName = new HashMap();
     
+    /**
+     * If not null, then calls by the parser to this object's characters, 
+     * startElement, endElement and processingInstruction methods are 
+     * forwarded to the specified object. This is intended to allow rules
+     * to temporarily "take control" of the sax events. In particular, 
+     * this is used by NodeCreateRule.
+     * <p>
+     * See setCustomContentHandler.
+     */
+    private ContentHandler customContentHandler = null;
+
     // ------------------------------------------------------------- Properties
 
     /**
@@ -921,6 +933,53 @@
         this.substitutor = substitutor;
     }
 
+    /*
+     * See setCustomContentHandler.
+     * 
+     * @since 1.7 
+     */
+    public ContentHandler getCustomContentHandler() {
+        return customContentHandler;
+    }
+
+    /** 
+     * Redirects (or cancels redirecting) of SAX ContentHandler events to an
+     * external object.
+     * <p>
+     * When this object's customContentHandler is non-null, any SAX events
+     * received from the parser will simply be passed on to the specified 
+     * object instead of this object handling them. This allows Rule classes 
+     * to take control of the SAX event stream for a while in order to do 
+     * custom processing. Such a rule should save the old value before setting
+     * a new one, and restore the old value in order to resume normal digester
+     * processing.
+     * <p>
+     * An example of a Rule which needs this feature is NodeCreateRule.
+     * <p>
+     * Note that saving the old value is probably not needed as it should 
always
+     * be null; a custom rule that wants to take control could only have been 
+     * called when there was no custom content handler. But it seems cleaner
+     * to properly save/restore the value and maybe some day this will come in
+     * useful.
+     * <p>
+     * Note also that this is not quite equivalent to
+     * <pre>
+     * digester.getXMLReader().setContentHandler(handler)
+     * </pre>
+     * for these reasons:
+     * <ul>
+     * <li>Some xml parsers don't like having setContentHandler called after
+     * parsing has started. The Aelfred parser is one example.</li>
+     * <li>Directing the events via the Digester object potentially allows
+     * us to log information about those SAX events at the digester level.</li>
+     * </ul>
+     * 
+     * @since 1.7 
+     */
+    public void setCustomContentHandler(ContentHandler handler) {
+        customContentHandler = handler;
+    }
+
     // ------------------------------------------------- ContentHandler Methods
 
 
@@ -937,6 +996,12 @@
     public void characters(char buffer[], int start, int length)
             throws SAXException {
 
+        if (customContentHandler != null) {
+            // forward calls instead of handling them here
+            customContentHandler.characters(buffer, start, length);
+            return;
+        }
+
         if (saxLog.isDebugEnabled()) {
             saxLog.debug("characters(" + new String(buffer, start, length) + 
")");
         }
@@ -998,6 +1063,12 @@
     public void endElement(String namespaceURI, String localName,
                            String qName) throws SAXException {
 
+        if (customContentHandler != null) {
+            // forward calls instead of handling them here
+            customContentHandler.endElement(namespaceURI, localName, qName);
+            return;
+        }
+
         boolean debug = log.isDebugEnabled();
 
         if (debug) {
@@ -1145,6 +1216,12 @@
     public void processingInstruction(String target, String data)
             throws SAXException {
 
+        if (customContentHandler != null) {
+            // forward calls instead of handling them here
+            customContentHandler.processingInstruction(target, data);
+            return;
+        }
+
         if (saxLog.isDebugEnabled()) {
             saxLog.debug("processingInstruction('" + target + "','" + data + 
"')");
         }
@@ -1235,6 +1312,12 @@
             throws SAXException {
         boolean debug = log.isDebugEnabled();
         
+        if (customContentHandler != null) {
+            // forward calls instead of handling them here
+            customContentHandler.startElement(namespaceURI, localName, qName, 
list);
+            return;
+        }
+
         if (saxLog.isDebugEnabled()) {
             saxLog.debug("startElement(" + namespaceURI + "," + localName + 
"," +
                     qName + ")");
@@ -2416,6 +2499,7 @@
         publicId = null;
         stack.clear();
         stacksByName.clear();
+        customContentHandler = null;
     }
 
 
@@ -2637,6 +2721,8 @@
      * to parse multiple xml documents. However if you are determined to
      * do so, then you should call both clear() and resetRoot() before
      * each parse.
+     *
+     * @since 1.7
      */
     public void resetRoot() {
         root = null;

Modified: 
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/NodeCreateRule.java
URL: 
http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/NodeCreateRule.java?rev=178343&r1=178342&r2=178343&view=diff
==============================================================================
--- 
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/NodeCreateRule.java
 (original)
+++ 
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/NodeCreateRule.java
 Tue May 24 19:03:02 2005
@@ -111,7 +111,7 @@
             this.root = root;
             this.top = root;
             
-            oldContentHandler = digester.getXMLReader().getContentHandler();
+            oldContentHandler = digester.getCustomContentHandler();
 
         }
 
@@ -191,8 +191,7 @@
             
             try {
                 if (depth == 0) {
-                    getDigester().getXMLReader().setContentHandler(
-                        oldContentHandler);
+                    getDigester().setCustomContentHandler(oldContentHandler);
                     getDigester().push(root);
                     getDigester().endElement(namespaceURI, localName, qName);
                 }
@@ -372,11 +371,12 @@
 
 
     /**
-     * When this method fires, the content handler object used by the
-     * xml parser is replaced by a custom one, resulting in a DOM being
+     * When this method fires, the digester is told to forward all SAX
+     * ContentHandler events to the builder object, resulting in a DOM being
      * built instead of normal digester rule-handling occurring. When the
      * end of the current xml element is encountered, the original content 
-     * handler is restored, allowing Digester operations to continue.
+     * handler is restored (expected to be NULL, allowing normal Digester
+     * operations to continue).
      * 
      * @param namespaceURI the namespace URI of the matching element, or an 
      *   empty string if the parser is not namespace aware or the element has
@@ -389,7 +389,6 @@
     public void begin(String namespaceURI, String name, Attributes attributes)
         throws Exception {
 
-        XMLReader xmlReader = getDigester().getXMLReader();
         Document doc = documentBuilder.newDocument();
         NodeBuilder builder = null;
         if (nodeType == Node.ELEMENT_NODE) {
@@ -413,8 +412,11 @@
         } else {
             builder = new NodeBuilder(doc, doc.createDocumentFragment());
         }
-        xmlReader.setContentHandler(builder);
-
+        // the NodeBuilder constructor has already saved the original
+        // value of the digester's custom content handler (expected to
+        // be null, but we save it just in case). So now we just
+        // need to tell the digester to forward events to the builder.
+        getDigester().setCustomContentHandler(builder);
     }
 
 



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to