All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xmlpull.b5.XmlPullInfosetBuilder Maven / Gradle / Ivy

There is a newer version: 1.2.8
Show newest version
package org.xmlpull.b5;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.Iterator;
import org.xmlpull.infoset.XmlAttribute;
import org.xmlpull.infoset.XmlBuilderException;
import org.xmlpull.infoset.XmlCharacters;
import org.xmlpull.infoset.XmlComment;
import org.xmlpull.infoset.XmlContainer;
import org.xmlpull.infoset.XmlDocument;
import org.xmlpull.infoset.XmlElement;
import org.xmlpull.infoset.XmlInfosetBuilder;
import org.xmlpull.infoset.XmlNamespace;
import org.xmlpull.infoset.XmlPullSerializable;
import org.xmlpull.infoset.impl.XmlDocumentImpl;
//import org.xmlpull.infoset.impl.XmlElementImpl;
import org.xmlpull.infoset.impl.XmlElementWithViewsImpl;
import org.xmlpull.infoset.impl.XmlNamespaceImpl;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;

//TODO: add equals() and hashcode()
//TODO: think about how to do gneralize XmlSerialziable (to simplify serialize()
//TODO: in XmlElement use String namespaceName and do not use XmlNamespace namespace (except for getNamespace()?
//           NOTE: that XML infoset requires prefix information to be present even if not needed ....

/**
 * Implementation of generic builder that uses XmlPull API to access current
 * default XmlPullParser and XmlSerializer. By default builder is using
 * non-validating namespaces enabled pull parser with next() method with to
 * build tree consisting only of XmlDocument, XmlElemenet and String nodes. In
 * future additional options may be available to change builder behavior and to
 * generate any desired subset of XML Information Set 
 *
 * @version $Revision: 1.9 $
 * @author Aleksander Slominski
 *         
 */
public class XmlPullInfosetBuilder extends XmlInfosetBuilder {
    private final static String PROPERTY_XMLDECL_STANDALONE = "http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone";
    private final static String PROPERTY_XMLDECL_VERSION = "http://xmlpull.org/v1/doc/properties.html#xmldecl-version";
    
    
    private boolean readOnly;
    private boolean useComments;
    private boolean usePIs;
    private boolean wrapCharacters;
    private boolean useViews;
    private boolean provideDom2;
    
    protected XmlPullParserFactory factory;
    
    public XmlPullInfosetBuilder() throws XmlBuilderException {
        try {
            factory = XmlPullParserFactory.newInstance(System
                                                       .getProperty(XmlPullParserFactory.PROPERTY_NAME), null);
            factory.setNamespaceAware(true);
        } catch (XmlPullParserException ex) {
            throw new XmlBuilderException("could not create XmlPull factory:"
                                          + ex, ex);
        }
    }
    
    public XmlPullInfosetBuilder(XmlPullParserFactory factory)
    throws XmlBuilderException {
        if (factory == null) {
            throw new IllegalArgumentException();
        }
        this.factory = factory;
        // try {
        this.factory.setNamespaceAware(true);
        // } catch(XmlPullParserException ex) {
        // throw new XmlBuilderException("could not create XmlPull factory:"+ex,
        // ex);
        // }
    }
    
    /**
     * Method get XmlPull factory that is used by this builder.
     */
    public XmlPullParserFactory getFactory() throws XmlBuilderException {
        return factory;
    }
    
    public void setFeature(String featureName, boolean value) throws XmlBuilderException {
        if (featureName.equals(FEATURE_READ_ONLY)) {
            readOnly = value;
        } else if (featureName.equals(FEATURE_BUILD_COMMENTS)) {
            useComments = value;
        } else if (featureName.equals(FEATURE_BUILD_PROCESSING_INSTRUCTIONS)) {
            usePIs = value;
        } else if (featureName.equals(FEATURE_WRAP_CHARACTERS)) {
            wrapCharacters = value;
        } else if (featureName.equals(FEATURE_VIEWS)) {
            useViews = value;
        } else if (featureName.equals(FEATURE_BUILD_DOM2)) {
            provideDom2 = value;
        } else {
            throw new XmlBuilderException("feature '" + featureName + "' not recognized");
        }
        
    }
    
    
    public XmlDocument newDocument(String version, Boolean standalone,
                                   String characterEncoding) {
        return new XmlDocumentImpl(version, standalone, characterEncoding);
    }
    
    public XmlElement newFragment(String elementName) {
        return new XmlElementWithViewsImpl((XmlNamespace) null, elementName);
    }
    
