Package org.apache.oodt.cas.filemgr.util

Source Code of org.apache.oodt.cas.filemgr.util.SqlParser

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.oodt.cas.filemgr.util;

//OODT imports
import org.apache.oodt.cas.filemgr.structs.BooleanQueryCriteria;
import org.apache.oodt.cas.filemgr.structs.QueryCriteria;
import org.apache.oodt.cas.filemgr.structs.RangeQueryCriteria;
import org.apache.oodt.cas.filemgr.structs.TermQueryCriteria;
import org.apache.oodt.cas.filemgr.structs.exceptions.QueryFormulationException;
import org.apache.oodt.cas.filemgr.structs.query.ComplexQuery;
import org.apache.oodt.cas.filemgr.structs.query.QueryFilter;
import org.apache.oodt.cas.filemgr.structs.query.filter.FilterAlgor;


//JDK imports
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
*
* @author bfoster
* @version $Revision$
*
*          <p>
*          A fairly robust SQL parser, based on the Shunting yard
*          algorithm
*          </p>
*         
*          <p>
*          Evaluates SQL like string statements contained in a string. The SQL
*          statement should be enclosed within: SQL ({@literal <sql_arguments>}
*          ) { {@literal <sql_statement>} . the {@literal <sql_arguments>} can
*          be either FORMAT, SORT_BY, or FILTER. Syntax: SQL (FORMAT='<metadata
*          formated output>',SORT_BY='<metadata name>', FILTER='<start_time
*          metadata element>, <end_time metadata element>, <priority metadata
*          element>, <filter type>') { SELECT
*          <list-of-comma-segregated-metadata-elements-to-query-on> FROM
*          <productTypes-comma-segregated> WHERE <metadata-boolean-expressions>
*          } Here is an example SQL statement: SQL (FORMAT='FileLocation/Filename',
*          SORT_BY='FileSize',FILTER=<StartDateTime,EndDateTime,DataVersion,
*          TakeHighestPriority) { SELECT FileLocation,Filename,FileSize FROM IASI_L1C
*          WHERE ProductionDateTime >= '2007-12-01T00:00:00.000000Z' } This example
*          would query the cas-filemgr for metadata values:
*          FileLocation,Filename,FileSize for any data file where the
*          ProductType == IASI_L1C and the ProductionDateTime >=
*          2007-12-01T00:00:00.000000Z. It would then combine the return data
*          files metadata via the specified FORMAT. Each data file's metadata
*          will be formated to a string representation of (with the actual
*          values replaced in the location of the metadata keys):
*          FileLocation/Filename. They will be concatenated together, in
*          FileSize order.
*          </p>
*/
public class SqlParser {
   
    private SqlParser() {
    }
   
    public static ComplexQuery parseSqlQueryMethod(String sqlStringQueryMethod)
            throws QueryFormulationException {
        if (!Pattern.matches("((?:SQL)|(?:sql))\\s*(.*)\\s*\\{\\s*SELECT.*FROM.*(?:WHERE.*){0,1}\\}", sqlStringQueryMethod))
            throw new QueryFormulationException("Malformed SQL method");
       
        try {
            ComplexQuery complexQuery = parseSqlQuery(stripOutSqlDefinition(sqlStringQueryMethod));
           
            for (Expression expr : getSqlStatementArgs(sqlStringQueryMethod)) {
                if (expr.getKey().toUpperCase().equals("FORMAT"))
                    complexQuery.setToStringResultFormat(expr.getValue());
                else if (expr.getKey().toUpperCase().equals("SORT_BY"))
                    complexQuery.setSortByMetKey(expr.getValue());
                else if (expr.getKey().toUpperCase().equals("FILTER"))
                    complexQuery.setQueryFilter(createFilter(expr));
            }
           
            return complexQuery;
        }catch (Exception e) {
            e.printStackTrace();
            throw new QueryFormulationException("Failed to parse SQL method : " + e.getMessage());
        }
    }
   
    public static ComplexQuery parseSqlQuery(String sqlStringQuery)
            throws QueryFormulationException {
        String[] splitSqlStatement = sqlStringQuery
                .split("((?:SELECT)|(?:FROM)|(?:WHERE))");
        String[] selectValues = (splitSqlStatement[1].trim() + ",").split(",");
        String[] fromValues = (splitSqlStatement[2].trim() + ",").split(",");
        ComplexQuery sq = new ComplexQuery();
        List<String> selectValuesList = Arrays.asList(selectValues);
        if (!selectValuesList.contains("*"))
            sq.setReducedMetadata(Arrays.asList(selectValues));
        List<String> fromValuesList = Arrays.asList(fromValues);
        if (!fromValuesList.contains("*"))
            sq.setReducedProductTypeNames(fromValuesList);
       
        if (splitSqlStatement.length > 3)
            sq.addCriterion(parseStatement(toPostFix(splitSqlStatement[3]
                    .trim())));
        return sq;
    }
   
    public static QueryCriteria parseSqlWhereClause(String sqlWhereClause)
            throws QueryFormulationException {
        return parseStatement(toPostFix(sqlWhereClause.trim()));
    }
   
    public static String unparseSqlQuery(ComplexQuery complexQuery) throws QueryFormulationException {
        LinkedList<String> outputArgs = new LinkedList<String>();
        if (complexQuery.getToStringResultFormat() != null)
            outputArgs.add("FORMAT = '" + complexQuery.getToStringResultFormat() + "'");
        if (complexQuery.getSortByMetKey() != null)
            outputArgs.add("SORT_BY = '" + complexQuery.getSortByMetKey() + "'");
        if (complexQuery.getQueryFilter() != null) {
            String filterString = "FILTER = '"
                    + complexQuery.getQueryFilter().getStartDateTimeMetKey() + ","
                    + complexQuery.getQueryFilter().getEndDateTimeMetKey() + ","
                    + complexQuery.getQueryFilter().getPriorityMetKey() + ","
                    + complexQuery.getQueryFilter().getFilterAlgor().getClass().getCanonicalName() + ","
                    + complexQuery.getQueryFilter().getFilterAlgor().getEpsilon();
            outputArgs.add(filterString + "'");
        }
        String sqlQueryString = getInfixCriteriaString(complexQuery.getCriteria());
        if (sqlQueryString != null && sqlQueryString.startsWith("(") && sqlQueryString.endsWith(")"))
                sqlQueryString = sqlQueryString.substring(1, sqlQueryString.length() - 1);
        return "SQL ("
                + listToString(outputArgs)
                + ") { SELECT " + listToString(complexQuery.getReducedMetadata())
                + " FROM " + (complexQuery.getReducedProductTypeNames() != null ? listToString(complexQuery.getReducedProductTypeNames()) : "*")
                + (sqlQueryString != null ? " WHERE " + sqlQueryString : "") + " }";
    }

    public static String getInfixCriteriaString(List<QueryCriteria> criteriaList) throws QueryFormulationException {
        if (criteriaList.size() > 1)
            return getInfixCriteriaString(new BooleanQueryCriteria(criteriaList, BooleanQueryCriteria.AND));
        else if (criteriaList.size() == 1)
            return getInfixCriteriaString(criteriaList.get(0));
        else
            return null;
    }
   
    public static String getInfixCriteriaString(QueryCriteria criteria) {
        String returnString = "";
        if (criteria instanceof BooleanQueryCriteria) {
            BooleanQueryCriteria bqc = (BooleanQueryCriteria) criteria;
            List<QueryCriteria> terms = bqc.getTerms();
            switch(bqc.getOperator()){
            case 0:
                returnString = "(" + getInfixCriteriaString((QueryCriteria) terms.get(0));
                for (int i = 1; i < terms.size(); i++)
                    returnString += " AND " + getInfixCriteriaString((QueryCriteria) terms.get(i));
                returnString += ")";
                break;
            case 1:
                returnString = "(" + getInfixCriteriaString((QueryCriteria) terms.get(0));
                for (int i = 1; i < terms.size(); i++)
                    returnString += " OR " + getInfixCriteriaString((QueryCriteria) terms.get(i));
                returnString += ")";
                break;
            case 2:
                QueryCriteria qc = bqc.getTerms().get(0);
                if (qc instanceof TermQueryCriteria) {
                    TermQueryCriteria tqc = (TermQueryCriteria) qc;
                    returnString = tqc.getElementName() + " != '" + tqc.getValue() + "'";
                }else {
                    returnString = "NOT(" + getInfixCriteriaString(qc) + ")";
                }
                break;
            }
        }else if (criteria instanceof RangeQueryCriteria) {
            RangeQueryCriteria rqc = (RangeQueryCriteria) criteria;
            String opString = rqc.getInclusive() ? "=" : "";
            if (rqc.getStartValue() != null) {
                opString = ">" + opString + " '" + rqc.getStartValue() + "'";
            }else
                opString = "<" + opString + " '" + rqc.getEndValue() + "'";
            returnString = rqc.getElementName() + " " + opString;
        }else if (criteria instanceof TermQueryCriteria) {
            TermQueryCriteria tqc = (TermQueryCriteria) criteria;
            returnString = tqc.getElementName() + " == '" + tqc.getValue() + "'";
        }
        return returnString;
    }
   
    private static String stripOutSqlDefinition(String sqlStringQueryMethod) {
        return sqlStringQueryMethod.trim().replaceAll("((?:SQL)|(?:sql))\\s*(.*)\\s*\\{", "").replaceAll("}$", "").trim();
    }
   
    private static List<Expression> getSqlStatementArgs(String sqlStringQueryMethod) throws QueryFormulationException {
        boolean inExpr = false;
        int startArgs = 0;
        for (int i = 0; i < sqlStringQueryMethod.length(); i++) {
            char curChar = sqlStringQueryMethod.charAt(i);
            switch (curChar) {
            case '(':
                startArgs = i + 1;
                break;
            case ')':
                if (!inExpr) {
                    String[] args = sqlStringQueryMethod.substring(startArgs, i).trim().split("'\\s*,");
                    LinkedList<Expression> argsList = new LinkedList<Expression>();
                    for (String arg : args)
                        argsList.add(new Expression((arg = arg.trim()).endsWith("'") ? arg : (arg + "'")));
                    return argsList;
                } else {
                    break;
                }
            case '\'':
                inExpr = !inExpr;
                break;
            }
        }
        throw new QueryFormulationException("Failed to read in args");
    }
   
    private static QueryFilter createFilter(Expression expr) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        String[] filterArgs = expr.getValue().split(",");
        FilterAlgor filterAlgor = (FilterAlgor) Class.forName(filterArgs[3]).newInstance();
        QueryFilter qf = new QueryFilter(filterArgs[0], filterArgs[1], filterArgs[2], filterAlgor);
        filterAlgor.setEpsilon(Integer.parseInt(filterArgs[4]));
        return qf;
    }

    /**
     * Uses "Shunting yard algorithm" (see:
     * http://en.wikipedia.org/wiki/Shunting_yard_algorithm)
     */
    private static LinkedList<String> toPostFix(String statement) {
        LinkedList<String> postFix = new LinkedList<String>();
        Stack<String> stack = new Stack<String>();

        for (int i = 0; i < statement.length(); i++) {
            char curChar = statement.charAt(i);
            switch (curChar) {
            case '(':
                stack.push(new String("("));
                break;
            case ')':
                String value = null;
                while (!(value = stack.pop()).equals("("))
                    postFix.add(value);
                if (stack.peek().equals("NOT"))
                    postFix.add(stack.pop());
                break;
            case ' ':
                break;
            default:
                if (statement.substring(i, i + 3).equals("AND")) {
                    while (!stack.isEmpty()
                            && (stack.peek().equals("AND")))
                        postFix.add(stack.pop());
                    stack.push("AND");
                    i += 2;
                } else if (statement.substring(i, i + 2).equals("OR")) {
                    while (!stack.isEmpty()
                            && (stack.peek().equals("AND") || stack.peek()
                                    .equals("OR")))
                        postFix.add(stack.pop());
                    stack.push("OR");
                    i += 1;
                } else if (statement.substring(i, i + 3).equals("NOT")) {
                    stack.push("NOT");
                    i += 2;
                } else {
                    int endIndex = statement.indexOf('\'', statement.indexOf(
                            '\'', i) + 1) + 1;
                    postFix.add(statement.substring(i, endIndex));
                    i = endIndex - 1;
                }
            }
        }

        while (!stack.isEmpty())
            postFix.add(stack.pop());

        return postFix;
    }

    private static QueryCriteria parseStatement(LinkedList<String> postFixStatement)
            throws QueryFormulationException {
        Stack<QueryCriteria> stack = new Stack<QueryCriteria>();
        for (String item : postFixStatement) {
            if (item.equals("AND")) {
                BooleanQueryCriteria bQC = new BooleanQueryCriteria();
                bQC.addTerm(stack.pop());
                bQC.addTerm(stack.pop());
                stack.push(bQC);
            } else if (item.equals("OR")) {
                BooleanQueryCriteria bQC = new BooleanQueryCriteria();
                bQC.setOperator(BooleanQueryCriteria.OR);
                bQC.addTerm(stack.pop());
                bQC.addTerm(stack.pop());
                stack.push(bQC);
            } else if (item.equals("NOT")) {
                BooleanQueryCriteria bQC = new BooleanQueryCriteria();
                bQC.setOperator(BooleanQueryCriteria.NOT);
                bQC.addTerm(stack.pop());
                stack.push(bQC);
            } else {
                stack.push(new Expression(item).convertToQueryCriteria());
            }
        }
        return stack.pop();
    }

    private static String listToString(List<String> list) {
        String arrayString = "";
        if (list.size() > 0) {
            arrayString = list.get(0);
            for (int i = 1; i < list.size(); i++)
                arrayString += "," + list.get(i);
       
        return arrayString;
    }


    private static class Expression {

        public static final short GREATER_THAN = 12;

        public static final short LESS_THAN = 3;

        public static final short EQUAL_TO = 9;

        public static final short NOT_EQUAL_TO = 15;

        public static final short GREATER_THAN_OR_EQUAL_TO = 13;

        public static final short LESS_THAN_OR_EQUAL_TO = 11;

        public static final short NOT = 6;

        private String[] stringValues = new String[] { "`", "`", "`", "<", "`",
                "`", "!", "`", "`", "=", "`", "<=", ">", ">=", "`", "!=" };

        private String expression;

        private String key;

        private String val;

        private int op;

        public Expression(String expression) {
            this.parseExpression(this.expression = expression);
        }

        public Expression(String key, int op, String val) {
            this.key = key.trim();
            this.op = op;
            this.val = this.removeTickBounds(val.trim());
        }

        private void parseExpression(String expression) {
            Matcher matcher = Pattern.compile("((?:>=)|(?:<=)|(?:==)|(?:!=)|(?:=)|(?:>)|(?:<))").matcher(expression);
            matcher.find();
            this.key = expression.substring(0, matcher.start()).trim();
            this.val = this.removeTickBounds(expression.substring(matcher.end()).trim());
            String opString = matcher.group();
            for (char c : opString.toCharArray())
                this.op = this.op | this.getShortValueForOp(c);
        }

        private String removeTickBounds(String value) {
            if (value.startsWith("'") && value.endsWith("'"))
                value = value.substring(1, value.length() - 1);
            return value;
        }

        private int getShortValueForOp(char op) {
            switch (op) {
            case '>':
                return GREATER_THAN;
            case '<':
                return LESS_THAN;
            case '=':
                return EQUAL_TO;
            case '!':
                return NOT;
            default:
                return 0;
            }
        }

        public QueryCriteria convertToQueryCriteria()
                throws QueryFormulationException {
            switch (this.op) {
            case GREATER_THAN:
                return new RangeQueryCriteria(this.key, this.val, null, false);
            case LESS_THAN:
                return new RangeQueryCriteria(this.key, null, this.val, false);
            case EQUAL_TO:
                return new TermQueryCriteria(this.key, this.val);
            case NOT_EQUAL_TO:
                BooleanQueryCriteria notEqBQC = new BooleanQueryCriteria();
                notEqBQC.setOperator(BooleanQueryCriteria.NOT);
                notEqBQC.addTerm(new TermQueryCriteria(this.key, this.val));
                return notEqBQC;
            case GREATER_THAN_OR_EQUAL_TO:
                return new RangeQueryCriteria(this.key, this.val, null, true);
            case LESS_THAN_OR_EQUAL_TO:
                return new RangeQueryCriteria(this.key, null, this.val, true);
            }
            throw new QueryFormulationException(
                    "Was not able to form query . . . probably an invalid operator -- "
                            + this.toString());
        }

        public String getKey() {
            return this.key;
        }

        public String getValue() {
            return this.val;
        }

        public int getOp() {
            return this.op;
        }

        public String getExpression() {
            return this.expression;
        }

        public String toString() {
            return this.key + " " + this.stringValues[this.op] + " " + this.val;
        }

    }
   
    public static void main(String[] args) throws QueryFormulationException {
        String query = "SELECT * FROM IASI_L1C WHERE one == '1' AND two == '2' OR NOT(five == '5') OR three == '3' AND four == '4'";
        System.out.println("query: " + query);
        System.out.println("query after : " + unparseSqlQuery(parseSqlQuery(query)));
        query = "SELECT * FROM IASI_L1C";
        System.out.println("query: " + query);
        System.out.println("query after : " + unparseSqlQuery(parseSqlQuery(query)));
        query = "SELECT * FROM *";
        System.out.println("query: " + query);
        System.out.println("query after : " + unparseSqlQuery(parseSqlQuery(query)));
    }
}
TOP

Related Classes of org.apache.oodt.cas.filemgr.util.SqlParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.