Package org.jinq.jpa.transform

Source Code of org.jinq.jpa.transform.SymbExToColumns

package org.jinq.jpa.transform;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jinq.jpa.jpqlquery.BinaryExpression;
import org.jinq.jpa.jpqlquery.ColumnExpressions;
import org.jinq.jpa.jpqlquery.ConstantExpression;
import org.jinq.jpa.jpqlquery.Expression;
import org.jinq.jpa.jpqlquery.FunctionExpression;
import org.jinq.jpa.jpqlquery.JPQLQuery;
import org.jinq.jpa.jpqlquery.ReadFieldExpression;
import org.jinq.jpa.jpqlquery.RowReader;
import org.jinq.jpa.jpqlquery.SelectFromWhere;
import org.jinq.jpa.jpqlquery.SelectOnly;
import org.jinq.jpa.jpqlquery.SimpleRowReader;
import org.jinq.jpa.jpqlquery.SubqueryExpression;
import org.jinq.jpa.jpqlquery.TupleRowReader;
import org.jinq.jpa.jpqlquery.UnaryExpression;
import org.objectweb.asm.Type;

import ch.epfl.labos.iu.orm.queryll2.path.TransformationClassAnalyzer;
import ch.epfl.labos.iu.orm.queryll2.symbolic.ConstantValue;
import ch.epfl.labos.iu.orm.queryll2.symbolic.ConstantValue.NullConstant;
import ch.epfl.labos.iu.orm.queryll2.symbolic.LambdaFactory;
import ch.epfl.labos.iu.orm.queryll2.symbolic.MethodCallValue;
import ch.epfl.labos.iu.orm.queryll2.symbolic.MethodSignature;
import ch.epfl.labos.iu.orm.queryll2.symbolic.TypedValue;
import ch.epfl.labos.iu.orm.queryll2.symbolic.TypedValueVisitor;
import ch.epfl.labos.iu.orm.queryll2.symbolic.TypedValueVisitorException;

public class SymbExToColumns extends TypedValueVisitor<SymbExPassDown, ColumnExpressions<?>, TypedValueVisitorException>
{
   final SymbExArgumentHandler argHandler;
   final JPQLQueryTransformConfiguration config;

   SymbExToColumns(JPQLQueryTransformConfiguration config, SymbExArgumentHandler argumentHandler)
   {
      this.config = config;
      this.argHandler = argumentHandler;
   }
  
   @Override public ColumnExpressions<?> defaultValue(TypedValue val, SymbExPassDown in) throws TypedValueVisitorException
   {
      throw new TypedValueVisitorException("Unhandled symbolic execution operation: " + val);
   }

   @Override public ColumnExpressions<?> argValue(TypedValue.ArgValue val, SymbExPassDown in) throws TypedValueVisitorException
   {
      int index = val.getIndex();
      return argHandler.handleArg(index, val.getType());
   }
  
   @Override public ColumnExpressions<?> booleanConstantValue(ConstantValue.BooleanConstant val, SymbExPassDown in) throws TypedValueVisitorException
   {
      if (in.isExpectingConditional)
      {
         return ColumnExpressions.singleColumn(new SimpleRowReader<Integer>(),
               new ConstantExpression(val.val ? "(1=1)" : "(1!=1)"));
      }
      else
      {
         return ColumnExpressions.singleColumn(new SimpleRowReader<Integer>(),
               new ConstantExpression(val.val ? "TRUE" : "FALSE"));
      }
   }

   @Override public ColumnExpressions<?> integerConstantValue(ConstantValue.IntegerConstant val, SymbExPassDown in) throws TypedValueVisitorException
   {
      return ColumnExpressions.singleColumn(new SimpleRowReader<Integer>(),
            new ConstantExpression(Integer.toString(val.val)));
   }
  
   @Override public ColumnExpressions<?> longConstantValue(ConstantValue.LongConstant val, SymbExPassDown in) throws TypedValueVisitorException
   {
      return ColumnExpressions.singleColumn(new SimpleRowReader<Long>(),
            new ConstantExpression(Long.toString(val.val)));
   }

   @Override public ColumnExpressions<?> doubleConstantValue(ConstantValue.DoubleConstant val, SymbExPassDown in) throws TypedValueVisitorException
   {
      return ColumnExpressions.singleColumn(new SimpleRowReader<Double>(),
            new ConstantExpression(Double.toString(val.val)));
   }