    public XmlElement newFragment(String elementNamespaceName,
                                  String elementName) {
        return new XmlElementWithViewsImpl(elementNamespaceName, elementName);
    }
    
    public XmlElement newFragment(XmlNamespace elementNamespace,
                                  String elementName) {
        return new XmlElementWithViewsImpl(elementNamespace, elementName);
    }
    
    // public abstract XmlNamespace newNamespace(String namespaceName);
    // public abstract XmlNamespace newNamespace(String prefix, String
    // namespaceName);
    
    public XmlNamespace newNamespace(String namespaceName) {
        return new XmlNamespaceImpl(null, namespaceName);
    }
    
    public XmlNamespace newNamespace(String prefix, String namespaceName) {
        return new XmlNamespaceImpl(prefix, namespaceName);
    }
    
    /**
     * Parse document - parser must be in START_DOCUMENT state.
     */
    public XmlDocument parse(XmlPullParser pp) throws XmlBuilderException {
        XmlDocument doc = parseDocumentStart(pp);
        XmlElement root = parseFragment(pp);
        doc.setDocumentElement(root);
        // TODO parseDocumentEnd() - parse epilog with nextToken();
        return doc;
    }
    
    /**
     * Will convert current parser state into event representing XML Infoset
     * item:
     * 
    *
  • START_Document: XmlDocument without root element *
  • START_TAG: XmlElement without children *
  • TEXT: String or XmlCHaracters depending on builder mode *
  • additional states to corresponding XML Infoset items (when * implemented!) *
*/ public Object parseItem(XmlPullParser pp) throws XmlBuilderException { try { int eventType = pp.getEventType(); if (eventType == XmlPullParser.START_TAG) { return parseStartTag(pp); } else if (eventType == XmlPullParser.TEXT) { return pp.getText(); } else if (eventType == XmlPullParser.START_DOCUMENT) { return parseDocumentStart(pp); } else { throw new XmlBuilderException( "currently unsupported event type " + XmlPullParser.TYPES[eventType] + pp.getPositionDescription()); } } catch (XmlPullParserException e) { throw new XmlBuilderException("could not parse XML item", e); } } private XmlDocument parseDocumentStart(XmlPullParser pp) { // sourceForNode.next(); XmlDocument doc = null; try { if (pp.getEventType() != XmlPullParser.START_DOCUMENT) { throw new XmlBuilderException( "parser must be positioned on beginning of document" + " and not " + pp.getPositionDescription()); } // TODO use nextToken() pp.next(); String xmlDeclVersion = (String) pp .getProperty(PROPERTY_XMLDECL_VERSION); Boolean xmlDeclStandalone = (Boolean) pp .getProperty(PROPERTY_XMLDECL_STANDALONE); ; String characterEncoding = pp.getInputEncoding(); doc = new XmlDocumentImpl(xmlDeclVersion, xmlDeclStandalone, characterEncoding); } catch (XmlPullParserException e) { throw new XmlBuilderException( "could not parse XML document prolog", e); } catch (IOException e) { throw new XmlBuilderException("could not read XML document prolog", e); } return doc; } /** * Parse fragment - parser must be on START_TAG. After parsing is on * corresponding END_TAG. */ public XmlElement parseFragment(XmlPullParser pp) throws XmlBuilderException { try { int depth = pp.getDepth(); int eventType = pp.getEventType(); if (eventType != XmlPullParser.START_TAG) { throw new XmlBuilderException( "expected parser to be on start tag and not " + XmlPullParser.TYPES[eventType] + pp.getPositionDescription()); } // int level = depth + 1; XmlElement curElem = parseStartTag(pp); while (true) { eventType = pp.next(); if (eventType == XmlPullParser.START_TAG) { XmlElement child = parseStartTag(pp); curElem.addElement(child); curElem = child; } else if (eventType == XmlPullParser.END_TAG) { XmlContainer parent = curElem.getParent(); if (parent == null) { if (pp.getDepth() != depth) { throw new XmlBuilderException("unbalanced input" + pp.getPositionDescription()); } return curElem; } curElem = (XmlElement) parent; // --level; // curElem = curElem.parent; } else if (eventType == XmlPullParser.TEXT) { curElem.addChild(pp.getText()); } else if (eventType == XmlPullParser.COMMENT) { // TODO } } } catch (XmlPullParserException e) { throw new XmlBuilderException("could not build tree from XML", e); } catch (IOException e) { throw new XmlBuilderException("could not read XML tree content", e); } } /** * Parser must be on START_TAG and this method will convert START_TAG * content into XmlELement. Parser location is not changed. */ public XmlElement parseStartTag(XmlPullParser pp) throws XmlBuilderException { try { // assert pp.getEventType() == XmlPullParser.START_TAG; if (pp.getEventType() != XmlPullParser.START_TAG) { throw new XmlBuilderException( "parser must be on START_TAG and not " + pp.getPositionDescription()); } String elNsPrefix = pp.getPrefix(); XmlNamespace elementNs = new XmlNamespaceImpl(elNsPrefix, pp .getNamespace()); XmlElement el = new XmlElementWithViewsImpl(elementNs, pp.getName()); // add namespaces declarations (if any) for (int i = pp.getNamespaceCount(pp.getDepth() - 1); i < pp .getNamespaceCount(pp.getDepth()); i++) { // TODO think about changing XmlPull to return "" in this case // ... or // not String prefix = pp.getNamespacePrefix(i); el.declareNamespace(prefix == null ? "" : prefix, pp .getNamespaceUri(i)); } // add attributes and namespaces for (int i = 0; i < pp.getAttributeCount(); i++) { el.setAttribute(pp.getAttributeType(i), pp .getAttributePrefix(i), pp.getAttributeNamespace(i), pp .getAttributeName(i), pp.getAttributeValue(i), pp .isAttributeDefault(i) == false); } return el; } catch (XmlPullParserException e) { throw new XmlBuilderException("could not parse XML start tag", e); // } catch (IOException e) { // throw new XmlBuilderException("could not read XML start tag" ,e); } } public XmlDocument parseLocation(String locationUrl) throws XmlBuilderException { // TODO: if first is "/" try opening file URL url = null; try { url = new URL(locationUrl); } catch (MalformedURLException e) { throw new XmlBuilderException("could not parse URL " + locationUrl, e); } try { return parseInputStream(url.openStream()); } catch (IOException e) { throw new XmlBuilderException("could not open connection to URL " + locationUrl, e); } } public XmlElement parseFragmentFromInputStream(InputStream is) throws XmlBuilderException { XmlPullParser pp = null; try { pp = factory.newPullParser(); pp.setInput(is, null); // set options ... try { pp.nextTag(); } catch (IOException e) { throw new XmlBuilderException( "IO error when starting to parse input stream", e); } } catch (XmlPullParserException e) { throw new XmlBuilderException( "could not start parsing input stream", e); } return parseFragment(pp); } public XmlElement parseFragmentFromInputStream(InputStream is, String encoding) throws XmlBuilderException { XmlPullParser pp = null; try { pp = factory.newPullParser(); pp.setInput(is, encoding); // set options ... try { pp.nextTag(); } catch (IOException e) { throw new XmlBuilderException( "IO error when starting to parse input stream (encoding=" + encoding + ")", e); } } catch (XmlPullParserException e) { throw new XmlBuilderException( "could not start parsing input stream (encoding=" + encoding + ")", e); } return parseFragment(pp); } public XmlElement parseFragmentFromReader(Reader reader) throws XmlBuilderException { XmlPullParser pp = null; try { pp = factory.newPullParser(); pp.setInput(reader); // set options ... try { pp.nextTag(); } catch (IOException e) { throw new XmlBuilderException( "IO error when starting to parse from reader", e); } } catch (XmlPullParserException e) { throw new XmlBuilderException( "could not start parsing input from reader", e); } return parseFragment(pp); } public XmlDocument parseInputStream(InputStream is) throws XmlBuilderException { XmlPullParser pp = null; try { pp = factory.newPullParser(); pp.setInput(is, null); // set options ... } catch (XmlPullParserException e) { throw new XmlBuilderException( "could not start parsing input stream", e); } return parse(pp); } public XmlDocument parseInputStream(InputStream is, String encoding) throws XmlBuilderException { XmlPullParser pp = null; try { pp = factory.newPullParser(); pp.setInput(is, encoding); // set options ... } catch (XmlPullParserException e) { throw new XmlBuilderException( "could not start parsing input stream (encoding=" + encoding + ")", e); } return parse(pp); } public XmlDocument parseReader(Reader reader) throws XmlBuilderException { XmlPullParser pp = null; try { pp = factory.newPullParser(); pp.setInput(reader); // set options ... } catch (XmlPullParserException e) { throw new XmlBuilderException( "could not start parsing input from reader", e); } return parse(pp); } /** * Move parser from START_TAG to the corresponding END_TAG which means that * XML sub tree is skipped. * * @param pp * a XmlPullParser * * @exception XmlBuilderException * */ public void skipSubTree(XmlPullParser pp) throws XmlBuilderException { try { pp.require(XmlPullParser.START_TAG, null, null); int level = 1; while (level > 0) { int eventType = pp.next(); if (eventType == XmlPullParser.END_TAG) { --level; } else if (eventType == XmlPullParser.START_TAG) { ++level; } } } catch (XmlPullParserException e) { throw new XmlBuilderException("could not skip subtree", e); } catch (IOException e) { throw new XmlBuilderException("IO error when skipping subtree", e); } } /** * Serialize XML Infoset item including serializing of children. If item is * Collection all items in collection are serialized by recursively calling * this function. This method assumes that item is either interface defined * in XB1 API, class String, or that item implements XmlSerializable * otherwise IllegalArgumentException is thrown. */ public void serialize(Object item, XmlSerializer serializer) throws XmlBuilderException { if (item instanceof Collection) { Collection c = (Collection) item; for (Iterator i = c.iterator(); i.hasNext();) { serialize(i.next(), serializer); } } else if (item instanceof XmlContainer) { serializeContainer((XmlContainer) item, serializer); } else { serializeItem(item, serializer); } } private void serializeContainer(XmlContainer node, XmlSerializer serializer) { if (node instanceof XmlPullSerializable) { try { ((XmlPullSerializable) node).serialize(serializer); } catch (IOException e) { throw new XmlBuilderException("could not serialize node " + node + ": " + e, e); } } else if (node instanceof XmlDocument) { serializeDocument((XmlDocument) node, serializer); } else if (node instanceof XmlElement) { serializeFragment((XmlElement) node, serializer); } else { throw new IllegalArgumentException( "could not serialzie unknown XML container " + node.getClass()); } } /** * Serialize XML Infoset item without serializing any of children. * This method assumes that item is either interface defined in XB1 API, * class String, or item that implements XmlSerializable otherwise * IllegalArgumentException is thrown. */ public void serializeItem(Object item, XmlSerializer ser) throws XmlBuilderException { serializeItem(null, item, ser); } public void serializeItem(XmlElement parent, Object item, XmlSerializer ser) throws XmlBuilderException { try { if (item instanceof XmlPullSerializable) { // ((XmlSerializable)item).serialize(ser); try { ((XmlPullSerializable) item).serialize(ser); } catch (IOException e) { throw new XmlBuilderException("could not serialize item " + item + ": " + e, e); } } else if (item instanceof String) { ser.text(item.toString()); } else if (item instanceof XmlCharacters) { ser.text(((XmlCharacters) item).getText()); } else if (item instanceof XmlComment) { ser.comment(((XmlComment) item).getContent()); } else { String context = ""; if(parent != null) { context = getHeritage(parent); } throw new IllegalArgumentException("could not serialize " + (item != null ? item.getClass() : item)); } } catch (IOException e) { throw new XmlBuilderException("serializing XML start tag failed", e); } } /** * Write XML start tag with information provided in XML element. * * @param el * a XmlElement * @param ser * a XmlSerializer * * @exception XmlBuilderException * */ public void serializeStartTag(XmlElement el, XmlSerializer ser) { try { // declare namespaces XmlNamespace elNamespace = el.getNamespace(); String elPrefix = (elNamespace != null) ? elNamespace.getPrefix() : ""; if (elPrefix == null) { elPrefix = ""; } String nToDeclare = null; if (el.hasNamespaceDeclarations()) { //Iterator iter = el.namespaces().iterator(); //while (iter.hasNext()) { //XmlNamespace n = iter.next(); for (XmlNamespace n : el.namespaces()) { String nPrefix = n.getPrefix(); if (!elPrefix.equals(nPrefix)) { ser.setPrefix(nPrefix, n.getName()); } else { nToDeclare = n.getName(); } } } // make sure that preferred element prefix is declared last so it is // picked up by serializer if (nToDeclare != null) { ser.setPrefix(elPrefix, nToDeclare); } else { if (elNamespace != null) { // first check that it needs to be declared - will declaring // change // anything? String namespaceName = elNamespace.getName(); if (namespaceName == null) { namespaceName = ""; } String serPrefix = null; if (namespaceName.length() > 0) { ser.getPrefix(namespaceName, false); } if (serPrefix == null) { serPrefix = ""; } if (serPrefix != elPrefix && !serPrefix.equals(elPrefix)) { // the prefix was not declared on current elment but // just to enforce // prefix choice ... ser.setPrefix(elPrefix, namespaceName); } } } ser.startTag(el.getNamespaceName(), el.getName()); if (el.hasAttributes()) { //Iterator iter = el.attributes(); //while (iter.hasNext()) { //XmlAttribute a = (XmlAttribute) iter.next(); for (XmlAttribute a : el.attributes()) { if (a instanceof XmlPullSerializable) { ((XmlPullSerializable) a).serialize(ser); } else { ser.attribute(a.getNamespaceName(), a.getName(), a .getValue()); } } } } catch (IOException e) { throw new XmlBuilderException("serializing XML start tag failed", e); } } /** * Write XML end tag with information provided in XML element. * * @param el * a XmlElement * @param ser * a XmlSerializer * * @exception XmlBuilderException * */ public void serializeEndTag(XmlElement el, XmlSerializer ser) { try { ser.endTag(el.getNamespaceName(), el.getName()); } catch (IOException e) { throw new XmlBuilderException("serializing XML end tag failed", e); } } // TODO: would iit be simple make XmlContainer serialziable and use it to // serialize .... // TODO but would it be more flexible? private void serializeDocument(XmlDocument doc, XmlSerializer ser) { try { ser.startDocument(doc.getCharacterEncodingScheme(), doc .isStandalone()); } catch (IOException e) { throw new XmlBuilderException( "serializing XML document start failed", e); } if (doc.getDocumentElement() != null) { serializeFragment(doc.getDocumentElement(), ser); } else { throw new XmlBuilderException( "could not serialize document without root element " + doc + ": "); } try { ser.endDocument(); } catch (IOException e) { throw new XmlBuilderException( "serializing XML document end failed", e); } } private void serializeFragment(XmlElement el, XmlSerializer ser) { serializeStartTag(el, ser); // try { if (el.hasChildren()) { //Iterator iter = el.children().iterator(); //while (iter.hasNext()) { // Object child = iter.next(); for (Object child : el.children()) { if (child instanceof XmlPullSerializable) { // ((XmlSerializable)child).serialize(ser); try { ((XmlPullSerializable) child).serialize(ser); } catch (IOException e) { throw new XmlBuilderException( "could not serialize item " + child + ": " + e, e); } } else if (child instanceof XmlElement) { // if("event-sink-epr".equals(((XmlElement)child).getName())){ // check there is a loop //// XmlContainer parentEl = ((XmlElement) child).getParent(); //// while (parentEl != null) { //// if (parentEl == child) { //// throw new IllegalStateException("detected loop " + child); //// } //// if(parentEl instanceof XmlElement) { //// parentEl = ((XmlElement)parentEl).getParent(); //// } else { //// break; //// } //// } // System.err.println(child); // } serializeFragment((XmlElement) child, ser); } else { //if (child != null) { serializeItem(el, child, ser); //} else { // throw new XmlBuilderException( // "could not serialize null in element " + getHeritage(el)); //} } } } // } catch (IOException e) { // throw new XmlBuilderException("serializing XML element children // failed", // e); // } serializeEndTag(el, ser); } /** * Print parent of parent of parent ... */ private String getHeritage(XmlElement el) { XmlContainer parent = el.getParent(); String path; if (parent instanceof XmlElement) { path = getHeritage((XmlElement) parent) + "/" + el.getName(); } else { path = "/" + el.getName(); } return path; } public void serializeToOutputStream(Object item, // XmlContainer node, OutputStream os, String encoding) throws XmlBuilderException { XmlSerializer ser = null; try { ser = factory.newSerializer(); ser.setOutput(os, encoding); } catch (Exception e) { throw new XmlBuilderException( "could not serialize node to output stream" + " (encoding=" + encoding + ")", e); } serialize(item, ser); try { ser.flush(); } catch (IOException e) { throw new XmlBuilderException("could not flush output", e); } } public void serializeToWriter(Object item, Writer writer) throws XmlBuilderException { XmlSerializer ser = null; try { ser = factory.newSerializer(); ser.setOutput(writer); } catch (Exception e) { throw new XmlBuilderException("could not serialize node to writer", e); } serialize(item, ser); try { ser.flush(); } catch (IOException e) { throw new XmlBuilderException("could not flush output", e); } } public void serializeToWriter(Object item, Writer writer, boolean pretty) throws XmlBuilderException { XmlSerializer ser = null; try { ser = factory.newSerializer(); ser.setOutput(writer); ser.setProperty("http://xmlpull.org/v1/doc/properties.html#serializer-indentation", " "); } catch (Exception e) { throw new XmlBuilderException("could not serialize node to writer", e); } serialize(item, ser); try { ser.flush(); } catch (IOException e) { throw new XmlBuilderException("could not flush output", e); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy