Package org.xorm.query

Source Code of org.xorm.query.BoundExpression

/*
    $Header: /cvsroot/xorm/xorm/src/org/xorm/query/BoundExpression.java,v 1.26 2003/07/09 01:22:50 wbiggs Exp $

    This file is part of XORM.

    XORM is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    XORM is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with XORM; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package org.xorm.query;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;

import javax.jdo.JDOHelper;
import javax.jdo.JDOUserException;
import javax.jdo.PersistenceManager;
import javax.jdo.spi.PersistenceCapable;

import org.xorm.ClassMapping;
import org.xorm.InterfaceInvocationHandler;
import org.xorm.ModelMapping;
import org.xorm.RelationshipMapping;
import org.xorm.XORM;
import org.xorm.I15d;
import org.xorm.datastore.Column;
import org.xorm.datastore.Table;
import org.xorm.util.FieldDescriptor;

/**
* Represents a Query where parameters have been bound to specific
* values and mapped against the datastore.  This object bridges the
* gap between the object query model (Expression) and the data query
* model (Selector).  The task of a DatastoreDriver is to take a
* Selector and convert it to a native query representation.
*
* Currently only a limited set of Expressions can be transformed
* to Selectors.
*
* Note that once bound, a BoundExpression does not require any knowledge
* of JDO interfaces.
*/
public class BoundExpression extends QueryContext implements ExpressionVisitor, I15d {
    private QueryLanguage query;
    private ModelMapping modelMapping;

    // This gets cached after first call to getSelector()
    private Selector top;
    private Selector selector;
    private HashMap varToSelector = new HashMap();

    private Column lastColumn;
    private RelationshipMapping lastRelationship;
    private ClassMapping mapping;
    private Object lastValue;
    private boolean valueMode;

    /**
     * Creates a new BoundExpression for the given query.
     */
    public BoundExpression(QueryLanguage query, PersistenceManager mgr) {
  super(query.getCandidateClass());
  this.query = query;
  this.modelMapping = XORM.getModelMapping(mgr);
    }

    private Selector.Ordering[] getOrdering() {
  // Convert QueryOrdering[] to Selector.Ordering[]
  QueryOrdering[] fieldOrdering = query.getOrdering();
  if (fieldOrdering == null) return null;
  Selector.Ordering[] ordering = new Selector.Ordering [fieldOrdering.length];
  for (int i = 0; i < fieldOrdering.length; i++) {
      String field = fieldOrdering[i].getField();
            Column column = null;

            // Handle a field of a field of a field of a...
            Class currentClass = getCandidateClass();
            StringTokenizer tok = new StringTokenizer(field, ".");

            String fieldToken;
      FieldDescriptor fd;
      Selector firstSelector = null, currentSelector = null;
     
      while (modelMapping.isManagedType(currentClass)) {
    ClassMapping currentClassMapping = modelMapping.getClassMapping(currentClass);
   
    Selector nextSelector = new Selector(currentClassMapping.getTable(), null);
    if (currentSelector != null) {
        Condition condition = new SimpleCondition
      (column, Operator.EQUAL, nextSelector);
        currentSelector.setCondition(condition);
    } else {
        firstSelector = nextSelector;
    }
    currentSelector = nextSelector;

    if (!tok.hasMoreTokens()) {
        throw new JDOUserException(I18N.msg("E_ordering"));
    }
    fieldToken = tok.nextToken();
    fd = currentClassMapping.getFieldDescriptor(fieldToken);
    if (fd == null) {
        throw new JDOUserException(I18N.msg("E_unmapped_field", fieldToken));
    }
    column = currentClassMapping.getColumn(fieldToken);
    currentClass = fd.type;
      }

      top.merge(firstSelector, Operator.ANDC);

            // Ok, now that we jumped through all the field dereferencing
            // hoops, let's set the column we're actually ordering on.
            ordering[i] = new Selector.Ordering
    (column, fieldOrdering[i].getOrder());
        }
  return ordering;
    }

    public void bindParameter(int index, Object value) {
  if (query instanceof AbstractQueryLanguage) {
      AbstractQueryLanguage aql = (AbstractQueryLanguage) query;

      String name = (String) aql.getParameterNames().get(index);
      Class clazz = aql.getParameterType(name);
      if (value == null) {
    if (clazz.isPrimitive()) {
        throw new JDOUserException(I18N.msg("E_null_primitive"));
    }
    bindParameter(name, value);
      } else {
    if (clazz.isInstance(value) || isWrappedInstance(value, clazz)) {
        bindParameter(name, value);
    } else {
        throw new JDOUserException(I18N.msg("E_param_type", value.getClass().getName(), clazz.getName()));
    }
      }
  }
    }