   @Override public ColumnExpressions<?> stringConstantValue(ConstantValue.StringConstant val, SymbExPassDown in) throws TypedValueVisitorException
   {
      return ColumnExpressions.singleColumn(new SimpleRowReader<String>(),
            new ConstantExpression("'"+ val.val.replaceAll("'", "''") +"'"));
   }
  
   @Override public ColumnExpressions<?> nullConstantValue(NullConstant val, SymbExPassDown in) throws TypedValueVisitorException
   {
      throw new TypedValueVisitorException("Unexpected NULL value");
   }
  
   @Override public ColumnExpressions<?> unaryMathOpValue(TypedValue.UnaryMathOpValue val, SymbExPassDown in) throws TypedValueVisitorException
   {
      SymbExPassDown passdown = SymbExPassDown.with(val, false);
      ColumnExpressions<?> left = val.operand.visit(this, passdown);
      return ColumnExpressions.singleColumn(left.reader,
            UnaryExpression.prefix(val.op.getOpString(), left.getOnlyColumn()));
   }

   @Override public ColumnExpressions<?> notOpValue(TypedValue.NotValue val, SymbExPassDown in) throws TypedValueVisitorException
   {
      SymbExPassDown passdown = SymbExPassDown.with(val, true);
      ColumnExpressions<?> left = val.operand.visit(this, passdown);
      return ColumnExpressions.singleColumn(left.reader,
            UnaryExpression.prefix("NOT", left.getOnlyColumn()));
   }

   @Override public ColumnExpressions<?> getStaticFieldValue(TypedValue.GetStaticFieldValue val, SymbExPassDown in) throws TypedValueVisitorException
   {
      // Check if we're just reading an enum constant
      if (config.metamodel.isKnownEnumType(val.owner))
      {
         String enumFullName = config.metamodel.getFullEnumConstantName(val.owner, val.name);
         if (enumFullName != null)
            return ColumnExpressions.singleColumn(new SimpleRowReader<Enum<?>>(),
                  new ConstantExpression(enumFullName));
      }
      return defaultValue(val, in);
   }

   @Override public ColumnExpressions<?> castValue(TypedValue.CastValue val, SymbExPassDown in) throws TypedValueVisitorException
   {
      // We need to handle casts between primitive types carefully
      // because JPQL doesn't really support them directly
      if (val.isPrimitive())
      {
         throw new TypedValueVisitorException("Casts of primitive values are not support in JPQL");
      }
      // TODO: Check if cast is consistent with the reader
//      SQLColumnValues toReturn = val.operand.visit(this, in);
//      if (!toReturn.reader.isCastConsistent(val.getType().getInternalName()))
//         throw new TypedValueVisitorException("Attempting to cast to an inconsistent type");
      return val.operand.visit(this, SymbExPassDown.with(val, in.isExpectingConditional));
   }

   private boolean isWideningCast(TypedValue val)
   {
      if (val instanceof TypedValue.CastValue)
      {
         TypedValue.CastValue castedVal = (TypedValue.CastValue)val;
         Type toType = castedVal.getType();
         Type fromType = castedVal.operand.getType();
         if (!numericPromotionPriority.containsKey(fromType)) return false;
         if (!numericPromotionPriority.containsKey(toType)) return false;
         if (numericPromotionPriority.get(toType) > numericPromotionPriority.get(fromType))
            return true;
      }
      else if (val instanceof MethodCallValue.VirtualMethodCallValue)
      {
         MethodCallValue methodCall = (MethodCallValue.VirtualMethodCallValue)val;
         MethodSignature sig = methodCall.getSignature();
         if (sig.equals(TransformationClassAnalyzer.newBigDecimalLong)
               || sig.equals(TransformationClassAnalyzer.newBigDecimalInt)
               || sig.equals(TransformationClassAnalyzer.newBigDecimalBigInteger))
         {
            return true;
         }
         else if (sig.equals(TransformationClassAnalyzer.bigDecimalDoubleValue)
               || sig.equals(TransformationClassAnalyzer.bigIntegerDoubleValue))
         {
            return true;
         }

      }
      else if (val instanceof MethodCallValue.StaticMethodCallValue)
      {
         MethodCallValue methodCall = (MethodCallValue.StaticMethodCallValue)val;
         MethodSignature sig = methodCall.getSignature();
         if (sig.equals(TransformationClassAnalyzer.bigIntegerValueOfLong))
         {
            return true;
         }
      }
      return false;
   }

