/*******************************************************************************
* Copyright (c) 2009 MATERNA Information & Communications. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html. For further
* project-related information visit http://www.ws4d.org. The most recent
* version of the JMEDS framework can be obtained from
* http://sourceforge.net/projects/ws4d-javame.
******************************************************************************/
package org.ws4d.java.io.xml;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import org.ws4d.java.constants.WSDConstants;
import org.ws4d.java.constants.XMLConstants;
import org.ws4d.java.structures.HashMap;
import org.ws4d.java.types.AttributedURI;
import org.ws4d.java.types.LocalizedString;
import org.ws4d.java.types.ProbeScopeSet;
import org.ws4d.java.types.QName;
import org.ws4d.java.types.QNameSet;
import org.ws4d.java.types.ScopeSet;
import org.ws4d.java.types.URI;
import org.ws4d.java.types.URISet;
import org.ws4d.java.types.UnknownDataContainer;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
/**
*
*/
public class ElementParser implements XmlPullParser {
private static final String ILLEGAL_TYPE = "Wrong event type";
private static final String AT_END_OF_DOCUMENT = "Already at end of document";
private final XmlPullParser source;
private final int elementDepth;
private boolean finished = false;
public static int nextNonWhiteSpace(String src, int offset) {
if (src == null) {
return -1;
}
int len = src.length();
for (int i = offset + 1; i < len; i++) {
char c = src.charAt(i);
switch (c) {
case (' '):
case ('\t'):
case ('\n'):
case ('\r'): {
break;
}
default: {
return i;
}
}
}
return -1;
}
public static int nextWhiteSpace(String src, int offset) {
if (src == null) {
return -1;
}
int len = src.length();
for (int i = offset + 1; i < len; i++) {
char c = src.charAt(i);
switch (c) {
case (' '):
case ('\t'):
case ('\n'):
case ('\r'): {
return i;
}
default: {
break;
}
}
}
return -1;
}
/**
* @param source
*/
public ElementParser(XmlPullParser source) {
super();
this.source = source;
this.elementDepth = source.getDepth();
}
/*
* (non-Javadoc)
* @see
* org.xmlpull.v1.XmlPullParser#defineEntityReplacementText(java.lang.String
* , java.lang.String)
*/
public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException {
throw new XmlPullParserException("This parser doesn't support entity replacement text", this, null);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getAttributeCount()
*/
public int getAttributeCount() {
return finished ? -1 : source.getAttributeCount();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getAttributeName(int)
*/
public String getAttributeName(int index) {
if (finished) {
throw new IndexOutOfBoundsException();
}
return source.getAttributeName(index);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getAttributeNamespace(int)
*/
public String getAttributeNamespace(int index) {
if (finished) {
throw new IndexOutOfBoundsException();
}
return source.getAttributeNamespace(index);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getAttributePrefix(int)
*/
public String getAttributePrefix(int index) {
if (finished) {
throw new IndexOutOfBoundsException();
}
return source.getAttributePrefix(index);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getAttributeType(int)
*/
public String getAttributeType(int index) {
return "CDATA";
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getAttributeValue(int)
*/
public String getAttributeValue(int index) {
if (finished) {
throw new IndexOutOfBoundsException();
}
return source.getAttributeValue(index);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getAttributeValue(java.lang.String,
* java.lang.String)
*/
public String getAttributeValue(String namespace, String name) {
if (finished) {
throw new IndexOutOfBoundsException();
}
return source.getAttributeValue(namespace, name);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getColumnNumber()
*/
public int getColumnNumber() {
return source.getColumnNumber();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getDepth()
*/
public int getDepth() {
return source.getDepth() - elementDepth;
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getEventType()
*/
public int getEventType() throws XmlPullParserException {
return finished ? END_DOCUMENT : source.getEventType();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getFeature(java.lang.String)
*/
public boolean getFeature(String name) {
return false;
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getInputEncoding()
*/
public String getInputEncoding() {
return source.getInputEncoding();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getLineNumber()
*/
public int getLineNumber() {
return source.getLineNumber();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getName()
*/
public String getName() {
return finished ? null : source.getName();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getNamespace()
*/
public String getNamespace() {
return finished ? null : source.getNamespace();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getNamespace(java.lang.String)
*/
public String getNamespace(String prefix) {
return source.getNamespace(prefix);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getNamespaceCount(int)
*/
public int getNamespaceCount(int depth) throws XmlPullParserException {
return source.getNamespaceCount(depth + elementDepth);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getNamespacePrefix(int)
*/
public String getNamespacePrefix(int pos) throws XmlPullParserException {
return source.getNamespacePrefix(pos);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getNamespaceUri(int)
*/
public String getNamespaceUri(int pos) throws XmlPullParserException {
return source.getNamespaceUri(pos);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getPositionDescription()
*/
public String getPositionDescription() {
return source.getPositionDescription();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getPrefix()
*/
public String getPrefix() {
return finished ? null : source.getPrefix();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getProperty(java.lang.String)
*/
public Object getProperty(String name) {
return null;
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getText()
*/
public String getText() {
return finished ? null : source.getText();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#getTextCharacters(int[])
*/
public char[] getTextCharacters(int[] holderForStartAndLength) {
return finished ? null : source.getTextCharacters(holderForStartAndLength);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#isAttributeDefault(int)
*/
public boolean isAttributeDefault(int index) {
return finished ? false : source.isAttributeDefault(index);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#isEmptyElementTag()
*/
public boolean isEmptyElementTag() throws XmlPullParserException {
if (finished) {
throw new XmlPullParserException(ILLEGAL_TYPE, this, null);
}
return source.isEmptyElementTag();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#isWhitespace()
*/
public boolean isWhitespace() throws XmlPullParserException {
if (finished) {
throw new XmlPullParserException(AT_END_OF_DOCUMENT, this, null);
}
return source.isWhitespace();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#next()
*/
public int next() throws XmlPullParserException, IOException {
checkFinished();
return finished ? END_DOCUMENT : source.next();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#nextTag()
*/
public int nextTag() throws XmlPullParserException, IOException {
checkFinished();
if (finished) {
throw new XmlPullParserException(AT_END_OF_DOCUMENT, this, null);
}
return source.nextTag();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#nextText()
*/
public String nextText() throws XmlPullParserException, IOException {
checkFinished();
if (finished) {
throw new XmlPullParserException(AT_END_OF_DOCUMENT, this, null);
}
return source.nextText().trim();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#nextToken()
*/
public int nextToken() throws XmlPullParserException, IOException {
checkFinished();
return finished ? END_DOCUMENT : source.nextToken();
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#require(int, java.lang.String,
* java.lang.String)
*/
public void require(int type, String namespace, String name) throws XmlPullParserException, IOException {
if (finished) {
throw new XmlPullParserException(AT_END_OF_DOCUMENT, this, null);
}
source.require(type, namespace, name);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#setFeature(java.lang.String, boolean)
*/
public void setFeature(String name, boolean state) throws XmlPullParserException {
throw new XmlPullParserException("This parser doesn't support features", this, null);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#setInput(java.io.Reader)
*/
public void setInput(Reader in) throws XmlPullParserException {
throw new XmlPullParserException("This parser doesn't support resetting", this, null);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#setInput(java.io.InputStream,
* java.lang.String)
*/
public void setInput(InputStream inputStream, String inputEncoding) throws XmlPullParserException {
throw new XmlPullParserException("This parser doesn't support resetting", this, null);
}
/*
* (non-Javadoc)
* @see org.xmlpull.v1.XmlPullParser#setProperty(java.lang.String,
* java.lang.Object)
*/
public void setProperty(String name, Object value) throws XmlPullParserException {
throw new XmlPullParserException("This parser doesn't support properties", this, null);
}
public LocalizedString nextLocalizedString() throws XmlPullParserException, IOException {
LocalizedString result;
String lang = null;
int attributeCount = getAttributeCount();
HashMap attributes = null;
for (int i = 0; i < attributeCount; i++) {
String namespace = getAttributeNamespace(i);
String name = getAttributeName(i);
String value = getAttributeValue(i);
if (XMLConstants.XML_NAMESPACE_NAME.equals(namespace) && XMLConstants.XML_ATTRIBUTE_LANGUAGE.equals(name)) {
lang = value;
} else {
if (attributes == null) {
attributes = new HashMap();
}
attributes.put(new QName(name, namespace), value);
}
}
result = new LocalizedString(nextText().trim(), lang);
if (attributes != null) {
result.setUnknownAttributes(attributes);
}
return result;
}
public AttributedURI nextAttributedUri() throws XmlPullParserException, IOException {
AttributedURI result;
int attributeCount = getAttributeCount();
if (attributeCount > 0) {
HashMap attributes = new HashMap();
for (int i = 0; i < attributeCount; i++) {
String namespace = getAttributeNamespace(i);
String name = getAttributeName(i);
String value = getAttributeValue(i);
attributes.put(new QName(name, namespace), value);
}
result = new AttributedURI(nextText().trim(), attributes);
} else {
result = new AttributedURI(nextText().trim());
}
return result;
}
/**
* @return the next text as {@link QName}
* @throws XmlPullParserException
* @throws IOException
*/
public QName nextQName() throws XmlPullParserException, IOException {
return createQName(nextText());
}
/**
* @param i
* @return the value of the attribute at index <code>i</code> as
* {@link QName}
*/
public QName getAttributeValueAsQName(int i) {
return createQName(getAttributeValue(i));
}
// private ArrayList parseServiceName() throws XmlPullParserException,
// IOException {
// ArrayList list = new ArrayList();
// ElementParser parser = new ElementParser(this);
// int attributeCount = parser.getAttributeCount();
// if (attributeCount > 0) {
// String value = parser.getAttributeValue(0);
// list.add(value);
// }
// QName serviceName = parser.nextQName();
// list.add(serviceName);
// return list;
// }
/**
* @return the next {@link QNameSet}
* @throws XmlPullParserException
* @throws IOException
*/
public QNameSet nextQNameSet() throws XmlPullParserException, IOException {
QNameSet qNameSet = new QNameSet();
String value = nextText();
int pos1 = -1;
int pos2 = pos1;
do {
pos1 = nextNonWhiteSpace(value, pos1);
if (pos1 == -1) {
break;
}
pos2 = nextWhiteSpace(value, pos1);
if (pos2 == -1) {
pos2 = value.length();
}
String rawQName = value.substring(pos1, pos2);
qNameSet.add(createQName(rawQName));
pos1 = pos2;
} while (pos1 != -1);
return qNameSet;
}
public ScopeSet nextScopeSet() throws XmlPullParserException, IOException {
ScopeSet scopeSet = new ScopeSet();
int attributeCount = getAttributeCount();
for (int i = 0; i < attributeCount; i++) {
String namespace = getAttributeNamespace(i);
String name = getAttributeName(i);
String value = getAttributeValue(i);
scopeSet.addUnknownAttribute(new QName(name, namespace), value);
}
nextScopeSet(scopeSet);
return scopeSet;
}
public ProbeScopeSet nextProbeScopeSet() throws XmlPullParserException, IOException {
ProbeScopeSet scopeSet = new ProbeScopeSet();
int attributeCount = getAttributeCount();
String matchBy = WSDConstants.WSD_MATCHING_RULE_DEFAULT;
for (int i = 0; i < attributeCount; i++) {
String namespace = getAttributeNamespace(i);
String name = getAttributeName(i);
String value = getAttributeValue(i);
if ("".equals(namespace) && WSDConstants.WSD_ATTR_MATCH_BY.equals(name)) {
matchBy = value;
} else {
scopeSet.addUnknownAttribute(new QName(name, namespace), value);
}
}
scopeSet.setMatchBy(matchBy);
nextScopeSet(scopeSet);
return scopeSet;
}
/**
* Parses scopes list and adds scope to given scope set.
*
* @param scopes
* @throws XmlPullParserException
* @throws IOException
*/
public void nextScopeSet(ScopeSet scopes) throws XmlPullParserException, IOException {
String value = nextText();
int pos1 = -1;
int pos2 = pos1;
do {
pos1 = nextNonWhiteSpace(value, pos1);
if (pos1 == -1) {
break;
}
pos2 = nextWhiteSpace(value, pos1);
if (pos2 == -1) {
pos2 = value.length();
}
String uri = value.substring(pos1, pos2);
scopes.addScope(uri);
pos1 = pos2;
} while (pos1 != -1);
}
/**
* Parses uri list and returns uri set.
*
* @return set of uris
* @throws XmlPullParserException
* @throws IOException
*/
public URISet nextUriSet() throws XmlPullParserException, IOException {
URISet uriSet = new URISet();
String value = nextText();
int pos1 = -1;
int pos2 = pos1;
do {
pos1 = nextNonWhiteSpace(value, pos1);
if (pos1 == -1) {
break;
}
pos2 = nextWhiteSpace(value, pos1);
if (pos2 == -1) {
pos2 = value.length();
}
String uri = value.substring(pos1, pos2);
uriSet.add(new URI(uri));
pos1 = pos2;
} while (pos1 != -1);
return uriSet;
}
public UnknownDataContainer nextGenericElement(UnknownDataContainer container) throws XmlPullParserException, IOException {
ElementParser parser = new ElementParser(this);
int attributeCount = parser.getAttributeCount();
if (attributeCount > 0) {
for (int i = 0; i < attributeCount; i++) {
container.addUnknownAttribute(new QName(parser.getAttributeName(i), parser.getAttributeNamespace(i)), parser.getAttributeValue(i));
}
}
while (nextTag() != END_TAG) {
String namespace = parser.getNamespace();
String name = parser.getName();
QName elementName = new QName(name, namespace);
Object result = chainHandler(elementName);
if (result != null) {
container.addUnknownElement(elementName, result);
}
}
return container;
}
public void handleUnknownAttributes(UnknownDataContainer container) {
int count = getAttributeCount();
for (int i = 0; i < count; i++) {
String namespace = getAttributeNamespace(i);
String name = getAttributeName(i);
String value = getAttributeValue(i);
container.addUnknownAttribute(new QName(name, namespace), value);
}
}
/**
* @param container
* @param namespace
* @param name
* @throws XmlPullParserException
* @throws IOException
*/
public void addUnknownElement(UnknownDataContainer container, String namespace, String name) throws XmlPullParserException, IOException {
QName childName = new QName(name, namespace);
Object value = chainHandler(childName);
if (value != null) {
container.addUnknownElement(childName, value);
}
}
/**
* Equivalent to {@link #chainHandler(QName, boolean)
* chainHandler(elementName, true)}.
*
* @param elementName
* @return the representation of the parsed element block or
* <code>null</code>
* @throws XmlPullParserException
* @throws IOException
*/
public Object chainHandler(QName elementName) throws XmlPullParserException, IOException {
return chainHandler(elementName, true);
}
/**
* Searches for a registered {@link ElementHandler} capable of processing
* the element with the specified qualified name <code>elementName</code>.
* If it finds one, the XML stream represented by this {@link ElementParser}
* instance is passed to it and this method returns the result of its
* {@link ElementHandler#handleElement(QName, ElementParser)} method.
* Otherwise, if the flag <code>consume</code> is set to <code>true</code>,
* the complete XML block from the current element's start tag to the
* corresponding end tag is silently ignored and this method returns
* <code>null</code>. Finally, if <code>consume</code> is <code>false</code>
* , this method returns <code>null</code> immediately (without advancing
* this parser's state).
*
* @param elementName the name of the element to process
* @param consume whether to consume the entire element block in case there
* is no registered {@link ElementHandler element handler} for it
* @return the representation of the parsed element block or
* <code>null</code>
* @throws XmlPullParserException
* @throws IOException
*/
public Object chainHandler(QName elementName, boolean consume) throws XmlPullParserException, IOException {
ElementParser parser = null;
ElementHandler handler = ElementHandlerRegistry.getRegistry().getElementHandler(elementName);
if (handler != null) {
parser = new ElementParser(this);
Object result = handler.handleElement(elementName, parser);
while (!parser.finished) {
parser.next();
}
return result;
}
if (consume) {
parser = new ElementParser(this);
parser.consume();
}
return null;
}
/**
* Advances this element parser instance to its end.
*
* @throws XmlPullParserException
* @throws IOException
*/
public void consume() throws XmlPullParserException, IOException {
// advance parser to corresponding closing tag
int event = getEventType();
while (event != END_TAG || source.getDepth() > elementDepth) {
event = next();
}
}
/**
* Creates a qualified name from its prefixed string representation. Takes
* care about resolving the namespace prefix.
*
* @param rawQName a string representation of the qualified name in prefixed
* form
* @return the qualified name including its correct namespace
*/
public QName createQName(String rawQName) {
int idx = rawQName.indexOf(':');
if (idx == -1) {
idx = rawQName.lastIndexOf('/');
if (idx == -1) {
return new QName(rawQName, getNamespace(null));
}
String localPart = rawQName.substring(idx + 1);
String namespace = rawQName.substring(0, idx);
return new QName(localPart, namespace);
}
String localPart = rawQName.substring(idx + 1);
String namespacePrefix = rawQName.substring(0, idx);
return new QName(localPart, getNamespace(namespacePrefix));
}
private void checkFinished() throws XmlPullParserException {
// intercept element end at same depth as elementDepth
int current = source.getEventType();
if (current == END_TAG && source.getDepth() == elementDepth) {
finished = true;
}
}
}