    private static boolean isWrappedInstance(Object value, Class clazz) {
  if (!clazz.isPrimitive()) { return false; }

  if (clazz.equals(Integer.TYPE)) {
      return value instanceof Integer;
  } else if (clazz.equals(Short.TYPE)) {
      return value instanceof Short;
  } else if (clazz.equals(Long.TYPE)) {
      return value instanceof Long;
  } else if (clazz.equals(Boolean.TYPE)) {
      return value instanceof Boolean;
  } else if (clazz.equals(Byte.TYPE)) {
      return value instanceof Byte;
  } else if (clazz.equals(Float.TYPE)) {
      return value instanceof Float;
  } else if (clazz.equals(Double.TYPE)) {
      return value instanceof Double;
  } else if (clazz.equals(Character.TYPE)) {
      return value instanceof Character;
  } else {
      // Never gets here
      return false;
  }
    }

    /**
     * Creates or retrieves the Selector tree for this query.
     */
    public Selector getSelector() {
  if (top == null) {
      if (query instanceof DataQuery) {
    top = makeSelector((DataQuery) query);
      } else {
    //System.out.println("Expression in: " + query.getExpression());
    getSelector(query.getExpression());
      }
      top.setOrdering(getOrdering());
  }
  //System.out.println("Selector out; " + top);
  return top;
    }

    // Convert DataQuery to Selector
    private Selector makeSelector(DataQuery dataQuery) {
  Class targetClass = dataQuery.getCandidateClass();
  ClassMapping mapping = modelMapping.getClassMapping(targetClass);
  Table table = mapping.getTable();
  Condition dataCondition = dataQuery.getCondition();
  if (dataCondition instanceof RawCondition) {
      // Resolve parameters
      dataCondition = makeRawCondition((RawCondition) dataCondition);
  }
  return new Selector(table, dataCondition);
    }

    private RawCondition makeRawCondition(RawCondition condition) {
  String where2 = condition.getRawQuery();
  int pos = 0;
  while ((pos = where2.indexOf('{', pos)) != -1) {
      int pos2 = where2.indexOf('}', pos + 1);
      if (pos2 == -1) break;
      String name = where2.substring(pos+1,pos2);
      boolean contains = hasParameter(name);
      Object operand = resolveParameter(name);
     
      /*
       * Ignore {xxx} if xxx has no value this is to allow JDBC escape
       * sequences to be passed through.  Maybe XORM should use a
       * different delimiter than {} so it doesn't conflict with JDBC...
       */
      if (!contains) {
    pos++;
    continue;
      }

      where2 = where2.substring(0,pos)
    + condition.convertOperand(operand)
    + where2.substring(pos2 + 1);
      pos = pos2 + 1;
  }
  return new RawCondition(condition.getTables(), where2);
    }

    // Called internally
    private Selector getSelector(Expression expression) {
  mapping = modelMapping.getClassMapping(query.getCandidateClass());
  Table table = mapping.getTable();
  top = new Selector(table, null);
  selector = top;
  expression.accept(this);

  // Deal with boolean standalones
  while (expression instanceof Expression.Not) {
      expression = ((Expression.Not) expression).getOperand();
  }

  if ((expression instanceof Expression.FieldAccess)
      && (expression.getType().equals(Boolean.TYPE))) {
      selector.setCondition(new SimpleCondition(lastColumn, Operator.EQUAL, Boolean.TRUE));
  }

  //System.out.println("getSelector(" + expression + ")\nreturns: " + top);
  return top;
    }

    public boolean visitAnd(Expression.And exp) {
  visitAndOrImpl(exp, Operator.ANDC);
  return true;
    }

    public boolean visitConditionalAnd(Expression.ConditionalAnd exp) {
  visitAndOrImpl(exp, Operator.ANDC);
  return true;
    }

    public boolean visitConditionalOr(Expression.ConditionalOr exp) {
  visitAndOrImpl(exp, Operator.ORC);
  return true;
    }

    private void visitAndOrImpl(Expression.Comparison exp, Operator operator) {
  Selector orig = top;

  // Visit lhs
  Selector lhs = getSelector(exp.getLHS());

  // Visit rhs
  Selector rhs = getSelector(exp.getRHS());

  // Merge left and right
  if (lhs != null) {
      lhs.merge(rhs, operator);
      orig.setCondition(lhs.getCondition());
  } else {
      orig.setCondition(rhs.getCondition());
  }
  top = orig;
  //System.out.println("returning with top: " + top);
    }


