Package com.meidusa.amoeba.sqljep

Source Code of com.meidusa.amoeba.sqljep.BaseJEP

/*****************************************************************************
      SQLJEP - Java SQL Expression Parser 0.2
      November 1 2006
         (c) Copyright 2006, Alexey Gaidukov
      SQLJEP Author: Alexey Gaidukov

      SQLJEP is based on JEP 2.24 (http://www.singularsys.com/jep/)
           (c) Copyright 2002, Nathan Funk
      See LICENSE.txt for license information.
*****************************************************************************/

package com.meidusa.amoeba.sqljep;

import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.meidusa.amoeba.sqljep.function.Comparative;
import com.meidusa.amoeba.sqljep.function.ComparativeBaseList;
import com.meidusa.amoeba.sqljep.function.Declare;
import com.meidusa.amoeba.sqljep.function.PostfixCommand;
import com.meidusa.amoeba.sqljep.function.PostfixCommandI;
import com.meidusa.amoeba.sqljep.variable.Variable;
import com.meidusa.amoeba.util.StaticString;
import com.meidusa.amoeba.util.ThreadLocalMap;

/**
* Base class for different SQLJEP classes. This class doesn't  know how to get source data.
* <p/>
* There are two types of variables in an expression. Columns of a abstarct table data and
* external variables. Example of external variables are <CODE>TIMESTAMP,DATE,TIME,SYSDATE</CODE> variables.
* <p/>
* User should override methods {@link #findColumn(String name)} and {@link #getColumnObject(int column)} for defining
* access to abstarct table data and the method {@link #getVariable(String name)} to access to external variables.
* <p/>
* To get a variable value at first {@link #getVariable(String name)} and after that {@link #findColumn(String name)} method is used.
* <p/>
* In expressions 'part_1.part2' names of variables are supported.
* Parser is case sensitive.
* <p/>
* Visit <a href="http://sourceforge.net/projects/sqljep">http://sourceforge.net/projects/sqljep</a>
* for the newest version of SQLJEP, and complete documentation.
* @author Alexey Gaidukov
*/
public abstract class BaseJEP implements ParserVisitor {
  /** Debug flag for extra command line output */
  public static final boolean debug = false;
 
  /** Parse time error List */
  final protected List<String> errorList = new ArrayList<String>();

  /** Node at the top of the parse tree */
  protected Node[] nodes;

  protected String expression;
 
  /**
   * Creates a new SQLJEP instance.
   * Store String representation of the expression. <br/>
   * To compile it use {@link #parseExpression} method.<br/>
   * To evaluate use {@link #getValue} method.
   */
  public BaseJEP(String exp) {
    if (exp == null) {
      throw new IllegalArgumentException("expression can be null");
    }
    expression = exp;
    nodes = null;
  }

  /**
  * Change SQLJEP's state to initial state.
  *
  */
  public void clear() {
    nodes = null;
    errorList.clear();
  }
 
  /**
  * @return true if expresson is in compiled state otherwise false
  */
  public boolean isValid() {
    return (nodes != null);
  }

  /**
   * Returns the top node of the expression tree. Because all nodes are
   * pointed to either directly or indirectly, the entire expression tree
   * can be accessed through this node. It may be used to manipulate the
   * expression, and subsequently evaluate it manually.
   * <p/>
   *   Sometimes it is necessary to change value of a SQLJEP expression
   *   by some external reasons. Then following code can be applied
   * <blockquote><pre>
   *     Node topNode = sqljep.getTopNode();
   *     if (topNode instanceof ASTVarNode) {
   *       ((ASTVarNode)topNode).variable.setValue(value);
   *     }
   *     else {
   *       throw new org.medfoster.sqljep.ParseException("Expressions are not permitted");
   *     }
   * </pre></blockquote>
   * @return The top node of the expression tree
   */
  public Node getTopNode() {
    return nodes[nodes.length-1];
  }
 
  /**
   * method is used to get the value of the column in the current row of
   * abstract table data.
   * @return the column value
   * @param column Number in the abstract table data
   * @throws com.meidusa.amoeba.sqljep.ParseException
   */
  @SuppressWarnings("unchecked")
  public abstract Comparable getColumnObject(int column) throws ParseException;

  /**
 
  /**
   * Returns true if an error occured during the most recent
   * action (parsing or evaluation).
   * @return Returns <code>true</code> if an error occured during the most
   * recent action (parsing or evaluation).
   */
  boolean hasError() {
    return !errorList.isEmpty();
  }

