com.espertech.esper.event.xml.SchemaXMLPropertyParser.java Source code

Java tutorial

Introduction

Here is the source code for com.espertech.esper.event.xml.SchemaXMLPropertyParser.java

Source

/**************************************************************************************
 * 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.EPException;
import com.espertech.esper.client.EventPropertyGetter;
import com.espertech.esper.client.PropertyAccessException;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.epl.generated.EsperEPL2GrammarParser;
import com.espertech.esper.event.EventAdapterService;
import com.espertech.esper.event.property.Property;
import com.espertech.esper.event.property.PropertyParser;
import com.espertech.esper.type.IntValue;
import com.espertech.esper.type.StringValue;
import com.espertech.esper.util.ExecutionPathDebugLog;
import org.antlr.runtime.tree.Tree;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Node;

import javax.xml.namespace.QName;
import javax.xml.xpath.*;
import java.util.List;

/**
 * Parses event property names and transforms to XPath expressions using the schema information supplied. Supports the
 * nested, indexed and mapped event properties.
 */
public class SchemaXMLPropertyParser {
    /**
     * Return the xPath corresponding to the given property.
     * The propertyName String may be simple, nested, indexed or mapped.
     *
     * @param propertyName is the event property name
     * @param namespace is the default namespace
     * @param schemaModel is the schema model
     * @param xPathFactory is the xpath factory instance to use
     * @param rootElementName is the name of the root element
     * @param eventAdapterService for type lookup and creation
     * @param xmlEventType the resolving type
     * @param isAllowFragment whether fragmenting is allowed
     * @param defaultNamespace default namespace
     * @return xpath expression
     * @throws EPException is there are XPath errors
     */
    public static EventPropertyGetter getXPathResolution(String propertyName, XPathFactory xPathFactory,
            String rootElementName, String namespace, SchemaModel schemaModel,
            EventAdapterService eventAdapterService, BaseXMLEventType xmlEventType, boolean isAllowFragment,
            String defaultNamespace) throws EPException {
        if (log.isDebugEnabled()) {
            log.debug("Determining XPath expression for property '" + propertyName + "'");
        }

        XPathNamespaceContext ctx = new XPathNamespaceContext();
        List<String> namespaces = schemaModel.getNamespaces();

        String defaultNamespacePrefix = null;
        for (int i = 0; i < namespaces.size(); i++) {
            String prefix = "n" + i;
            ctx.addPrefix(prefix, namespaces.get(i));
            if ((defaultNamespace != null) && (defaultNamespace.equals(namespaces.get(i)))) {
                defaultNamespacePrefix = prefix;
            }
        }

        Tree ast = PropertyParser.parse(propertyName);
        Property property = PropertyParser.parse(propertyName, false);
        boolean isDynamic = property.isDynamic();

        SchemaElementComplex rootComplexElement = SchemaUtil.findRootElement(schemaModel, namespace,
                rootElementName);
        String prefix = ctx.getPrefix(rootComplexElement.getNamespace());
        if (prefix == null) {
            prefix = "";
        } else {
            prefix += ':';
        }

        StringBuilder xPathBuf = new StringBuilder();
        xPathBuf.append('/');
        xPathBuf.append(prefix);
        if (rootElementName.startsWith("//")) {
            xPathBuf.append(rootElementName.substring(2));
        } else {
            xPathBuf.append(rootElementName);
        }

        SchemaElementComplex parentComplexElement = rootComplexElement;
        Pair<String, QName> pair = null;

        if (ast.getChildCount() == 1) {
            pair = makeProperty(rootComplexElement, ast.getChild(0), ctx, true, isDynamic, defaultNamespacePrefix);
            if (pair == null) {
                throw new PropertyAccessException("Failed to locate property '" + propertyName + "' in schema");
            }
            xPathBuf.append(pair.getFirst());
        } else {
            for (int i = 0; i < ast.getChildCount(); i++) {
                boolean isLast = (i == ast.getChildCount() - 1);
                Tree child = ast.getChild(i);
                pair = makeProperty(parentComplexElement, child, ctx, isLast, isDynamic, defaultNamespacePrefix);
                if (pair == null) {
                    throw new PropertyAccessException("Failed to locate property '" + propertyName
                            + "' nested property part '" + child.toString() + "' in schema");
                }

                String text = child.getChild(0).getText();
                SchemaItem obj = SchemaUtil.findPropertyMapping(parentComplexElement, text);
                if (obj instanceof SchemaElementComplex) {
                    parentComplexElement = (SchemaElementComplex) obj;
                }
                xPathBuf.append(pair.getFirst());
            }
        }

        String xPath = xPathBuf.toString();
        if ((ExecutionPathDebugLog.isDebugEnabled) && (log.isDebugEnabled())) {
            log.debug(".parse XPath for property '" + propertyName + "' is expression=" + xPath);
        }

        // Compile assembled XPath expression
        XPath path = xPathFactory.newXPath();
        path.setNamespaceContext(ctx);

        if (log.isDebugEnabled()) {
            log.debug("Compiling XPath expression '" + xPath + "' for property '" + propertyName
                    + "' using namespace context :" + ctx);
        }

        XPathExpression expr;
        try {
            expr = path.compile(xPath);
        } catch (XPathExpressionException e) {
            String detail = "Error constructing XPath expression from property expression '" + propertyName
                    + "' expression '" + xPath + "'";
            if (e.getMessage() != null) {
                throw new EPException(detail + " :" + e.getMessage(), e);
            }
            throw new EPException(detail, e);
        }

        // get type
        SchemaItem item = property.getPropertyTypeSchema(rootComplexElement, eventAdapterService);
        if ((item == null) && (!isDynamic)) {
            return null;
        }

        Class resultType;
        if (!isDynamic) {
            resultType = SchemaUtil.toReturnType(item);
        } else {
            resultType = Node.class;
        }

        FragmentFactory fragmentFactory = null;
        if (isAllowFragment) {
            fragmentFactory = new FragmentFactoryDOMGetter(eventAdapterService, xmlEventType, propertyName);
        }
        return new XPathPropertyGetter(propertyName, xPath, expr, pair.getSecond(), resultType, fragmentFactory);
    }