    public boolean visitComparison(Expression.Comparison exp) {
  Expression left = exp.getLHS();
  Expression right = exp.getRHS();

  // Check for some cheats
  // x.indexOf(y) != -1 --> x.strstr(y)
  // x.indexOf(y) >= 0  --> same  (for you old-school hackers)
  if ((left instanceof Expression.MethodCall)
      && (right instanceof Expression.Constant)
      && "indexOf".equals(((Expression.MethodCall) left).getName())
      && (((exp.operator() == Operator.NOT_EQUAL)
     && new Integer(-1).equals(right.evaluate(this)))
    || ((exp.operator() == Operator.GTE)
    && new Integer(0).equals(right.evaluate(this))))) {
      Expression.MethodCall lmc = (Expression.MethodCall) left;
      Expression exp2 = new Expression.MethodCall
    (lmc.getOwner(), "strstr", lmc.getParameters(), Boolean.TYPE);
      exp2.accept(this);
      return true;
  }

  // Visit the field
  exp.getLHS().accept(this);

  // Get the operator
  Operator operator = exp.operator();

  // Get the value
  valueMode = true;
  exp.getRHS().accept(this);
  // Convert Objects to their IDs
  if (lastValue instanceof PersistenceCapable) {
      lastValue = XORM.extractPrimaryKey(JDOHelper.getObjectId(lastValue));
  }
  valueMode = false;

  SimpleCondition sc = new SimpleCondition
      (lastColumn, operator, lastValue);
  selector.setCondition(sc);
  return true;
    }

    public boolean visitFieldAccess(Expression.FieldAccess exp) {
  //System.out.println("Visit fieldAccess: " + exp.toString());
  if (exp == Expression.FieldAccess.THIS) {
      mapping = modelMapping.getClassMapping(query.getCandidateClass());
      lastRelationship = null;
      lastColumn = mapping.getTable().getPrimaryKey();
  } else {
      // Recurse up the chain to "this"
      Expression owner = exp.getOwner();
      owner.accept(this);

      if (valueMode) {
    // Introspect on lastValue (which should be a JDO object)
    InterfaceInvocationHandler handler = InterfaceInvocationHandler.getHandler(lastValue);
    Class returnType = exp.getType();
    ClassMapping returnTypeMapping = null;
    if (ClassMapping.isUserType(returnType)) {
        returnTypeMapping = modelMapping.getClassMapping(returnType);
    }
    lastValue = handler.invokeGet(exp.getName(), returnTypeMapping, exp.getType());
      } else {
    // If we're hanging from another field, interpolate
    // the table join
    if (lastRelationship != null) {
        checkJoin(owner);
    }
   
    lastColumn = mapping.getColumn(exp.getName());
    lastRelationship = mapping.getRelationship(exp.getName());
      }
  }
  return true;
    }

    private void checkJoin(Expression owner) {
  if (lastRelationship != null) {
      // Ex. x.y; lastColumn = x
      // generate x_id = x.x_id
      ClassMapping nextMapping = modelMapping.getClassMapping(owner.getType());
      Selector se = new Selector(nextMapping.getTable(), null);
      SimpleCondition join = new SimpleCondition
    (mapping.getColumn(((Expression.Symbolic) owner).getName()),
     Operator.EQUAL, se);
      selector.setCondition(join);
      selector = se;
      mapping = nextMapping;
  }
    }

    public boolean visitVariable(Expression.Variable exp) {
  mapping = modelMapping.getClassMapping(exp.getType());
  selector = (Selector) varToSelector.get(exp.getName());
  top = (Selector) selector.clone();
  lastRelationship = null;

  //System.out.println("top is now: " + top);
  // TODO This may not be correct if the table is touched
  // previously/elsewhere in the query
  selector = top.findSelector(mapping.getTable());
  //System.out.println("selector is now: " + selector);
  return true;
    }

