
org.xmlpull.b5.XmlPullInfosetBuilder Maven / Gradle / Ivy
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