  /**
   * Reports information on the errors that occured during the most recent
   * action.
   * @return A string containing information on the errors, each separated
   * by a newline character; null if no error has occured
   */
  String getErrorInfo() {
    if (hasError()) {
      String str = "";
      for (String err : errorList) {
        str += err + "\n";
      }
      return str;
    } else {
      return null;
    }
  }

  /**
   * Parses the expression.
   * The root of the expression tree is returned by {@link #getTopNode()} method
   * @throws com.meidusa.amoeba.sqljep.ParseException If there are errors in the expression then it fires the exception
   */
  final public void parseExpression(Map<String,Integer> columnMapping,Map<String,Variable> variableMapping,final Map<String,PostfixCommand> funMap) throws ParseException {
    Reader reader = new StringReader(expression);
    Parser parser = new Parser(reader){

      @Override
      public boolean containsKey(String key) {
        return funMap.containsKey(key);
      }

      @Override
      public PostfixCommandI getFunction(String name) {
        return funMap.get(name);
      }
     
    };
    parser.setColumnMapping(columnMapping);
    parser.setVariableMapping(variableMapping);
    try {
      // try parsing
      nodes = parser.parseStream(reader);
      errorList.clear();
      errorList.addAll(parser.getErrorList());
    } catch (ParseException e) {
      // an exception was thrown, so there is no parse tree
      nodes = null;
      errorList.add(e.getMessage());
    } catch (Throwable e) {
      throw new com.meidusa.amoeba.sqljep.ParseException(toString(), e);
    }
   
    if (hasError()) {
      throw new com.meidusa.amoeba.sqljep.ParseException(getErrorInfo());
    }
   
    // If debug is enabled, print a dump of the tree to
    // standard output
    if (debug) {
      ParserVisitor v = new ParserDumpVisitor();
      try {
        for(Node node : nodes){
          node.jjtAccept(v, null);
        }
      } catch (ParseException e) {
        errorList.add(e.getMessage());
        e.printStackTrace();
      }
    }
  }
 
  /**
   * Returns the value of the expression as an object. The expression
   * tree is specified with its top node. The algorithm uses a stack
   * {@link com.meidusa.amoeba.sqljep.JepRuntime#stack} for evaluation.
   * <p>
   * An exception is thrown, if an error occurs during evaluation.
   * @return The value of the expression as an object.
   */
  public Comparable<?> getValue(Comparable<?>[] row) throws ParseException {
    JepRuntime runtime = getThreadJepRuntime(this);
    runtime.row = row;
    for(Comparable<?> comparable :runtime.row){
      if(comparable instanceof ComparativeBaseList){
        runtime.isMultValue = true;
      }
    }
    try{
      if (!isValid()) {
        throw new ParseException("Parser is not prepared");
      }
      if (!hasError()) {
        runtime.stack.setSize(0);
        // evaluate by letting the top node accept the visitor
        for(Node node : nodes){
          node.jjtAccept(this, null);
        }
        // something is wrong if not exactly one item remains on the stack
        // or if the error flag has been set
        if (runtime.stack.size() != 1) {
          throw new ParseException("Wrong stack state. Stack size: "+runtime.stack.size());
        }
   
        // return the value of the expression
        return runtime.stack.pop();
      } else {
        throw new ParseException(getErrorInfo());
      }
    }finally{
      runtime.vars.clear();
      runtime.stack.setSize(0);
      runtime.isMultValue = false;
    }
  }

  /**
   * Compares two objects for equality.
   * Two SQLJEPs are equals only when their String representations are equals.
   */
  public boolean equals(Object obj) {
    if (obj instanceof BaseJEP) {
      BaseJEP f = (BaseJEP)obj;
      return expression.equals(f.expression);
    }
    return false;
  }

    /**
   * Returns the expression string representation {@link #BaseJEP(String exp)}
   */
  public String toString() {
    return expression;
  }

  /* ParserVisitor interface */
 
  /**
   * This method should never be called when evaluation a normal
   * expression.
   */
  final public Object visit(SimpleNode node, Object data) throws ParseException {
    return null;
  }
 
  /**
   * This method should never be called when evaluating a normal
   * expression.
   */
  final public Object visit(ASTStart node, Object data) throws ParseException {
    return null;
  }
 
