[jdom-interest] Code Submission: SAXBuilder, SAXHandler, SAXFactory refactor...

James Strachan james at metastuff.com
Fri Nov 17 11:30:13 PST 2000


I've refactored my previous refactor ;-)

I've attached modified SAXBuilder and SAXHandler which use a new class
SAXFactory. SAXFactory is a collection of factory methods used by SAXHandler
complete with default implementations. If you want to build custom
implementations of (say) Element this should be achieveable with minimal
code...

public class MyFactory extends SAXFactory {

    public Element createElement(String localName, Namespace
elementNamespace) {
        return new MyElement( localName, elementNamespace );
    }

    public Element createElement(String localName) {
        return new MyElement(localName);
    }
}

    ... then later

    SAXBuilder builder = new SAXBuilder();
    builder.setFactory( new MyFactory() );

Note that using the same technique custom implementations of Attribute,
CDATA, Namespace etc. can all easily be integrated without large numbers of
new classes, APIs etc.

Does this all seem OK with everyone?

J.

James Strachan
=============
email: james at metastuff.com
web: http://www.metastuff.com



If you are not the addressee of this confidential e-mail and any
attachments, please delete it and inform the sender; unauthorised
redistribution or publication is prohibited. Views expressed are those of
the author and do not necessarily represent those of Citria Limited.
        

-------------- next part --------------
A non-text attachment was scrubbed...
Name: SAXHandler.java
Type: application/octet-stream
Size: 19087 bytes
Desc: not available
Url : http://jdom.org/pipermail/jdom-interest/attachments/20001117/246bed14/SAXHandler.obj
-------------- next part --------------
A non-text attachment was scrubbed...
Name: SAXBuilder.java
Type: application/octet-stream
Size: 17403 bytes
Desc: not available
Url : http://jdom.org/pipermail/jdom-interest/attachments/20001117/246bed14/SAXBuilder.obj
-------------- next part --------------
A non-text attachment was scrubbed...
Name: SAXFactory.java
Type: application/octet-stream
Size: 5305 bytes
Desc: not available
Url : http://jdom.org/pipermail/jdom-interest/attachments/20001117/246bed14/SAXFactory.obj
-------------- next part --------------
Index: SAXBuilder.java
===================================================================
RCS file: /home/cvspublic/jdom/src/java/org/jdom/input/SAXBuilder.java,v
retrieving revision 1.25
diff -u -r1.25 SAXBuilder.java
--- SAXBuilder.java	2000/10/23 17:16:29	1.25
+++ SAXBuilder.java	2000/11/17 18:52:54
@@ -80,6 +80,7 @@
 import org.jdom.ProcessingInstruction;
 
 import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
 import org.xml.sax.DTDHandler;
 import org.xml.sax.EntityResolver;
 import org.xml.sax.ErrorHandler;
@@ -107,6 +108,10 @@
     /** Default parser class to use */
     private static final String DEFAULT_SAX_DRIVER =
         "org.apache.xerces.parsers.SAXParser";
+    
+    /** Default singleton implementation of the factory used to build JDOM 
+      * objects if no factory is specified prior to building. */      
+    private static final SAXFactory defaultFactory = new SAXFactory();
 
     /** Whether validation should occur */
     private boolean validate;
@@ -114,6 +119,9 @@
     /** Adapter class to use */
     private String saxDriverClass;
 
+    /** <code>SAXFactory</code> used to build JDOM objects */
+    private SAXFactory factory;
+    
     /** ErrorHandler class to use */
     private ErrorHandler saxErrorHandler = null;
  
