No subject


Fri Aug 6 17:04:17 PDT 2004


ok; I was able to do clean round-trip with simple
XML-docs (XML file -> StAX -> JDom -> serialize to
file).

Oh, and the code compiled with latest CVS source, but
not with b10; I guess there have been a few API
changes recently, to get them in for 1.0?

Let me know if it looks good. It'd also be interesting
to know how performance compares to SAX-based
alternatives (there shouldn't be huge differences but
who knows).

-+ Tatu +-



		
__________________________________
Do you Yahoo!?
Yahoo! Mail is new and improved - Check it out!
http://promotions.yahoo.com/new_mail
--0-1221077319-1087445116=:11648
Content-Type: text/x-java; name="StAXBuilder.java"
Content-Description: StAXBuilder.java
Content-Disposition: inline; filename="StAXBuilder.java"

package org.jdom.input;

import java.util.HashMap;

import org.jdom.*;

import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

/**
 * Builds a JDOM {@link org.jdom.Document org.jdom.Document} using a
 * {@link javax.xml.stream.XMLStreamReader}.
 */
public class StAXBuilder {

    /**
     * Map that contains conversion from textual attribute types StAX uses,
     * to int values JDOM uses.
     */
    final static HashMap attrTypes = new HashMap(32);
    static {
        attrTypes.put("CDATA", new Integer(Attribute.CDATA_TYPE));
        attrTypes.put("cdata", new Integer(Attribute.CDATA_TYPE));
        attrTypes.put("ID", new Integer(Attribute.ID_TYPE));
        attrTypes.put("id", new Integer(Attribute.ID_TYPE));
        attrTypes.put("IDREF", new Integer(Attribute.IDREF_TYPE));
        attrTypes.put("idref", new Integer(Attribute.IDREF_TYPE));
        attrTypes.put("IDREFS", new Integer(Attribute.IDREFS_TYPE));
        attrTypes.put("idrefs", new Integer(Attribute.IDREFS_TYPE));
        attrTypes.put("ENTITY", new Integer(Attribute.ENTITY_TYPE));
        attrTypes.put("entity", new Integer(Attribute.ENTITY_TYPE));
        attrTypes.put("ENTITIES", new Integer(Attribute.ENTITIES_TYPE));
        attrTypes.put("entities", new Integer(Attribute.ENTITIES_TYPE));
        attrTypes.put("NMTOKEN", new Integer(Attribute.NMTOKEN_TYPE));
        attrTypes.put("nmtoken", new Integer(Attribute.NMTOKEN_TYPE));
        attrTypes.put("NMTOKENS", new Integer(Attribute.NMTOKENS_TYPE));
        attrTypes.put("nmtokens", new Integer(Attribute.NMTOKENS_TYPE));
        attrTypes.put("NOTATION", new Integer(Attribute.NOTATION_TYPE));
        attrTypes.put("notation", new Integer(Attribute.NOTATION_TYPE));
        attrTypes.put("ENUMERATED", new Integer(Attribute.ENUMERATED_TYPE));
        attrTypes.put("enumerated", new Integer(Attribute.ENUMERATED_TYPE));
    }

    /** The factory for creating new JDOM objects */
    private JDOMFactory factory = new DefaultJDOMFactory();

    /**
     * Default constructor.
     */
    public StAXBuilder() {
    }

    /*
     * This sets a custom JDOMFactory for the builder.  Use this to build
     * the tree with your own subclasses of the JDOM classes.
     *
     * @param factory <code>JDOMFactory</code> to use
     */
    public void setFactory(JDOMFactory factory) {
        this.factory = factory;
    }

    /**
     * Returns the current {@link org.jdom.JDOMFactory} in use.
     * @return the factory in use
     */
    public JDOMFactory getFactory() {
        return factory;
    }

    /**
     * This will build a JDOM tree given a StAX stream reader.
     *
     * @param r Stream reader from which input is read.
     * @return <code>Document</code> - JDOM document object.
     * @throws XMLStreamException If the reader threw such exception (to
     *   indicate a parsing or I/O problem)
     */
    public Document build(XMLStreamReader r)
        throws XMLStreamException
    {
        /* Should we do sanity checking to see that r is positioned at
         * beginning? Not doing so will allow creating documents from
         * sub-trees, though?
         */
        Document doc = factory.document(null);
        buildTree(r, doc, null);
        return doc;
    }