  /**
   * Visit a function node. The values of the child nodes
   * are first pushed onto the stack. Then the function class associated
   * with the node is used to evaluate the function.
   */
  final public Object visit(ASTFunNode node, Object data) throws ParseException {
    JepRuntime runtime = getThreadJepRuntime(this);
    PostfixCommandI pfmc = node.getPFMC();

    // check if the function class is set
    if (pfmc == null) {
      throw new ParseException("No function class associated with " + node.getName());
    }

    // evaluate all children (each leaves their result on the stack)

    if (debug) {
      System.out.println("Stack size after childrenAccept: " + runtime.stack.size());
    }
   
    Comparable<?>[] parameters = pfmc.evaluate(node, runtime);
   
    if(pfmc.isAutoBox()){
      ComparativeBaseList list = null;
      int index = -1;
      for(int i=0;i<parameters.length;i++){
        if(parameters[i] instanceof ComparativeBaseList){
          index = i;
          list = (ComparativeBaseList)parameters[i];
          break;
        }else{
         
        }
      }
     
      if(index >=0){
        for(int i=0;i<parameters.length;i++){
          if(i != index){
            if(parameters[i] instanceof Comparative){
              parameters[i] =((Comparative) parameters[i]).getValue();
            }
          }
        }
       
        for(Comparative comp:list.getList()){
          parameters[index] = comp.getValue();
          Comparable<?> value = pfmc.getResult(parameters);
          if(value instanceof Comparative){
            comp.setComparison(((Comparative) value).getComparison());
            comp.setValue(((Comparative) value).getValue());
          }else{
            comp.setValue(value);
          }
        }
        if(pfmc instanceof Declare){
          Declare declare = (Declare) pfmc;
          declare.declare(runtime, list);
        }else{
          runtime.stack.push(list);
        }
      }else{
        //分析每个参数是否是 Comparative 类型
        Comparative lastComparative = null;
        for(int i=0;i<parameters.length;i++){
          if(parameters[i] instanceof Comparative){
            lastComparative = ((Comparative) parameters[i]);
            parameters[i] =((Comparative) parameters[i]).getValue();
          }
        }

        Comparable<?> result = pfmc.getResult(parameters);
        if(lastComparative != null){
          lastComparative.setValue(result);
          result = lastComparative;
        }
        if(pfmc instanceof Declare){
          Declare declare = (Declare) pfmc;
          declare.declare(runtime, result);
        }else{
          runtime.stack.push(result);
        }
      }
    }else{
      if(pfmc instanceof Declare){
        Declare declare = (Declare) pfmc;
        declare.declare(runtime, pfmc.getResult(parameters));
      }else{
        runtime.stack.push(pfmc.getResult(parameters));
      }
    }
   
   
    if (debug) {
      System.out.println("Stack size after run: " + runtime.stack.size());
    }
    return null;
  }

  /**
   * Visit a variable node. The value of the variable is obtained from the
   * model and pushed onto the stack.
   */
  @SuppressWarnings("unchecked")
  final public Object visit(ASTVarNode node, Object data) throws ParseException {
    JepRuntime runtime = getThreadJepRuntime(this);
    if (node.index >= 0) {
      Comparable value = getColumnObject(node.index);
      if(value instanceof Comparative){
        value = (Comparable)((Comparative)value).clone();
      }
      runtime.stack.push(value);
    } else {
      if(node.variable == null){
        Comparable comparable = runtime.vars.get(node.ident);
        runtime.stack.push(comparable);
      }else{
        runtime.stack.push(node.variable.getValue());
      }
    }
    return null;
  }

  /**
   * Visit a constant node. The value of the constant is pushed onto the
   * stack.
   */
  final public Object visit(ASTConstant node, Object data) throws ParseException {
    JepRuntime runtime = getThreadJepRuntime(this);
    runtime.stack.push((Comparable<?>)node.value);
    return null;
  }

  final public Object visit(ASTArray node, Object data) throws ParseException {
    node.childrenAccept(this, null);
    return null;
  }
 
  public static JepRuntime getThreadJepRuntime(BaseJEP baseJep){
    JepRuntime runtime = (JepRuntime)ThreadLocalMap.get(StaticString.JEP_RUNTIME);
    if(runtime == null){
      runtime = new JepRuntime(baseJep);
      ThreadLocalMap.put(StaticString.JEP_RUNTIME, runtime);
    }else{
      runtime.ev = baseJep;
    }
    return runtime;
  }
}
TOP

Related Classes of com.meidusa.amoeba.sqljep.BaseJEP

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.