   private TypedValue skipWideningCast(TypedValue val) throws TypedValueVisitorException
   {
      if (!isWideningCast(val)) return val;
      if (val instanceof TypedValue.CastValue)
      {
         TypedValue.CastValue castedVal = (TypedValue.CastValue)val;
         return skipWideningCast(castedVal.operand);
      }
      else if (val instanceof MethodCallValue.VirtualMethodCallValue)
      {
         MethodCallValue.VirtualMethodCallValue methodCall = (MethodCallValue.VirtualMethodCallValue)val;
         MethodSignature sig = methodCall.getSignature();
         if (sig.equals(TransformationClassAnalyzer.newBigDecimalLong)
               || sig.equals(TransformationClassAnalyzer.newBigDecimalInt)
               || sig.equals(TransformationClassAnalyzer.newBigDecimalBigInteger))
         {
            return skipWideningCast(methodCall.args.get(0));
         }
         else if (sig.equals(TransformationClassAnalyzer.bigDecimalDoubleValue)
               || sig.equals(TransformationClassAnalyzer.bigIntegerDoubleValue))
         {
            return skipWideningCast(methodCall.base);
         }
      }
      else if (val instanceof MethodCallValue.StaticMethodCallValue)
      {
         MethodCallValue methodCall = (MethodCallValue.StaticMethodCallValue)val;
         MethodSignature sig = methodCall.getSignature();
         if (sig.equals(TransformationClassAnalyzer.bigIntegerValueOfLong))
         {
            return skipWideningCast(methodCall.args.get(0));
         }
      }
      throw new IllegalArgumentException("Cannot skip an unknown widening cast type");
   }

   private <U> ColumnExpressions<U> binaryOpWithNull(String opString, TypedValue leftVal, TypedValue rightVal, SymbExPassDown passdown) throws TypedValueVisitorException
   {
      if (!("=".equals(opString) || "<>".equals(opString)))
         throw new TypedValueVisitorException("Unhandled operation involving NULL");
      if (leftVal instanceof ConstantValue.NullConstant && rightVal instanceof ConstantValue.NullConstant)
         throw new TypedValueVisitorException("Cannot handle comparisons involving two NULLs");
      TypedValue operandVal;
      if (leftVal instanceof ConstantValue.NullConstant)
         operandVal = rightVal;
      else
         operandVal = leftVal;
      ColumnExpressions<U> operand = (ColumnExpressions<U>)operandVal.visit(this, passdown);
      if ("=".equals(opString))
         return ColumnExpressions.singleColumn(new SimpleRowReader<>(),
               UnaryExpression.postfix("IS NULL", operand.getOnlyColumn()));
      else
         return ColumnExpressions.singleColumn(new SimpleRowReader<>(),
               UnaryExpression.postfix("IS NOT NULL", operand.getOnlyColumn()));
   }
  
   private <U> ColumnExpressions<U> binaryOpWithNumericPromotion(String opString, TypedValue leftVal, TypedValue rightVal, SymbExPassDown passdown) throws TypedValueVisitorException
   {
      boolean isFinalTypeFromLeft = true;
      // Handle operations with NULL separately
      if (leftVal instanceof ConstantValue.NullConstant || rightVal instanceof ConstantValue.NullConstant)
         return binaryOpWithNull(opString, leftVal, rightVal, passdown);
      // Check if we have a valid numeric promotion (i.e. one side has a widening cast
      // to match the type of the other side).
      assert(leftVal.getType().equals(rightVal.getType())
            || (leftVal.getType().getInternalName().equals("java/lang/Object") && config.isObjectEqualsSafe)// in Scala, many comparisons are done on Objects
            || (rightVal.getType().getInternalName().equals("java/lang/Object") && config.isObjectEqualsSafe));
      if (isWideningCast(leftVal))
      {
         if (!isWideningCast(rightVal))
         {
            leftVal = skipWideningCast(leftVal);
            isFinalTypeFromLeft = false;
         }
      }
      else if (isWideningCast(rightVal))
      {
         rightVal = skipWideningCast(rightVal);
      }
      // Actually translate the expressions now
      ColumnExpressions<U> left = (ColumnExpressions<U>)leftVal.visit(this, passdown);
      ColumnExpressions<U> right = (ColumnExpressions<U>)rightVal.visit(this, passdown);
      return ColumnExpressions.singleColumn(isFinalTypeFromLeft ? left.reader : right.reader,
            new BinaryExpression(opString, left.getOnlyColumn(), right.getOnlyColumn()));
   }
  