    private static Pair<String, QName> makeProperty(SchemaElementComplex parent, Tree child,
            XPathNamespaceContext ctx, boolean isLast, boolean isDynamic, String defaultNamespacePrefix) {
        String text = child.getChild(0).getText();
        SchemaItem obj = SchemaUtil.findPropertyMapping(parent, text);
        if ((obj instanceof SchemaElementSimple) || (obj instanceof SchemaElementComplex)) {
            return makeElementProperty((SchemaElement) obj, child, ctx, isLast, isDynamic, defaultNamespacePrefix);
        } else if (obj != null) {
            return makeAttributeProperty((SchemaItemAttribute) obj, child, ctx);
        } else if (isDynamic) {
            return makeElementProperty(null, child, ctx, isLast, isDynamic, defaultNamespacePrefix);
        } else {
            return null;
        }
    }

    private static Pair<String, QName> makeAttributeProperty(SchemaItemAttribute attribute, Tree child,
            XPathNamespaceContext ctx) {
        String prefix = ctx.getPrefix(attribute.getNamespace());
        if (prefix == null)
            prefix = "";
        else
            prefix += ':';
        switch (child.getType()) {
        case EsperEPL2GrammarParser.EVENT_PROP_DYNAMIC_SIMPLE:
        case EsperEPL2GrammarParser.EVENT_PROP_SIMPLE:
            QName type = SchemaUtil.simpleTypeToQName(attribute.getXsSimpleType());
            String path = "/@" + prefix + child.getChild(0).getText();
            return new Pair<String, QName>(path, type);
        case EsperEPL2GrammarParser.EVENT_PROP_DYNAMIC_MAPPED:
        case EsperEPL2GrammarParser.EVENT_PROP_MAPPED:
            throw new RuntimeException("Mapped properties not applicable to attributes");
        case EsperEPL2GrammarParser.EVENT_PROP_DYNAMIC_INDEXED:
        case EsperEPL2GrammarParser.EVENT_PROP_INDEXED:
            throw new RuntimeException("Mapped properties not applicable to attributes");
        default:
            throw new IllegalStateException("Event property AST node not recognized, type=" + child.getType());
        }
    }

    private static Pair<String, QName> makeElementProperty(SchemaElement schemaElement, Tree child,
            XPathNamespaceContext ctx, boolean isAlone, boolean isDynamic, String defaultNamespacePrefix) {
        QName type;
        if (isDynamic) {
            type = XPathConstants.NODE;
        } else if (schemaElement instanceof SchemaElementSimple) {
            type = SchemaUtil.simpleTypeToQName(((SchemaElementSimple) schemaElement).getXsSimpleType());
        } else {
            SchemaElementComplex complex = (SchemaElementComplex) schemaElement;
            if (complex.getOptionalSimpleType() != null) {
                type = SchemaUtil.simpleTypeToQName(complex.getOptionalSimpleType());
            } else {
                // The result is a node
                type = XPathConstants.NODE;
            }
        }

        String prefix;
        if (!isDynamic) {
            prefix = ctx.getPrefix(schemaElement.getNamespace());
        } else {
            prefix = defaultNamespacePrefix;
        }
        if (prefix == null) {
            prefix = "";
        } else {
            prefix += ':';
        }

        switch (child.getType()) {
        case EsperEPL2GrammarParser.EVENT_PROP_SIMPLE:
        case EsperEPL2GrammarParser.EVENT_PROP_DYNAMIC_SIMPLE:
            if (!isDynamic && schemaElement.isArray() && !isAlone) {
                throw new PropertyAccessException(
                        "Simple property not allowed in repeating elements at '" + schemaElement.getName() + "'");
            }
            return new Pair<String, QName>('/' + prefix + child.getChild(0).getText(), type);

        case EsperEPL2GrammarParser.EVENT_PROP_MAPPED:
        case EsperEPL2GrammarParser.EVENT_PROP_DYNAMIC_MAPPED:
            if (!isDynamic && !schemaElement.isArray()) {
                throw new PropertyAccessException("Element " + child.getChild(0).getText()
                        + " is not a collection, cannot be used as mapped property");
            }
            String key = StringValue.parseString(child.getChild(1).getText());
            return new Pair<String, QName>('/' + prefix + child.getChild(0).getText() + "[@id='" + key + "']",
                    type);

        case EsperEPL2GrammarParser.EVENT_PROP_INDEXED:
        case EsperEPL2GrammarParser.EVENT_PROP_DYNAMIC_INDEXED:
            if (!isDynamic && !schemaElement.isArray()) {
                throw new PropertyAccessException("Element " + child.getChild(0).getText()
                        + " is not a collection, cannot be used as mapped property");
            }
            int index = IntValue.parseString(child.getChild(1).getText());
            int xPathPosition = index + 1;
            return new Pair<String, QName>(
                    '/' + prefix + child.getChild(0).getText() + "[position() = " + xPathPosition + ']', type);

        default:
            throw new IllegalStateException("Event property AST node not recognized, type=" + child.getType());
        }
    }

    private static Log log = LogFactory.getLog(SchemaXMLPropertyParser.class);
}