/**************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved. *
* http://esper.codehaus.org *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package com.espertech.esper.event.xml;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.PropertyAccessException;
import com.espertech.esper.client.EventPropertyGetter;
import com.espertech.esper.util.JavaClassHelper;
import com.espertech.esper.util.SimpleTypeParser;
import com.espertech.esper.util.SimpleTypeParserFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.namespace.QName;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathConstants;
import java.lang.reflect.Array;
/**
* Getter for properties of DOM xml events.
*
* @author pablo
*/
public class XPathPropertyGetter implements EventPropertyGetter
{
private static final Log log = LogFactory.getLog(XPathPropertyGetter.class);
private final XPathExpression expression;
private final String expressionText;
private final String property;
private final QName resultType;
private final SimpleTypeParser simpleTypeParser;
private final Class optionalCastToType;
private final boolean isCastToArray;
private final FragmentFactory fragmentFactory;
/**
* Ctor.
* @param propertyName is the name of the event property for which this getter gets values
* @param expressionText is the property expression itself
* @param xPathExpression is a compile XPath expression
* @param resultType is the resulting type
* @param optionalCastToType if non-null then the return value of the xpath expression is cast to this value
* @param fragmentFactory for creating fragments, or null in none to be created
*/
public XPathPropertyGetter(String propertyName, String expressionText, XPathExpression xPathExpression, QName resultType, Class optionalCastToType, FragmentFactory fragmentFactory) {
this.expression = xPathExpression;
this.expressionText = expressionText;
this.property = propertyName;
this.resultType = resultType;
this.fragmentFactory = fragmentFactory;
if ((optionalCastToType != null) && (optionalCastToType.isArray()))
{
isCastToArray = true;
if (!resultType.equals(XPathConstants.NODESET))
{
throw new IllegalArgumentException("Array cast-to types require XPathConstants.NODESET as the XPath result type");
}
optionalCastToType = optionalCastToType.getComponentType();
}
else
{
isCastToArray = false;
}
if (optionalCastToType != null)
{
simpleTypeParser = SimpleTypeParserFactory.getParser(optionalCastToType);
}
else
{
simpleTypeParser = null;
}
if (optionalCastToType == Node.class)
{
this.optionalCastToType = null;
}
else
{
this.optionalCastToType = optionalCastToType;
}
}
public Object get(EventBean eventBean) throws PropertyAccessException {
Object und = eventBean.getUnderlying();
if (und == null)
{
throw new PropertyAccessException("Unexpected null underlying event encountered, expecting org.w3c.dom.Node instance as underlying");
}
if (!(und instanceof Node))
{
throw new PropertyAccessException("Unexpected underlying event of type '" + und.getClass() + "' encountered, expecting org.w3c.dom.Node as underlying");
}
try {
if (log.isDebugEnabled())
{
log.debug("Running XPath '" + expressionText + "' for property '" + property + "' against Node XML :");
}
// if there is no parser, return xpath expression type
if (optionalCastToType == null)
{
return expression.evaluate(und,resultType);
}
// obtain result
Object result = expression.evaluate(und,resultType);
if (result == null)
{
return null;
}
if (isCastToArray)
{
return castToArray(result);
}
// string results get parsed
if (result instanceof String)
{
try
{
return simpleTypeParser.parse(result.toString());
}
catch (RuntimeException ex)
{
log.warn("Error parsing XPath property named '" + property + "' expression result '" + result + " as type " + optionalCastToType.getName());
return null;
}
}
// coercion
if (result instanceof Double)
{
try
{
return JavaClassHelper.coerceBoxed((Number)result, optionalCastToType);
}
catch (RuntimeException ex)
{
log.warn("Error coercing XPath property named '" + property + "' expression result '" + result + " as type " + optionalCastToType.getName());
return null;
}
}
// check boolean type
if (result instanceof Boolean)
{
if (optionalCastToType != Boolean.class)
{
log.warn("Error coercing XPath property named '" + property + "' expression result '" + result + " as type " + optionalCastToType.getName());
return null;
}
return result;
}
log.warn("Error processing XPath property named '" + property + "' expression result '" + result + ", not a known type");
return null;
}
catch (XPathExpressionException e) {
throw new PropertyAccessException("Error getting property " + property,e);
}
}
public boolean isExistsProperty(EventBean eventBean)
{
return true; // Property exists as the property is not dynamic (unchecked)
}
public Object getFragment(EventBean eventBean)
{
if (fragmentFactory == null)
{
return null;
}
Object und = eventBean.getUnderlying();
if (und == null)
{
throw new PropertyAccessException("Unexpected null underlying event encountered, expecting org.w3c.dom.Node instance as underlying");
}
if (!(und instanceof Node))
{
throw new PropertyAccessException("Unexpected underlying event of type '" + und.getClass() + "' encountered, expecting org.w3c.dom.Node as underlying");
}
try {
if (log.isDebugEnabled())
{
log.debug("Running XPath '" + expressionText + "' for property '" + property + "' against Node XML :");
}
Object result = expression.evaluate(und,resultType);
if (result instanceof Node)
{
return fragmentFactory.getEvent((Node) result);
}
if (result instanceof NodeList)
{
NodeList nodeList = (NodeList) result;
EventBean[] events = new EventBean[nodeList.getLength()];
for (int i = 0; i < events.length; i++)
{
events[i] = fragmentFactory.getEvent(nodeList.item(i));
}
return events;
}
log.warn("Error processing XPath property named '" + property + "' expression result is not of type Node or Nodeset");
return null;
}
catch (XPathExpressionException e) {
throw new PropertyAccessException("Error getting property " + property,e);
}
}
private Object castToArray(Object result)
{
if (!(result instanceof NodeList))
{
return null;
}
NodeList nodeList = (NodeList) result;
Object array = Array.newInstance(optionalCastToType, nodeList.getLength());
for (int i = 0; i < nodeList.getLength(); i++)
{
Object arrayItem = null;
try
{
Node item = nodeList.item(i);
String textContent;
if ((item.getNodeType() == Node.ATTRIBUTE_NODE) || (item.getNodeType() == Node.ELEMENT_NODE))
{
textContent = nodeList.item(i).getTextContent();
}
else
{
continue;
}
arrayItem = simpleTypeParser.parse(textContent);
}
catch (Exception ex)
{
if (log.isInfoEnabled())
{
log.info("Parse error for text content " + nodeList.item(i).getTextContent() + " for expression " + expression);
}
}
Array.set(array, i, arrayItem);
}
return array;
}
}