   @Override public ColumnExpressions<?> mathOpValue(TypedValue.MathOpValue val, SymbExPassDown in) throws TypedValueVisitorException
   {
      if (val.op == TypedValue.MathOpValue.Op.cmp)
         throw new TypedValueVisitorException("cmp operator was not converted to a boolean operator");
      if (val.op == TypedValue.MathOpValue.Op.mod)
      {
         if (val.left.getType().equals(Type.INT_TYPE) || val.right.getType().equals(Type.INT_TYPE))
         {
            SymbExPassDown passdown = SymbExPassDown.with(val, false);
            ColumnExpressions<?> left = (ColumnExpressions<?>)val.left.visit(this, passdown);
            ColumnExpressions<?> right = (ColumnExpressions<?>)val.right.visit(this, passdown);
            return ColumnExpressions.singleColumn(left.reader,
                  FunctionExpression.twoParam("MOD", left.getOnlyColumn(), right.getOnlyColumn()));
         }
         throw new TypedValueVisitorException("mod operator cannot be used for the given types.");
      }
      SymbExPassDown passdown = SymbExPassDown.with(val, false);
      return binaryOpWithNumericPromotion(val.sqlOpString(), val.left, val.right, passdown);
   }

   @Override public ColumnExpressions<?> comparisonOpValue(TypedValue.ComparisonValue val, SymbExPassDown in) throws TypedValueVisitorException
   {
      SymbExPassDown passdown = SymbExPassDown.with(val, false);
      return binaryOpWithNumericPromotion(val.sqlOpString(), val.left, val.right, passdown);
//      if (val.left.getType() == Type.BOOLEAN_TYPE
//            || val.right.getType() == Type.BOOLEAN_TYPE)
//      {
//         // TODO: These simplifications should be put into a separate
//         // optimization step, maybe?
//         if (val.left instanceof ConstantValue.IntegerConstant
//               && ((ConstantValue.IntegerConstant)val.left).val == 0)
//         {
//            left = new SQLColumnValues(new SQLReader.BooleanSQLReader());
//            left.columns[0] = new SQLFragment("FALSE");
//         }
//         if (val.right instanceof ConstantValue.IntegerConstant
//               && ((ConstantValue.IntegerConstant)val.right).val == 0)
//         {
//            right = new SQLColumnValues(new SQLReader.BooleanSQLReader());
//            right.columns[0] = new SQLFragment("FALSE");
//         }
//      }
   }
  
   private boolean isAggregateMethod(MethodSignature sig)
   {
      return sig.equals(MethodChecker.streamSumInt)
            || sig.equals(MethodChecker.streamSumDouble)
            || sig.equals(MethodChecker.streamSumLong)
            || sig.equals(MethodChecker.streamSumBigInteger)
            || sig.equals(MethodChecker.streamSumBigDecimal)
            || sig.equals(MethodChecker.streamMax)
            || sig.equals(MethodChecker.streamMin)
            || sig.equals(MethodChecker.streamAvg)
            || sig.equals(MethodChecker.streamCount);
   }
  
