com.espertech.esper.event.property.PropertyParser.java Source code

Java tutorial

Introduction

Here is the source code for com.espertech.esper.event.property.PropertyParser.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.property;

import com.espertech.esper.antlr.NoCaseSensitiveStream;
import com.espertech.esper.client.PropertyAccessException;
import com.espertech.esper.epl.generated.EsperEPL2GrammarLexer;
import com.espertech.esper.epl.generated.EsperEPL2GrammarParser;
import com.espertech.esper.epl.parse.ExceptionConvertor;
import com.espertech.esper.type.IntValue;
import com.espertech.esper.type.StringValue;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.Tree;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * Parser for property names that can be simple, nested, mapped or a combination of these.
 * Uses ANTLR parser to parse.
 */
public class PropertyParser {
    private static final Log log = LogFactory.getLog(PropertyParser.class);

    private static Set<String> keywordCache;

    /**
     * Parse the given property name returning a Property instance for the property.
     * @param propertyName is the property name to parse
     * @param isRootedDynamic is true to indicate that the property is already rooted in a dynamic
     * property and therefore all child properties should be dynamic properties as well
     * @return Property instance for property
     */
    public static Property parse(String propertyName, boolean isRootedDynamic) {
        Tree tree = parse(propertyName);

        if (tree.getChildCount() == 1) {
            return makeProperty(tree.getChild(0), isRootedDynamic);
        }

        List<Property> properties = new LinkedList<Property>();
        boolean isRootedInDynamic = isRootedDynamic;
        for (int i = 0; i < tree.getChildCount(); i++) {
            Tree child = tree.getChild(i);

            Property property = makeProperty(child, isRootedInDynamic);
            if (property instanceof DynamicSimpleProperty) {
                isRootedInDynamic = true;
            }
            properties.add(property);
        }

        return new NestedProperty(properties);
    }

    /**
     * Parses a given property name returning an AST.
     * @param propertyName to parse
     * @return AST syntax tree
     */
    public static Tree parse(String propertyName) {
        CharStream input;
        try {
            input = new NoCaseSensitiveStream(new StringReader(propertyName));
        } catch (IOException ex) {
            throw new PropertyAccessException("IOException parsing property name '" + propertyName + '\'', ex);
        }

        EsperEPL2GrammarLexer lex = new EsperEPL2GrammarLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lex);
        EsperEPL2GrammarParser g = new EsperEPL2GrammarParser(tokens);
        EsperEPL2GrammarParser.startEventPropertyRule_return r;

        try {
            r = g.startEventPropertyRule();
        } catch (RuntimeException e) {
            if (log.isDebugEnabled()) {
                log.debug("Error parsing property expression [" + propertyName + "]", e);
            }
            if (e.getCause() instanceof RecognitionException) {
                throw ExceptionConvertor.convertProperty((RecognitionException) e.getCause(), propertyName, true,
                        g);
            } else {
                throw e;
            }
        } catch (RecognitionException e) {
            // Check for keywords and escape each, parse again
            String escapedPropertyName = escapeKeywords(tokens);

            CharStream inputEscaped;
            try {
                inputEscaped = new NoCaseSensitiveStream(new StringReader(escapedPropertyName));
            } catch (IOException ex) {
                throw new PropertyAccessException("IOException parsing property name '" + propertyName + '\'', ex);
            }

            EsperEPL2GrammarLexer lexEscaped = new EsperEPL2GrammarLexer(inputEscaped);
            CommonTokenStream tokensEscaped = new CommonTokenStream(lexEscaped);
            EsperEPL2GrammarParser gEscaped = new EsperEPL2GrammarParser(tokensEscaped);
            EsperEPL2GrammarParser.startEventPropertyRule_return rEscaped;

            try {
                rEscaped = gEscaped.startEventPropertyRule();
                return (Tree) rEscaped.getTree();
            } catch (Exception eEscaped) {
            }

            throw ExceptionConvertor.convertProperty(e, propertyName, true, g);
        }