@@ -235,6 +243,19 @@
 
     /**
      * <p>
+     * This sets the <code>Factory</code> for the <code>Builder</code>.
+     * This method allows the building of custom JDOM tree objects to be implemented
+     * easily using a custom derivation of {@link SAXFactory}
+     * </p>
+     *
+     * @param factory <code>SAXFactory</code> used to create JDOM objects
+     */
+    public void setFactory(SAXFactory factory) {
+        this.factory = factory;
+    }
+
+    /**
+     * <p>
      * This builds a document from the supplied
      *   input source.
      * </p>
@@ -265,8 +286,7 @@
                 parser = saxXMLFilter;
             }
 
-            DefaultHandler contentHandler =
-                new SAXHandler(doc);
+            DefaultHandler contentHandler = createContentHandler( doc );
 
             parser.setContentHandler(contentHandler);
 
@@ -493,419 +513,17 @@
         }
         return new URL("file", "", path);
     }
-}
-
-class SAXHandler extends DefaultHandler implements LexicalHandler {
-
-    /** <code>Document</code> object being built */
-    private Document document;
-
-    /** Element stack */
-    private Stack stack;
-
-    /** Indicator of where in the document we are */
-    private boolean atRoot;
-
-    /** Indicator of whether we are in a DTD */
-    private boolean inDTD;
-
-    /** Indicator of whether we are in a CDATA */
-    private boolean inCDATA;
-
-    /** Indicator of whether we are in an <code>Entity</code> */
-    private boolean inEntity;
-
-    /** Namespaces declared, but not available */
-    private List declaredNamespaces;
-
-    /** Available namespaces */
-    private List availableNamespaces;
-
-    /**
-     * <p>
-     * This will set the <code>Document</code> to use.
-     * </p>
-     *
-     * @param document <code>Document</code> being parsed.
-     * @throws <code>IOException</code> when errors occur.
-     */
-    public SAXHandler(Document document) throws IOException {
-        this.document = document;
-        atRoot = true;
-        stack = new Stack();
-        declaredNamespaces = new LinkedList();
-        availableNamespaces = new LinkedList();
-        availableNamespaces.add(Namespace.XML_NAMESPACE);
-        inEntity = false;
-        inDTD = false;
-        inCDATA = false;
-    }
-
-    /**
-     * <p>
-     * This will indicate that a processing instruction (other than
-     *   the XML declaration) has been encountered.
-     * </p>
-     *
-     * @param target <code>String</code> target of PI
-     * @param data <code>String</code containing all data sent to the PI.
-     *             This typically looks like one or more attribute value
-     *             pairs.
-     * @throws <code>SAXException</code> when things go wrong
-     */
-    public void processingInstruction(String target, String data)
-        throws SAXException {
-
-        if (atRoot) {
-            document.addContent(new ProcessingInstruction(target, data));
-        } else {
-            ((Element)stack.peek()).addContent(new ProcessingInstruction(target, data));
-        }
-    }
-
-    /**
-     * <p>
-     * This will add the prefix mapping to the JDOM
-     *   <code>Document</code> object.
-     * </p>
-     *
-     * @param prefix <code>String</code> namespace prefix.
-     * @param uri <code>String</code> namespace URI.
-     */
-    public void startPrefixMapping(String prefix, String uri)
-        throws SAXException {
-
-        Namespace ns = Namespace.getNamespace(prefix, uri);
-        declaredNamespaces.add(ns);
-    }
-
-    /**
-     * <p>
-     * This will add the prefix mapping to the JDOM
-     *   <code>Document</code> object.
-     * </p>
-     *
-     * @param prefix <code>String</code> namespace prefix.
-     * @param uri <code>String</code> namespace URI.
-     */
-    public void endPrefixMapping(String prefix, String uri)
-        throws SAXException {
-
-        // This removes the namespace for the actual element
-        Namespace ns = Namespace.getNamespace(prefix, uri);
-        availableNamespaces.remove(ns);
-    }
-
-    /**
-     * <p>
-     * This reports the occurrence of an actual element.  It will include
-     *   the element's attributes, with the exception of XML vocabulary
-     *   specific attributes, such as
-     *   <code>xmlns:[namespace prefix]</code> and
-     *   <code>xsi:schemaLocation</code>.
-     * </p>
-     *
-     * @param namespaceURI <code>String</code> namespace URI this element
-     *                     is associated with, or an empty
-     *                     <code>String</code>
-     * @param localName <code>String</code> name of element (with no
-     *                  namespace prefix, if one is present)
-     * @param rawName <code>String</code> XML 1.0 version of element name:
-     *                [namespace prefix]:[localName]
-     * @param atts <code>Attributes</code> list for this element
-     * @throws <code>SAXException</code> when things go wrong
-     */
-    public void startElement(String namespaceURI, String localName,
-                             String qName, Attributes atts) throws SAXException {
-
-        Element element = null;
-
-        if ((namespaceURI != null) && (!namespaceURI.equals(""))) {
-            String prefix = "";
-
-            // Determine any prefix on the Element
-            if (localName != qName) {
-                int split = qName.indexOf(":");
-                prefix = qName.substring(0, split);
-            }
-            Namespace elementNamespace = Namespace.getNamespace(prefix, namespaceURI);
-            element = new Element(localName, elementNamespace);
-
-            // We want to remove this namespace from those declared, as we've handled that
-            declaredNamespaces.remove(elementNamespace);
-
-            // We do need to make it available for others to use, though
-            availableNamespaces.add(elementNamespace);
-        } else {
-            element = new Element(localName);
-        }
-
-        // We need to take all declared namespaces and add them to this element
-        transferNamespaces(element);
-
-        // Handle attributes
-        for (int i=0, len=atts.getLength(); i<len; i++) {
-            Attribute attribute = null;
-
-            String attLocalName = atts.getLocalName(i);
-            String attQName = atts.getQName(i);
-
-            if (attLocalName != attQName) {
-                String attPrefix = attQName.substring(0, attQName.indexOf(":"));
-                attribute = new Attribute(attLocalName, atts.getValue(i), getNamespace(attPrefix));
-            } else {
-                attribute = new Attribute(attLocalName, atts.getValue(i));
-            }
-
-            element.addAttribute(attribute);
-        }
-
-        if (atRoot) {
-            document.setRootElement(element);
-            stack.push(element);
-            atRoot = false;
-        } else {
-            ((Element)stack.peek()).addContent(element);
-            stack.push(element);
-        }
-    }
-
-    private void transferNamespaces(Element element) {
-        Iterator i = declaredNamespaces.iterator();
-        while (i.hasNext()) {
-            Namespace ns = (Namespace)i.next();
-            i.remove();
-            availableNamespaces.add(ns);
-            element.addNamespaceDeclaration(ns);
-        }
-    }
-
-    private Namespace getNamespace(String prefix) {
-        for (int i=availableNamespaces.size(); i>0; i--) {
-            Namespace ns = (Namespace)availableNamespaces.get(i-1);
-            if (prefix.equals(ns.getPrefix())) {
-                return ns;
-            }
-        }
-
-        return Namespace.NO_NAMESPACE;
-    }
-
-    /**
-     * <p>
-     * This will report character data (within an element).
-     * </p>
-     *
-     * @param ch <code>char[]</code> character array with character data
-     * @param start <code>int</code> index in array where data starts.
-     * @param end <code>int</code> index in array where data ends.
-     * @throws <code>SAXException</code> when things go wrong
-     */
-    public void characters(char[] ch, int start, int end)
-        throws SAXException {
-
-        String data = new String(ch, start, end);
 
-        if (inCDATA) {
-            ((Element)stack.peek()).addContent(new CDATA(data));
- 
-        /**
-         * This is commented out because of some problems with
-         *   the inline DTDs that Xerces seems to have.
-        } else if (!inDTD) {
-            if (inEntity) {
-                ((Entity)stack.peek()).setContent(data);
-            } else {
-                Element e = (Element)stack.peek();
-                e.addContent(data);
-            }
-         */
-        } else if (inEntity) {
-            ((Entity)stack.peek()).setContent(data);
-        } else {
-            Element e = (Element)stack.peek();
-            e.addContent(data);
-        }
-    }
-
-    /**
-     * <p>
-     * Indicates the end of an element
-     *   (<code>&lt;/[element name]&gt;</code>) is reached.  Note that
-     *   the parser does not distinguish between empty
-     *   elements and non-empty elements, so this will occur uniformly.
-     * </p>
-     *
-     * @param namespaceURI <code>String</code> URI of namespace this
-     *                     element is associated with
-     * @param localName <code>String</code> name of element without prefix
-     * @param rawName <code>String</code> name of element in XML 1.0 form
-     * @throws <code>SAXException</code> when things go wrong
-     */
-    public void endElement(String namespaceURI, String localName,
-                           String rawName) {
-
-        Element element = (Element)stack.pop();
-        
-        // Remove the namespaces that this element makes available
-        availableNamespaces.remove(element.getAdditionalNamespaces());
-        
-    }
-
-    /**
-     * <p>
-     * This will report an error that has occurred; this indicates
-     *   that a rule was broken, typically in validation, but that
-     *   parsing can reasonably continue.
-     * </p>
-     *
-     * @param exception <code>SAXParseException</code> that occurred.
-     * @throws <code>SAXException</code> when things go wrong
-     */
-    public void error(SAXParseException exception)
-        throws SAXException {
-
-        throw exception;
-    }
-
-    /**
-     * <p>
-     * This will report an error that has occurred; this indicates
-     *   that a rule was broken, typically in validation, but that
-     *   parsing can reasonably continue.
-     * </p>
-     *
-     * @param exception <code>SAXParseException</code> that occurred.
-     * @throws <code>SAXException</code> when things go wrong
-     */
-    public void warning(SAXParseException exception)
-        throws SAXException {
-
-        throw exception;
-    }
-
-    /**
-     * <p>
-     * This will report an error that has occurred; this indicates
-     *   that a rule was broken, typically in validation, but that
-     *   parsing can reasonably continue.
-     * </p>
-     *
-     * @param exception <code>SAXParseException</code> that occurred.
-     * @throws <code>SAXException</code> when things go wrong
-     */
-    public void fatalError(SAXParseException exception)
-        throws SAXException {
-
-        throw exception;
-    }
-
-    /**
-     * <p>
-     * This will signify that a DTD is being parsed, and can be
-     *   used to ensure that comments and other lexical structures
-     *   in the DTD are not added to the JDOM <code>Document</code>
-     *   object.
-     * </p>
-     *
-     * @param name <code>String</code> name of element listed in DTD
-     * @param publicId <code>String</code> public ID of DTD
-     * @param systemId <code>String</code> syste ID of DTD
-     */
-    public void startDTD(String name, String publicId, String systemId)
-        throws SAXException {
-
-        document.setDocType(
-            new DocType(name, publicId, systemId));
-        inDTD = true;
-
-        /* Debug
-        System.out.println("startDTD invoked...");
-        */
-    }
-
-    /**
-     * <p>
-     * This signifies that the reading of the DTD is complete.
-     * </p>
-     */
-    public void endDTD() throws SAXException {
-        inDTD = false;
-
-        /* Debug 
-        System.out.println("endDTD invoked...");
-        */
-    }
-
-    public void startEntity(String name)
-        throws SAXException {
-
-        // Ignore DTD references, and translate the standard 5
-        if ((!inDTD) &&
-            (!name.equals("amp")) &&
-            (!name.equals("lt")) &&
-            (!name.equals("gt")) &&
-            (!name.equals("apos")) &&
-            (!name.equals("quot"))) {
-
-            Entity entity = new Entity(name);
-            ((Element)stack.peek()).addContent(entity);
-            stack.push(entity);
-            inEntity = true;
-        }
-    }
-
-    public void endEntity(String name)
-        throws SAXException {
-
-        if (inEntity) {
-            stack.pop();
-            inEntity = false;
-        }
-    }
-
-    /**
-     * <p>
-     * Report a CDATA section - ignored in SAXBuilder.
-     * </p>
-     */
-    public void startCDATA() throws SAXException {
-        inCDATA = true;
-    }
-
-    /**
-     * <p>
-     * Report a CDATA section - ignored in SAXBuilder.
-     * </p>
-     */
-    public void endCDATA() throws SAXException {
-        inCDATA = false;
-    }
-
-    /**
-     * <p>
-     * This reports that a comments is parsed.  If not in the
-     *   DTD, this comment is added to the current JDOM
-     *   <code>Element</code>, or the <code>Document</code> itself
-     *   if at that level.
-     * </p>
-     *
-     * @param ch <code>ch[]</code> array of comment characters.
-     * @param start <code>int</code> index to start reading from.
-     * @param end <code>int</code> index to end reading at.
-     */
-    public void comment(char[] ch, int start, int end)
-        throws SAXException {
-
-        String commentText = new String(ch, start, end);
-        if ((!inDTD) && (!commentText.equals(""))) {
-            if (stack.empty()) {
-                document.addContent(
-                   new Comment(commentText));
-            } else {
-                ((Element)stack.peek()).addContent(
-                    new Comment(commentText));
-            }
-        }
+    /** @return the default singleton if no custom factory is specified */
+    protected SAXFactory getFactory() {
+        if ( factory == null ) {
+            return defaultFactory;
+        }
+        return factory;
+    }
+    /** Factory Method to allow user derived SAXHandlers to be used
+      */
+    protected DefaultHandler createContentHandler( Document doc ) throws IOException {
+        return new SAXHandler(getFactory(), doc);
     }
 }


More information about the jdom-interest mailing list