   @Override public ColumnExpressions<?> virtualMethodCallValue(MethodCallValue.VirtualMethodCallValue val, SymbExPassDown in) throws TypedValueVisitorException
   {
      MethodSignature sig = val.getSignature();
      if (TransformationClassAnalyzer.newPair.equals(sig)
            || TransformationClassAnalyzer.newTuple3.equals(sig)
            || TransformationClassAnalyzer.newTuple4.equals(sig)
            || TransformationClassAnalyzer.newTuple5.equals(sig)
            || TransformationClassAnalyzer.newTuple8.equals(sig))
      {
         ColumnExpressions<?> [] vals = new ColumnExpressions<?> [val.args.size()];
         // TODO: This is a little wonky passing down isExpectingConditional, but I think it's right for those times you create a tuple with booleans and then extract the booleans later
         SymbExPassDown passdown = SymbExPassDown.with(val, in.isExpectingConditional);
         for (int n = 0; n < vals.length; n++)
            vals[n] = val.args.get(n).visit(this, passdown);
         RowReader<?> [] valReaders = new RowReader[vals.length];
         for (int n = 0; n < vals.length; n++)
            valReaders[n] = vals[n].reader;

         ColumnExpressions<?> toReturn = new ColumnExpressions<>(TupleRowReader.createReaderForTuple(sig.owner, valReaders));
         for (int n = 0; n < vals.length; n++)
            toReturn.columns.addAll(vals[n].columns);
         return toReturn;
      }
      else if (config.metamodel.isSingularAttributeFieldMethod(sig))
      {
         String fieldName = config.metamodel.fieldMethodToFieldName(sig);
         SymbExPassDown passdown = SymbExPassDown.with(val, in.isExpectingConditional);
         ColumnExpressions<?> base = val.base.visit(this, passdown);
         if (in.isExpectingConditional &&
               (sig.getReturnType().equals(Type.BOOLEAN_TYPE)
               || sig.getReturnType().equals(Type.getObjectType("java/lang/Boolean"))))
         {
            return ColumnExpressions.singleColumn(new SimpleRowReader<>(),
                  new BinaryExpression("=", new ReadFieldExpression(base.getOnlyColumn(), fieldName), new ConstantExpression("TRUE")));
         }
         return ColumnExpressions.singleColumn(new SimpleRowReader<>(),
               new ReadFieldExpression(base.getOnlyColumn(), fieldName));
//         SQLColumnValues sql = new SQLColumnValues(base.reader.getReaderForField(fieldName));
//         for (int n = 0; n < sql.reader.getNumColumns(); n++)
//            sql.columns[n] = base.columns[base.reader.getColumnForField(fieldName) + n];
      }
      else if (MetamodelUtil.TUPLE_ACCESSORS.containsKey(sig))
      {
         int idx = MetamodelUtil.TUPLE_ACCESSORS.get(sig) - 1;
         // TODO: This is a little wonky passing down isExpectingConditional, but I think it's right for those times you create a tuple with booleans and then extract the booleans later
         SymbExPassDown passdown = SymbExPassDown.with(val, in.isExpectingConditional);
         ColumnExpressions<?> base = val.base.visit(this, passdown);
         RowReader<?> subreader = ((TupleRowReader<?>)base.reader).getReaderForIndex(idx);
         ColumnExpressions<?> toReturn = new ColumnExpressions<>(subreader);
         int baseOffset = ((TupleRowReader<?>)base.reader).getColumnForIndex(idx);
         for (int n = 0; n < subreader.getNumColumns(); n++)
            toReturn.columns.add(base.columns.get(n + baseOffset));
         return toReturn;
      }
      else if (sig.equals(TransformationClassAnalyzer.integerIntValue)
            || sig.equals(TransformationClassAnalyzer.longLongValue)
            || sig.equals(TransformationClassAnalyzer.doubleDoubleValue)
            || sig.equals(TransformationClassAnalyzer.booleanBooleanValue))
      {
         SymbExPassDown passdown = SymbExPassDown.with(val, in.isExpectingConditional);
         ColumnExpressions<?> base = val.base.visit(this, passdown);
         return base;
      }
      else if (sig.equals(TransformationClassAnalyzer.newBigDecimalLong)
            || sig.equals(TransformationClassAnalyzer.newBigDecimalDouble)
            || sig.equals(TransformationClassAnalyzer.newBigDecimalInt)
            || sig.equals(TransformationClassAnalyzer.newBigDecimalBigInteger))
      {
         throw new TypedValueVisitorException("New BigDecimals can only be created in the context of numeric promotion");
      }
      else if (isAggregateMethod(sig))
      {
         SymbExPassDown passdown = SymbExPassDown.with(val, false);
        
         // Check out what stream we're aggregating
         SymbExToSubQuery translator = config.newSymbExToSubQuery(argHandler);
         JPQLQuery<?> subQuery = val.base.visit(translator, passdown);
        
         // Extract the lambda used
         LambdaAnalysis lambda = null;
         if (val.args.size() > 0)
         {
            if (!(val.args.get(0) instanceof LambdaFactory))
               throw new TypedValueVisitorException("Expecting a lambda factory for aggregate method");
            LambdaFactory lambdaFactory = (LambdaFactory)val.args.get(0);
            try {
               lambda = LambdaAnalysis.analyzeMethod(config.metamodel, config.alternateClassLoader, config.isObjectEqualsSafe, lambdaFactory.getLambdaMethod(), lambdaFactory.getCapturedArgs(), true);
            } catch (Exception e)
            {
               throw new TypedValueVisitorException("Could not analyze the lambda code", e);
            }
         }
           
         try {
            AggregateTransform transform;
            if (sig.equals(MethodChecker.streamSumInt)
                  || sig.equals(MethodChecker.streamSumLong)
                  || sig.equals(MethodChecker.streamSumDouble)
                  || sig.equals(MethodChecker.streamSumBigDecimal)
                  || sig.equals(MethodChecker.streamSumBigInteger))
               transform = new AggregateTransform(config, AggregateTransform.AggregateType.SUM);
            else if (sig.equals(MethodChecker.streamMax))
               transform = new AggregateTransform(config, AggregateTransform.AggregateType.MAX);
            else if (sig.equals(MethodChecker.streamMin))
               transform = new AggregateTransform(config, AggregateTransform.AggregateType.MIN);
            else if (sig.equals(MethodChecker.streamAvg))
               transform = new AggregateTransform(config, AggregateTransform.AggregateType.AVG);
            else if (sig.equals(MethodChecker.streamCount))
               transform = new AggregateTransform(config, AggregateTransform.AggregateType.COUNT);
            else
               throw new TypedValueVisitorException("Unhandled aggregate operation");
            JPQLQuery<?> aggregatedQuery = transform.apply(subQuery, lambda, argHandler);
            // Return the aggregated columns that we've now calculated
            if (aggregatedQuery.getClass() == SelectOnly.class)
            {
               SelectOnly<?> select = (SelectOnly<?>)aggregatedQuery;
               return select.cols;
            }
            else if (aggregatedQuery.isValidSubquery() && aggregatedQuery instanceof SelectFromWhere)
            {
               SelectFromWhere<?> sfw = (SelectFromWhere<?>)aggregatedQuery;
               ColumnExpressions<?> toReturn = new ColumnExpressions<>(sfw.cols.reader);
               for (Expression col: sfw.cols.columns)
               {
                  SelectFromWhere<?> oneColQuery = sfw.shallowCopy();
                  oneColQuery.cols = ColumnExpressions.singleColumn(new SimpleRowReader<>(), col);
                  toReturn.columns.add(SubqueryExpression.from(oneColQuery));
               }
               return toReturn;
            }
            else
            {
               throw new TypedValueVisitorException("Unknown subquery type");
            }
         } catch (QueryTransformException e)
         {
            throw new TypedValueVisitorException("Could not derive an aggregate function for a lambda", e);
         }
      }
      else if (sig.equals(MethodChecker.streamGetOnlyValue))
      {
         SymbExPassDown passdown = SymbExPassDown.with(val, false);
        
         // Check out what stream we're aggregating
         SymbExToSubQuery translator = config.newSymbExToSubQuery(argHandler);
         JPQLQuery<?> subQuery = val.base.visit(translator, passdown);

         if (subQuery.isValidSubquery() && subQuery instanceof SelectFromWhere)
         {
            SelectFromWhere<?> sfw = (SelectFromWhere<?>)subQuery;
            ColumnExpressions<?> toReturn = new ColumnExpressions<>(sfw.cols.reader);
            for (Expression col: sfw.cols.columns)
            {
               SelectFromWhere<?> oneColQuery = sfw.shallowCopy();
               oneColQuery.cols = ColumnExpressions.singleColumn(new SimpleRowReader<>(), col);
               toReturn.columns.add(SubqueryExpression.from(oneColQuery));
            }
            return toReturn;
         }

         throw new TypedValueVisitorException("Cannot apply getOnlyValue() to the given subquery");
      }
      else if (MethodChecker.jpqlFunctionMethods.contains(sig))
      {
         if (sig.equals(MethodChecker.bigDecimalAbs)
               || sig.equals(MethodChecker.bigIntegerAbs))
         {
            SymbExPassDown passdown = SymbExPassDown.with(val, in.isExpectingConditional);
            ColumnExpressions<?> base = val.base.visit(this, passdown);
            return ColumnExpressions.singleColumn(base.reader,
                  FunctionExpression.singleParam("ABS", base.getOnlyColumn()));
         }
         else if (sig.equals(MethodChecker.stringToUpper))
         {
            SymbExPassDown passdown = SymbExPassDown.with(val, false);
            ColumnExpressions<?> base = val.base.visit(this, passdown);
            return ColumnExpressions.singleColumn(base.reader,
                  FunctionExpression.singleParam("UPPER", base.getOnlyColumn()));
         }
         else if (sig.equals(MethodChecker.stringToLower))
         {
            SymbExPassDown passdown = SymbExPassDown.with(val, false);
            ColumnExpressions<?> base = val.base.visit(this, passdown);
            return ColumnExpressions.singleColumn(base.reader,
                  FunctionExpression.singleParam("LOWER", base.getOnlyColumn()));
         }
         else if (sig.equals(MethodChecker.stringLength))
         {
            SymbExPassDown passdown = SymbExPassDown.with(val, false);
            ColumnExpressions<?> base = val.base.visit(this, passdown);
            return ColumnExpressions.singleColumn(base.reader,
                  FunctionExpression.singleParam("LENGTH", base.getOnlyColumn()));
         }
         else if (sig.equals(MethodChecker.stringTrim))
         {
            SymbExPassDown passdown = SymbExPassDown.with(val, false);
            ColumnExpressions<?> base = val.base.visit(this, passdown);
            return ColumnExpressions.singleColumn(base.reader,
                  FunctionExpression.singleParam("TRIM", base.getOnlyColumn()));
         }
         else if (sig.equals(MethodChecker.stringSubstring))
         {
            SymbExPassDown passdown = SymbExPassDown.with(val, false);
            ColumnExpressions<?> base = val.base.visit(this, passdown);
            ColumnExpressions<?> startIndex = val.args.get(0).visit(this, passdown);
            ColumnExpressions<?> endIndex = val.args.get(1).visit(this, passdown);
            return ColumnExpressions.singleColumn(base.reader,
                  FunctionExpression.threeParam("SUBSTRING",
                        base.getOnlyColumn(),
                        new BinaryExpression("+", startIndex.getOnlyColumn(), new ConstantExpression("1")),
                        new BinaryExpression("-", endIndex.getOnlyColumn(), startIndex.getOnlyColumn())));
         }
         else if (sig.equals(MethodChecker.stringIndexOf))
         {
            SymbExPassDown passdown = SymbExPassDown.with(val, false);
            ColumnExpressions<?> base = val.base.visit(this, passdown);
            ColumnExpressions<?> search = val.args.get(0).visit(this, passdown);
            return ColumnExpressions.singleColumn(base.reader,
                  new BinaryExpression("-",
                        FunctionExpression.twoParam("LOCATE",
                              search.getOnlyColumn(),
                              base.getOnlyColumn())
                        , new ConstantExpression("1")));
         }
         throw new TypedValueVisitorException("Do not know how to translate the method " + sig + " into a JPQL function");
      }
      else if (sig.equals(TransformationClassAnalyzer.stringBuilderToString))
      {
         List<ColumnExpressions<?>> concatenatedStrings = new ArrayList<>();
         MethodCallValue.VirtualMethodCallValue baseVal = val;
         while (true)
         {
            if (!(baseVal.base instanceof MethodCallValue.VirtualMethodCallValue))
               throw new TypedValueVisitorException("Unexpected use of StringBuilder");
            baseVal = (MethodCallValue.VirtualMethodCallValue)baseVal.base;
            if (baseVal.getSignature().equals(TransformationClassAnalyzer.newStringBuilderString))
            {
               SymbExPassDown passdown = SymbExPassDown.with(val, false);
               concatenatedStrings.add(baseVal.args.get(0).visit(this, passdown));
               break;
            }
            else if (baseVal.getSignature().equals(TransformationClassAnalyzer.newStringBuilder))
            {
               break;
            }
            else if (baseVal.getSignature().equals(TransformationClassAnalyzer.stringBuilderAppendString))
            {
               SymbExPassDown passdown = SymbExPassDown.with(val, false);
               concatenatedStrings.add(baseVal.args.get(0).visit(this, passdown));
            }
            else
               throw new TypedValueVisitorException("Unexpected use of StringBuilder");
         }
        
         if (concatenatedStrings.size() == 1)
            return concatenatedStrings.get(0);
         Expression head = concatenatedStrings.get(concatenatedStrings.size() - 1).getOnlyColumn();
         for (int n = concatenatedStrings.size() - 2; n >= 0 ; n--)
            head = FunctionExpression.twoParam("CONCAT", head, concatenatedStrings.get(n).getOnlyColumn());
         return ColumnExpressions.singleColumn(new SimpleRowReader<>(), head);
      }
      else
         return super.virtualMethodCallValue(val, in);
   }