    /**
     * This takes a <code>XMLStreamReader</code> and recursively builds up
     * a JDOM tree.
     *
     * @param node <code>Code</node> to examine.
     * @param doc JDOM <code>Document</code> being built.
     * @param current <code>Element</code> that is current parent; null
     *   indicates we are at root level.
     */
    private void buildTree(XMLStreamReader r, Document doc,
                           Element current)
        throws XMLStreamException
    {
        while (r.hasNext()) {
            Content child;

            switch (r.next()) {
            case XMLStreamConstants.CDATA:
                child = factory.cdata(r.getText());
                break;

            case XMLStreamConstants.CHARACTERS:
            case XMLStreamConstants.SPACE:
                /* Small complication: although (ignorable) white space
                 * is allowed in prolog/epilog, and StAX may report such
                 * event, JDOM barfs if trying to add it. Thus, let's just
                 * ignore all textual stuff outside the tree:
                 */
                child = (current == null) ? null : factory.text(r.getText());
                break;

            case XMLStreamConstants.COMMENT:
                child = factory.comment(r.getText());
                break;

            case XMLStreamConstants.DTD:
                /* !!! Note: StAX does not expose enough information about
                 *  doctype declaration (specifically, public and system id!);
                 *  should (re-)parse information... not yet implemented
                 */
                // TBI
                child = null;
                break;

            case XMLStreamConstants.END_DOCUMENT:
            case XMLStreamConstants.END_ELEMENT:
                /* Both of these indicate end of this level, actually; assuming
                 * reader does its own well-formedness checks, shouldn't need
                 * more checking here.
                 */
                return;

            case XMLStreamConstants.ENTITY_DECLARATION:
            case XMLStreamConstants.NOTATION_DECLARATION:
                /* Shouldn't really get these, but maybe some stream readers
                 * do provide the info. If so, better ignore it -- DTD event
                 * should have most/all we need.
                 */
                child = null;
                break;

            case XMLStreamConstants.ENTITY_REFERENCE:
                child = factory.entityRef(r.getLocalName());
                break;

            case XMLStreamConstants.PROCESSING_INSTRUCTION:
                child = factory.processingInstruction(r.getPITarget(), r.getPIData());
                break;

            case XMLStreamConstants.START_DOCUMENT:
                /* This should only be received at the beginning of document...
                 * so, should we indicate the problem or not?
                 */
                /* For now, let it pass: maybe some (broken) readers pass
                 * that info as first event in beginning of doc?
                 */
                child = null;
                break;

            case XMLStreamConstants.START_ELEMENT:
                // Ok, need to add a new element and recurse.
                {
                    Element newElem;
                    String elemPrefix = r.getPrefix(); // needed for special handling of elem's namespace
                    if (elemPrefix == null) {
                        elemPrefix = "";
                    }
                    {
                        String nsURI = r.getNamespaceURI();
                        newElem = factory.element(r.getLocalName(), elemPrefix,
                                                  (nsURI == null) ? "" : nsURI);
                    }
                    // Let's add element right away:
                    if (current == null) { // at root
                        doc.setRootElement(newElem);
                    } else {
                        factory.addContent(current, newElem);
                    }

                    // Any declared namespaces?
                    for (int i = 0, len = r.getNamespaceCount(); i < len; ++i) {
                        String prefix = r.getNamespacePrefix(i);
                        Namespace ns = Namespace.getNamespace(prefix, r.getNamespaceURI(i));
                        // JDOM has special handling for element's "own" ns:
                        if (prefix.equals(elemPrefix)) {
                            ; // already set by when it was constructed...
                        } else {
                            factory.addNamespaceDeclaration(newElem, ns);
                        }
                    }

                    // And then the attributes:
                    for (int i = 0, len = r.getAttributeCount(); i < len; ++i) {
                        Attribute attr;
                        int type = resolveAttrType(r.getAttributeType(i));
                        String prefix = r.getAttributePrefix(i);
                        if (prefix == null || prefix.length() == 0) {
                            // Attribute not in any namespace
                            attr = factory.attribute(r.getAttributeLocalName(i),
                                                     r.getAttributeValue(i),
                                                     Namespace.NO_NAMESPACE);
                        } else {
                            attr = factory.attribute(r.getAttributeLocalName(i),
                                                     r.getAttributeValue(i),
                                                     newElem.getNamespace(prefix));
                        }
                        factory.setAttribute(newElem, attr);
                    }

                    // And then let's recurse
                    buildTree(r, doc, newElem);
                }
                // Since we already added it, let's just loop again
                continue;

                // Should never get these, from a stream reader:
            case XMLStreamConstants.ATTRIBUTE:
            case XMLStreamConstants.NAMESPACE:
                throw new XMLStreamException("Unexpected iterator event type: "+r.getEventType()+"; should not receive such types (broken stream reader?)");

            default:
                throw new XMLStreamException("Unrecognized iterator event type: "+r.getEventType()+"; should not receive such types (broken stream reader?)");
            }

            if (child != null) {
                if (current == null) {
                    factory.addContent(doc, child);
                } else {
                    factory.addContent(current, child);
                }
            }
        }
    }

    private static int resolveAttrType(String typeStr) {
        if (typeStr != null && typeStr.length() > 0) {
            Integer I = (Integer) attrTypes.get(typeStr);
            if (I != null) {
                return I.intValue();
            }
        }
        return Attribute.UNDECLARED_TYPE;
    }

    /**
     * Trivial test driver for testing functionality.
     */
    public static void main(String[] args) throws Exception {
        if (args.length != 1) {
            System.err.println("Usage: java ... [file]");
            System.exit(1);
        }
        String filename = args[0];
        java.io.Reader r = new java.io.FileReader(filename);
        javax.xml.stream.XMLInputFactory f = javax.xml.stream.XMLInputFactory.newInstance();
        XMLStreamReader sr = f.createXMLStreamReader(r);

        Document domDoc = new StAXBuilder().build(sr);
        System.out.println("Done:");
        System.out.println("----- JDom -----");
        org.jdom.output.XMLOutputter outputter = new org.jdom.output.XMLOutputter();
        java.io.PrintWriter pw = new java.io.PrintWriter(System.out);
        outputter.output(domDoc, pw);
        pw.flush();
        System.out.println("----- /JDom -----");
    }

}

--0-1221077319-1087445116=:11648--



More information about the jdom-interest mailing list