org.xins.client.XINSCallResultParser Maven / Gradle / Ivy
/*
* $Id: XINSCallResultParser.java,v 1.67 2008/07/04 10:22:49 agoubard Exp $
*
* Copyright 2003-2008 Online Breedband B.V.
* See the COPYRIGHT file for redistribution and use restrictions.
*/
package org.xins.client;
import java.io.ByteArrayInputStream;
import java.util.Iterator;
import org.xins.common.MandatoryArgumentChecker;
import org.xins.common.Utils;
import org.xins.common.collections.BasicPropertyReader;
import org.xins.common.collections.PropertyReader;
import org.xins.common.text.ParseException;
import org.xins.common.text.TextUtils;
import org.xins.common.xml.Element;
import org.xins.common.xml.ElementParser;
/**
* XINS call result parser. XML is parsed to produce a {@link XINSCallResult}
* object.
*
* The root element in the XML must be of type result
. Inside
* this element, param
elements optionally define parameters and
* an optional data
element defines a data section.
*
*
If the result element contains an errorcode
or a
* code
attribute, then the value of the attribute is interpreted
* as the error code. If both these attributes are set and conflicting, then
* this is considered a showstopper.
*
*
TODO: Describe rest of parse process.
*
*
Note: This parser is
* XML Namespaces-aware.
*
* @version $Revision: 1.67 $ $Date: 2008/07/04 10:22:49 $
*
* @author Anthony Goubard
* @author Ernst de Haan
*
* @since XINS 1.0.0
*/
public class XINSCallResultParser {
/**
* The parser used to parse the XML.
*/
private final ElementParser _parser;
/**
* Constructs a new XINSCallResultParser
.
*/
public XINSCallResultParser() {
_parser = new ElementParser();
}
/**
* Parses the given XML string to create a XINSCallResultData
* object.
*
* @param xml
* the XML to be parsed, not null
.
*
* @return
* the parsed result of the call, not null
.
*
* @throws IllegalArgumentException
* if xml == null
.
*
* @throws ParseException
* if the specified string is not valid XML or if it is not a valid XINS
* API function call result.
*/
public XINSCallResultData parse(byte[] xml)
throws IllegalArgumentException, ParseException {
// Check preconditions
MandatoryArgumentChecker.check("xml", xml);
ByteArrayInputStream stream = null;
try {
// Convert the byte array to an input stream
stream = new ByteArrayInputStream(xml);
Element resultElement = _parser.parse(stream);
return new XINSCallResultDataImpl(resultElement);
} catch (Throwable exception) {
// Log: Parsing failed
String detail = exception.getMessage();
Log.log_2205(exception, detail);
// Include the exception message in our error message, if any
String message = "Unable to convert the specified string to XML.";
if (detail != null) {
detail = detail.trim();
if (detail.length() > 0) {
message = "Unable to convert the specified string to XML: " + detail;
}
}
// Throw exception with message, and register cause exception
throw new ParseException(message, exception, detail);
// Always dispose the ByteArrayInputStream
} finally {
if (stream != null) {
try {
stream.close();
} catch (Throwable exception) {
Utils.logProgrammingError(exception);
}
}
}
}
/**
* SAX event handler that will parse the result from a call to a XINS
* service.
*
* @version $Revision: 1.67 $ $Date: 2008/07/04 10:22:49 $
* @author Anthony Goubard
* @author Ernst de Haan
*/
private static class XINSCallResultDataImpl implements XINSCallResultData {
/**
* The error code returned by the function or null
, if no
* error code is returned.
*
*
The value will never return an empty string, so if the result is
* not null
, then it is safe to assume the length of the
* string is at least 1 character.
*/
private String _errorCode;
/**
* The list of the parameters (name/value) returned by the function.
* This field is null
if there is no output parameters returned.
*/
private BasicPropertyReader _parameters;
/**
* The data section of the result, can be null
.
*/
private Element _dataSection;
/**
* Constructs a new XINSCallResultDataImpl
instance.
*
* @param resultElement
* the parsed result, cannot be null
.
*
* @throws ParseException
* if the parse XML does not match the XINS protocol.
*/
private XINSCallResultDataImpl(Element resultElement) throws ParseException {
if (!"result".equals(resultElement.getLocalName())) {
String detail = "Incorrect root element '" + resultElement.getLocalName() + "'. Excpected 'result'.";
throw new ParseException(detail);
}
if (resultElement.getNamespaceURI() != null) {
String detail = "No namespace is allowed for the 'result' element. The namespace used is '" +
resultElement.getNamespaceURI() + "'.";
throw new ParseException(detail);
}
if (resultElement.getText() != null && !resultElement.getText().trim().equals("")) {
String detail = "No PCDATA is allowed for the 'result' element. The PCDATA returned is '" +
resultElement.getText() + "'.";
throw new ParseException(detail);
}
// Get and check the error code if any.
_errorCode = resultElement.getAttribute("errorcode");
String oldErrorCode = resultElement.getAttribute("code");
if (TextUtils.isEmpty(_errorCode) && !TextUtils.isEmpty(oldErrorCode)) {
_errorCode = oldErrorCode;
}
if (!TextUtils.isEmpty(_errorCode) && !TextUtils.isEmpty(oldErrorCode) && !_errorCode.equals(oldErrorCode)) {
// NOTE: No need to log here. This will be logged already in
// Logdoc log message 2205.
String detail = "Found conflicting duplicate value for the "
+ "error code, since attribute errorcode=\"" + _errorCode
+ "\", while attribute code=\"" + oldErrorCode + "\".";
throw new ParseException(detail);
}
// Get and check the parameters, if any.
Iterator itParamElements = resultElement.getChildElements("param").iterator();
while (itParamElements.hasNext()) {
Element nextParam = (Element) itParamElements.next();
String paramName = nextParam.getAttribute("name");
if (TextUtils.isEmpty(paramName)) {
throw new ParseException("No parameter name specified for a parameter: " + nextParam.toString());
}
String paramValue = nextParam.getText();
if (_parameters != null && _parameters.get(paramName) != null &&
!_parameters.get(paramName).equals(paramValue)) {
String detail = "Duplicate output parameter '" + paramName +
"'with different values: '" + _parameters.get(paramName) +
"' and '" + paramValue + "'.";
throw new ParseException(detail);
}
if (!TextUtils.isEmpty(paramValue) && nextParam.getNamespaceURI() == null) {
if (_parameters == null) {
_parameters = new BasicPropertyReader();
}
_parameters.set(paramName, paramValue);
}
}
// Get the data section, if any.
if (resultElement.getChildElements("data").size() > 0) {
_dataSection = resultElement.getUniqueChildElement("data");
}
}
/**
* Returns the error code. If null
is returned the call was
* successful and thus no error code was returned. Otherwise the call
* was unsuccessful.
*
*
This method will never return an empty string, so if the result is
* not null
, then it is safe to assume the length of the
* string is at least 1 character.
*
* @return
* the returned error code, or null
if the call was
* successful.
*/
public String getErrorCode() {
return _errorCode;
}
/**
* Get the parameters returned by the function.
*
* @return
* the parameters (name/value) or null
if the function
* does not have any parameters.
*/
public PropertyReader getParameters() {
return _parameters;
}
/**
* Get the data element returned by the function if any.
*
* @return
* the data element, or null
if the function did not
* return any data element.
*/
public Element getDataElement() {
return _dataSection;
}
}
}