   @Override public ColumnExpressions<?> staticMethodCallValue(MethodCallValue.StaticMethodCallValue val, SymbExPassDown in) throws TypedValueVisitorException
   {
      MethodSignature sig = val.getSignature();
      if (sig.equals(TransformationClassAnalyzer.integerValueOf)
            || sig.equals(TransformationClassAnalyzer.longValueOf)
            || sig.equals(TransformationClassAnalyzer.doubleValueOf)
            || sig.equals(TransformationClassAnalyzer.booleanValueOf))
      {
         // Integer.valueOf() to be like a cast and assume it's correct
         SymbExPassDown passdown = SymbExPassDown.with(val, in.isExpectingConditional);
         ColumnExpressions<?> base = val.args.get(0).visit(this, passdown);
         return base;
      }
      else if (sig.equals(TransformationClassAnalyzer.bigIntegerValueOfLong))
      {
         throw new TypedValueVisitorException("New BigIntegers can only be created in the context of numeric promotion");
      }
      else if (sig.equals(MethodChecker.stringValueOfObject))
      {
         if (!val.args.get(0).getType().equals(Type.getObjectType("java/lang/String")))
            throw new TypedValueVisitorException("Do not know how to convert type " + val.args.get(0).getType() + " to a string");
         SymbExPassDown passdown = SymbExPassDown.with(val, in.isExpectingConditional);
         ColumnExpressions<?> base = val.args.get(0).visit(this, passdown);
         return base;
      }
      else if (MethodChecker.jpqlFunctionStaticMethods.contains(sig))
      {
         if (sig.equals(MethodChecker.jpqlLike))
         {
            SymbExPassDown passdown = SymbExPassDown.with(val, in.isExpectingConditional);
            ColumnExpressions<?> base = val.args.get(0).visit(this, passdown);
            ColumnExpressions<?> pattern = val.args.get(1).visit(this, passdown);
            return ColumnExpressions.singleColumn(new SimpleRowReader<>(),
                  new BinaryExpression("LIKE", base.getOnlyColumn(), pattern.getOnlyColumn()));
         }
         else if (sig.equals(MethodChecker.mathAbsDouble)
               || sig.equals(MethodChecker.mathAbsInt)
               || sig.equals(MethodChecker.mathAbsLong))
         {
            SymbExPassDown passdown = SymbExPassDown.with(val, in.isExpectingConditional);
            ColumnExpressions<?> base = val.args.get(0).visit(this, passdown);
            return ColumnExpressions.singleColumn(base.reader,
                  FunctionExpression.singleParam("ABS", base.getOnlyColumn()));
         }
         else if (sig.equals(MethodChecker.mathSqrt))
         {
            SymbExPassDown passdown = SymbExPassDown.with(val, in.isExpectingConditional);
            TypedValue baseVal = val.args.get(0);
            if (isWideningCast(baseVal))
               baseVal = skipWideningCast(baseVal);
            ColumnExpressions<?> base = baseVal.visit(this, passdown);
            return ColumnExpressions.singleColumn(new SimpleRowReader<>(),
                  FunctionExpression.singleParam("SQRT", base.getOnlyColumn()));
         }
         throw new TypedValueVisitorException("Do not know how to translate the method " + sig + " into a JPQL function");
      }
      else
         return super.staticMethodCallValue(val, in);
   }

   // Tracks which numeric types are considered to have more information than
   // other types.
   static Map<Type, Integer> numericPromotionPriority = new HashMap<>();
   static {
      int n = 0;
      numericPromotionPriority.put(Type.INT_TYPE, n);
      numericPromotionPriority.put(Type.getObjectType("java/lang/Integer"), n);
      n++;
      numericPromotionPriority.put(Type.LONG_TYPE, n);
      numericPromotionPriority.put(Type.getObjectType("java/lang/Long"), n);
      n++;
      numericPromotionPriority.put(Type.getObjectType("java/math/BigInteger"), n);
      n++;
      numericPromotionPriority.put(Type.getObjectType("java/math/BigDecimal"), n);
      n++;
      numericPromotionPriority.put(Type.DOUBLE_TYPE, n);
      numericPromotionPriority.put(Type.getObjectType("java/lang/Double"), n);
      n++;
   }
}
TOP

Related Classes of org.jinq.jpa.transform.SymbExToColumns

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.