/*
* Created on Jun 8, 2005
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* Copyright @2005 the original author or authors.
*/
package org.springmodules.remoting.xmlrpc.stax;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;
import org.springmodules.remoting.xmlrpc.XmlRpcInvalidPayloadException;
import org.springmodules.remoting.xmlrpc.XmlRpcParsingException;
import org.springmodules.remoting.xmlrpc.support.XmlRpcArray;
import org.springmodules.remoting.xmlrpc.support.XmlRpcBase64;
import org.springmodules.remoting.xmlrpc.support.XmlRpcBoolean;
import org.springmodules.remoting.xmlrpc.support.XmlRpcDateTime;
import org.springmodules.remoting.xmlrpc.support.XmlRpcDouble;
import org.springmodules.remoting.xmlrpc.support.XmlRpcElement;
import org.springmodules.remoting.xmlrpc.support.XmlRpcElementNames;
import org.springmodules.remoting.xmlrpc.support.XmlRpcInteger;
import org.springmodules.remoting.xmlrpc.support.XmlRpcString;
import org.springmodules.remoting.xmlrpc.support.XmlRpcStruct;
import org.springmodules.remoting.xmlrpc.support.XmlRpcStruct.XmlRpcMember;
import org.springmodules.remoting.xmlrpc.util.XmlRpcParsingUtils;
/**
* <p>
* Template for XML-RPC request/response parsers that use StAX.
* </p>
*
* @author Alex Ruiz
*
* @version $Revision$ $Date$
*/
public abstract class AbstractStaxXmlRpcParser {
protected final Log logger = LogFactory.getLog(getClass());
public AbstractStaxXmlRpcParser() {
super();
}
/**
* Creates a new XML stream reader from the given InputStream.
*
* @param inputStream
* the InputStream used as source.
* @return the created XML stream reader.
* @throws XmlRpcParsingException
* if there are any errors during the parsing.
*/
protected final XMLStreamReader loadXmlReader(InputStream inputStream)
throws XMLStreamException {
XMLInputFactory factory = XMLInputFactory.newInstance();
if (logger.isDebugEnabled()) {
logger.debug("Using StAX implementation [" + factory + "]");
}
XMLStreamReader reader = factory.createXMLStreamReader(inputStream);
return reader;
}
/**
* Creates a new <code>java.util.List</code> from the current element being
* read in the specified <code>StreamReader</code>.
*
* @param reader
* the <code>StreamReader</code>.
* @return the new array of <code>java.util.List</code>s.
* @throws XmlRpcInvalidPayloadException
* if the element contains an unknown child. Only one "data" element
* is allowed inside an "array" element.
* @see #parseDataElement(XMLStreamReader)
*/
protected final XmlRpcArray parseArrayElement(XMLStreamReader reader)
throws XMLStreamException {
while (reader.hasNext()) {
int event = reader.next();
switch (event) {
case XMLStreamConstants.START_ELEMENT:
String localName = reader.getLocalName();
if (XmlRpcElementNames.DATA.equals(localName)) {
return parseDataElement(reader);
}
XmlRpcParsingUtils.handleUnexpectedElementFound(localName);
}
}
// we should not reach this point.
return null;
}
/**
* Creates a new <code>java.util.List</code> from the current element being
* read in the specified <code>StreamReader</code>.
*
* @param reader
* the <code>StreamReader</code>.
* @return the new array of <code>java.util.List</code>s.
* @see #parseValueElement(XMLStreamReader)
*/
protected final XmlRpcArray parseDataElement(XMLStreamReader reader)
throws XMLStreamException {
XmlRpcArray array = new XmlRpcArray();
while (reader.hasNext()) {
int event = reader.next();
String localName = null;
switch (event) {
case XMLStreamConstants.START_ELEMENT:
localName = reader.getLocalName();
if (XmlRpcElementNames.VALUE.equals(localName)) {
XmlRpcElement element = parseValueElement(reader);
array.add(element);
}
break;
case XMLStreamConstants.END_ELEMENT:
localName = reader.getLocalName();
if (XmlRpcElementNames.DATA.equals(localName)
|| XmlRpcElementNames.ARRAY.equals(localName)) {
return array;
}
}
}
// we should not reach this point.
return null;
}
/**
* Creates a new Object from the current element being read in the specified
* <code>StreamReader</code>.
*
* @param reader
* the <code>StreamReader</code>.
* @return the created Object.
* @throws XmlRpcInvalidPayloadException
* if the element contains an unknown child.
* @see #parseValueElement(XMLStreamReader)
*/
protected final XmlRpcElement parseParameterElement(XMLStreamReader reader)
throws XMLStreamException {
while (reader.hasNext()) {
int event = reader.next();
switch (event) {
case XMLStreamConstants.START_ELEMENT:
String localName = reader.getLocalName();
if (XmlRpcElementNames.VALUE.equals(localName)) {
return parseValueElement(reader);
}
XmlRpcParsingUtils.handleUnexpectedElementFound(localName);
}
}
// we should not reach this point.
return null;
}
/**
* Parses the given <code>XMLStreamReader</code> containing parameters for a
* XML-RPC request/response.
*
* @param reader
* the <code>StreamReader</code>.
* @return the parameters of the XML-RPC request/response.
*/
protected final XmlRpcElement[] parseParametersElement(XMLStreamReader reader)
throws XMLStreamException {
List parameters = new ArrayList();
while (reader.hasNext()) {
int event = reader.next();
String localName = null;
switch (event) {
case XMLStreamConstants.START_ELEMENT:
localName = reader.getLocalName();
if (XmlRpcElementNames.PARAM.equals(localName)) {
XmlRpcElement parameter = parseParameterElement(reader);
parameters.add(parameter);
} else {
XmlRpcParsingUtils.handleUnexpectedElementFound(localName);
}
break;
case XMLStreamConstants.END_ELEMENT:
localName = reader.getLocalName();
if (XmlRpcElementNames.PARAMS.equals(localName)) {
return (XmlRpcElement[]) parameters
.toArray(new XmlRpcElement[parameters.size()]);
}
}
}
// we should not reach this point.
return null;
}
/**
* Creates a new <code>java.util.Map</code> from the current element being
* read in the specified <code>StreamReader</code>.
*
* @param reader
* the <code>StreamReader</code>.
* @return the new array of <code>java.util.Map</code>s.
* @see #parseMemberElement(XMLStreamReader)
*/
protected final XmlRpcStruct parseStructElement(XMLStreamReader reader)
throws XMLStreamException {
XmlRpcStruct struct = new XmlRpcStruct();
while (reader.hasNext()) {
int event = reader.next();
String localName = null;
switch (event) {
case XMLStreamConstants.START_ELEMENT:
localName = reader.getLocalName();
if (XmlRpcElementNames.MEMBER.equals(localName)) {
XmlRpcMember member = parseMemberElement(reader);
struct.add(member);
}
break;
case XMLStreamConstants.END_ELEMENT:
localName = reader.getLocalName();
if (XmlRpcElementNames.STRUCT.equals(localName)) {
return struct;
}
}
}
// we should not reach this point.
return null;
}
/**
* Creates a new <code>StructMember</code> from the current element being
* read in the specified <code>StreamReader</code>.
*
* @param reader
* the <code>StreamReader</code>.
* @return the new <code>StructMember</code>.
* @throws XmlRpcInvalidPayloadException
* if the element contains an unknown child. Only one "name" element
* and one "value" element are allowed inside an "member" element.
* @see #parseValueElement(XMLStreamReader)
*/
protected final XmlRpcMember parseMemberElement(XMLStreamReader reader)
throws XMLStreamException {
String name = null;
XmlRpcElement value = null;
while (reader.hasNext()) {
int event = reader.next();
String localName = null;
switch (event) {
case XMLStreamConstants.START_ELEMENT:
localName = reader.getLocalName();
if (XmlRpcElementNames.NAME.equals(localName)) {
name = reader.getElementText();
} else if (XmlRpcElementNames.VALUE.equals(localName)) {
value = parseValueElement(reader);
} else {
XmlRpcParsingUtils.handleUnexpectedElementFound(localName);
}
break;
case XMLStreamConstants.END_ELEMENT:
localName = reader.getLocalName();
if (XmlRpcElementNames.MEMBER.equals(localName)) {
if (!StringUtils.hasText(name)) {
throw new XmlRpcInvalidPayloadException(
"The struct member should have a name");
}
return new XmlRpcMember(name, value);
}
}
}
// we should never reach this point.
return null;
}
/**
* Creates a new Object from the current element being read in the specified
* <code>StreamReader</code>.
*
* @param reader
* the <code>StreamReader</code>.
* @return the created Object.
* @throws XmlRpcInvalidPayloadException
* if the element contains an unknown child.
* @see #parseArrayElement(XMLStreamReader)
* @see #parseStructElement(XMLStreamReader)
*/
protected final XmlRpcElement parseValueElement(XMLStreamReader reader)
throws XMLStreamException {
while (reader.hasNext()) {
int event = reader.next();
String localName = null;
switch (event) {
case XMLStreamConstants.START_ELEMENT:
localName = reader.getLocalName();
if (XmlRpcElementNames.ARRAY.equals(localName)) {
return parseArrayElement(reader);
} else if (XmlRpcElementNames.BASE_64.equals(localName)) {
String source = reader.getElementText();
return new XmlRpcBase64(source);
} else if (XmlRpcElementNames.BOOLEAN.equals(localName)) {
String source = reader.getElementText();
return new XmlRpcBoolean(source);
} else if (XmlRpcElementNames.DATE_TIME.equals(localName)) {
String source = reader.getElementText();
return new XmlRpcDateTime(source);
} else if (XmlRpcElementNames.DOUBLE.equals(localName)) {
String source = reader.getElementText();
return new XmlRpcDouble(source);
} else if (XmlRpcElementNames.I4.equals(localName)
|| XmlRpcElementNames.INT.equals(localName)) {
String source = reader.getElementText();
return new XmlRpcInteger(source);
} else if (XmlRpcElementNames.STRING.equals(localName)
|| XmlRpcElementNames.INT.equals(localName)) {
String source = reader.getElementText();
return new XmlRpcString(source);
} else if (XmlRpcElementNames.STRUCT.equals(localName)) {
return parseStructElement(reader);
} else {
XmlRpcParsingUtils.handleUnexpectedElementFound(localName);
}
case XMLStreamConstants.CHARACTERS:
String source = reader.getText();
return new XmlRpcString(source);
}
}
// we should not reach this point.
return null;
}
}