        return (Tree) r.getTree();
    }

    private synchronized static String escapeKeywords(CommonTokenStream tokens) {

        if (keywordCache == null) {
            keywordCache = new HashSet<String>();
            Set<String> keywords = new EsperEPL2GrammarParser(tokens).getKeywords();
            for (String keyword : keywords) {
                if (keyword.charAt(0) == '\'' && keyword.charAt(keyword.length() - 1) == '\'') {
                    keywordCache.add(keyword.substring(1, keyword.length() - 1));
                }
            }
        }

        StringWriter writer = new StringWriter();
        for (Object token : tokens.getTokens()) // Call getTokens first before invoking tokens.size! ANTLR problem
        {
            Token t = (Token) token;
            boolean isKeyword = keywordCache.contains(t.getText().toLowerCase());
            if (isKeyword) {
                writer.append('`');
                writer.append(t.getText());
                writer.append('`');
            } else {
                writer.append(t.getText());
            }
        }
        return writer.toString();
    }

    /**
     * Returns true if the property is a dynamic property.
     * @param ast property ast
     * @return dynamic or not
     */
    public static boolean isPropertyDynamic(Tree ast) {
        for (int i = 0; i < ast.getChildCount(); i++) {
            int type = ast.getChild(i).getType();
            if ((type == EsperEPL2GrammarParser.EVENT_PROP_DYNAMIC_SIMPLE)
                    || (type == EsperEPL2GrammarParser.EVENT_PROP_DYNAMIC_INDEXED)
                    || (type == EsperEPL2GrammarParser.EVENT_PROP_DYNAMIC_MAPPED)) {
                return true;
            }
        }
        return false;
    }

    private static Property makeProperty(Tree child, boolean isRootedInDynamic) {
        switch (child.getType()) {
        case EsperEPL2GrammarParser.EVENT_PROP_SIMPLE:
            if (!isRootedInDynamic) {
                return new SimpleProperty(child.getChild(0).getText());
            } else {
                return new DynamicSimpleProperty(child.getChild(0).getText());
            }
        case EsperEPL2GrammarParser.EVENT_PROP_MAPPED:
            String key = StringValue.parseString(child.getChild(1).getText());
            if (!isRootedInDynamic) {
                return new MappedProperty(child.getChild(0).getText(), key);
            } else {
                return new DynamicMappedProperty(child.getChild(0).getText(), key);
            }
        case EsperEPL2GrammarParser.EVENT_PROP_INDEXED:
            int index = IntValue.parseString(child.getChild(1).getText());
            if (!isRootedInDynamic) {
                return new IndexedProperty(child.getChild(0).getText(), index);
            } else {
                return new DynamicIndexedProperty(child.getChild(0).getText(), index);
            }
        case EsperEPL2GrammarParser.EVENT_PROP_DYNAMIC_SIMPLE:
            return new DynamicSimpleProperty(child.getChild(0).getText());
        case EsperEPL2GrammarParser.EVENT_PROP_DYNAMIC_INDEXED:
            index = IntValue.parseString(child.getChild(1).getText());
            return new DynamicIndexedProperty(child.getChild(0).getText(), index);
        case EsperEPL2GrammarParser.EVENT_PROP_DYNAMIC_MAPPED:
            key = StringValue.parseString(child.getChild(1).getText());
            return new DynamicMappedProperty(child.getChild(0).getText(), key);
        default:
            throw new IllegalStateException("Event property AST node not recognized, type=" + child.getType());
        }
    }

    public static String unescapeBacktick(String unescapedPropertyName) {
        if (unescapedPropertyName.startsWith("`") && unescapedPropertyName.endsWith("`")) {
            return unescapedPropertyName.substring(1, unescapedPropertyName.length() - 1);
        } else {
            return unescapedPropertyName;
        }
    }
}