/************************************************************************************** * 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; } }