    public boolean visitMethodCall(Expression.MethodCall exp) {
  String name = exp.getName();
  // Read the field
  exp.getOwner().accept(this);
  // Do we need a join?
  if (!"contains".equals(name) && !"isEmpty".equals(name)) {
      checkJoin(exp.getOwner());
  }

  Column column = lastColumn;
  // Read the value
  Object param = null;
  Expression operand = null;
  if (!"isEmpty".equals(name)) {
      operand = exp.getParameters()[0];
      if ((operand instanceof Expression.Constant)
    || (operand instanceof Expression.Parameter)) {
    param = operand.evaluate(this);
      }
  }
  if ("contains".equals(name)) {
      Expression owner = exp.getOwner();

      if ((owner instanceof Expression.Parameter) && (operand instanceof Expression)) {
    mapping = modelMapping.getClassMapping(query.getCandidateClass());
    Table table = mapping.getTable();
    top = new Selector(table, null);
    selector = top;
    operand.accept(this);
    selector.setCondition
        (new SimpleCondition
         (lastColumn, Operator.IN, collectionToIDs((Collection) owner.evaluate(this))));
    return true;
      }

      String field = ((Expression.Member) owner).getName();
      RelationshipMapping rm = mapping.getRelationship(field);
      ClassMapping targetMapping = modelMapping.getClassMapping
    (rm.getSource().getElementClass());
      Selector s = null;
      Selector old = selector;
      if (operand instanceof Expression.Variable) {
    // This field is really the variable, using targetMapping
    selector = new Selector(targetMapping.getTable(),
          null);
      } else if (operand instanceof Expression.Parameter) {
    if (param != null) {
        param = XORM.extractPrimaryKey(JDOHelper.getObjectId(param));
    }
    selector = new Selector
        (rm.getTarget().getColumn().getTable(),
         new SimpleCondition
       (rm.getTarget().getColumn(),
        Operator.EQUAL, param));
    selector.setJoinColumn(rm.getTarget().getColumn());

    // If we have a parameter on a MToN mapping,
    // we can skip the target table
    selector.setJoinColumn(rm.getSource().getColumn());
    old.setCondition
        (new SimpleCondition(mapping.getTable().getPrimaryKey(),
           Operator.CONTAINS, selector));
    return true;
      }
      if (rm.isMToN()) {
    s = new Selector
        (rm.getTarget().getColumn().getTable(),
         new SimpleCondition
       (rm.getTarget().getColumn(),
        Operator.EQUAL,
        selector));
      } else {
    s = selector;
      }
      s.setJoinColumn(rm.getSource().getColumn());
      old.setCondition
    (new SimpleCondition(mapping.getTable().getPrimaryKey(),
             Operator.CONTAINS, s));
      if (operand instanceof Expression.Variable) {
    varToSelector.put(((Expression.Variable) operand).getName(),
          top);
    top = null;
      }
  } else if ("startsWith".equals(name)) {
      selector.setCondition
    (new SimpleCondition(column, Operator.STARTS_WITH,
             param));
  } else if ("endsWith".equals(name)) {
      selector.setCondition
    (new SimpleCondition(column, Operator.ENDS_WITH,
             param));
  } else if ("strstr".equals(name)) {
      selector.setCondition
    (new SimpleCondition(column, Operator.STR_CONTAINS,
             param));
  } else if ("isEmpty".equals(name)) {
      // model.model_id where model_id = model_platform
      //  .model_id where model_id is null
      String field = ((Expression.Member) exp.getOwner()).getName();
      RelationshipMapping rm = mapping.getRelationship(field);
      Selector s = new Selector
    (rm.getSource().getColumn().getTable(),
     new SimpleCondition(rm.getSource().getColumn(),
             Operator.EQUAL, null));
      s.setJoinColumn(rm.getSource().getColumn());
      s.setOuterJoin(true);
      selector.setCondition(new SimpleCondition
          (selector.getTable().getPrimaryKey(),
           Operator.CONTAINS, s));
  }
  return true;
    }

    public boolean visitParameter(Expression.Parameter exp) {
  //System.out.println("Visit parameter: " + exp.toString());
  lastValue = resolveParameter(exp.getName());
  lastRelationship = null;
  return true;
    }

    public boolean visitConstant(Expression.Constant exp) {
  lastValue = exp.getValue();
  return true;
    }

    public boolean visitNot(Expression.Not exp) {
  exp.getOperand().accept(this);
  Condition c = selector.getCondition();
  if (c != null) {
      c.setInverted();
  }
  return true;
    }

    public boolean visitUnary(Expression.Unary exp) {
  return false;
    }

    private Collection collectionToIDs(Collection input) {
  Collection output = new ArrayList(input.size());
  Iterator it = input.iterator();
  while (it.hasNext()) {
      Object obj = it.next();
      if (obj instanceof PersistenceCapable) {
    obj = XORM.extractPrimaryKey(JDOHelper.getObjectId(obj));
      }
      output.add(obj);
  }
  return output;
    }
}
TOP

Related Classes of org.xorm.query.BoundExpression

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.