Package org.jinq.jpa.transform

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

package org.jinq.jpa.transform;

import java.util.HashSet;
import java.util.Set;

import org.jinq.jpa.jpqlquery.ColumnExpressions;
import org.jinq.jpa.jpqlquery.JPQLQuery;
import org.jinq.jpa.jpqlquery.ParameterExpression;
import org.jinq.jpa.jpqlquery.ParameterFieldExpression;
import org.jinq.jpa.jpqlquery.SimpleRowReader;
import org.objectweb.asm.Type;

import ch.epfl.labos.iu.orm.queryll2.symbolic.TypedValue;
import ch.epfl.labos.iu.orm.queryll2.symbolic.TypedValueVisitorException;

/**
* Handles the lookup of parameters passed to a lambda. Parameters can
* be used to represent query parameters or references to the data stream.
* This class handles the lookup of a data stream of the result of a
* Select..From..Where query.
*/
public class LambdaParameterArgumentHandler implements SymbExArgumentHandler
{
   LambdaAnalysis lambda;
   MetamodelUtil metamodel;
   boolean hasInQueryStreamSource;
   SymbExArgumentHandler parentArgumentScope;
   final int numLambdaCapturedArgs;
   final int numLambdaArgs;
  
   public final static Set<Type> ALLOWED_QUERY_PARAMETER_TYPES = new HashSet<>();
   static {
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.INT_TYPE);
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.DOUBLE_TYPE);
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.LONG_TYPE);
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.BOOLEAN_TYPE);
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.getObjectType("java/lang/Integer"));
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.getObjectType("java/lang/Double"));
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.getObjectType("java/lang/Long"));
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.getObjectType("java/lang/Boolean"));
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.getObjectType("java/lang/String"));
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.getObjectType("java/sql/Date"));
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.getObjectType("java/sql/Time"));
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.getObjectType("java/sql/Timestamp"));
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.getObjectType("java/util/Date"));
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.getObjectType("java/util/Calendar"));
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.getObjectType("java/math/BigDecimal"));
      ALLOWED_QUERY_PARAMETER_TYPES.add(Type.getObjectType("java/math/BigInteger"));
   }

   public LambdaParameterArgumentHandler(LambdaAnalysis lambda, MetamodelUtil metamodel, SymbExArgumentHandler parentArgumentScope, boolean hasInQueryStreamSource)
   {
      this.lambda = lambda;
      this.metamodel = metamodel;
      this.hasInQueryStreamSource = hasInQueryStreamSource;
      numLambdaCapturedArgs = lambda.getNumCapturedArgs();
      numLambdaArgs = lambda.getNumLambdaArgs();
      this.parentArgumentScope = parentArgumentScope;
   }

   protected ColumnExpressions<?> handleLambdaArg(int argIndex, Type argType) throws TypedValueVisitorException
   {
      throw new TypedValueVisitorException("Unhandled lambda arguments");
   }
  
   protected ColumnExpressions<?> handleIndirectLambdaArg(int argIndex, Type argType) throws TypedValueVisitorException
   {
      // The actual value for the parameter is not available because this is a sub-lambda.
      // Extract the parent scope to see how the parameter is used in the parent lambda
      TypedValue paramVal = lambda.getIndirectCapturedArg(argIndex);
     
      // Right now, we only support sub-lambda parameters that are simply passthroughs for
      // parameters defined in the parent lambda.
      if (paramVal instanceof TypedValue.ArgValue)
      {
         TypedValue.ArgValue paramArg = (TypedValue.ArgValue)paramVal;
         int parentArgIndex = paramArg.getIndex();
         if (parentArgumentScope == null)
            throw new TypedValueVisitorException("Cannot find a parent scope to determine how to access as sublambda's parent parameters.");
         // TODO: Right now, we need to be careful about the scope of parent lambdas. Since we only support
         // limited usage of parameters for sublambdas, it's not a problem yet, but more complicated usages
         // might be problematic. (Might have to pass additional parameteres to handleArg etc.)
         return parentArgumentScope.handleArg(parentArgIndex, argType);
      }
      else
      {
         throw new TypedValueVisitorException("Jinq can only passthrough parent lambda parameters directly to sub-lambdas. Sublambdas cannot take parameters that involve computation.");
      }
   }
  
   protected ColumnExpressions<?> getAndValidateArg(int argIndex, Type argType) throws TypedValueVisitorException
   {
      // Currently, we only support parameters of a few small simple types.
      // We should also support more complex types (e.g. entities) and allow
      // fields/methods of those entities to be called in the query (code
      // motion will be used to push those field accesses or method calls
      // outside the query where they will be evaluated and then passed in
      // as a parameter)
      if (!ALLOWED_QUERY_PARAMETER_TYPES.contains(argType) && !metamodel.isKnownEnumType(argType.getInternalName()))
         throw new TypedValueVisitorException("Accessing a field with unhandled type");

      return ColumnExpressions.singleColumn(new SimpleRowReader<>(),
            new ParameterExpression(lambda.getLambdaIndex(), argIndex));
   }
  
   @Override
   public ColumnExpressions<?> handleArg(int argIndex, Type argType) throws TypedValueVisitorException
   {
      if (argIndex < numLambdaCapturedArgs)
      {
         if (lambda == null)
            throw new TypedValueVisitorException("No lambda source was supplied where parameters can be extracted");
         if (lambda.usesIndirectArgs())
         {
            return handleIndirectLambdaArg(argIndex, argType);
         }
        
         return getAndValidateArg(argIndex, argType);
      }
      else if (checkIsInQueryStreamSource(argIndex))
         throw new TypedValueVisitorException("Using InQueryStreamSource as data");
      else
         return handleLambdaArg(argIndex - numLambdaCapturedArgs, argType);
   }

   protected JPQLQuery<?> handleLambdaSubQueryArg(int argIndex, Type argType) throws TypedValueVisitorException
   {
      throw new TypedValueVisitorException("Unhandled lambda subquery arguments");
   }
  
   @Override
   public JPQLQuery<?> handleSubQueryArg(int argIndex, Type argType) throws TypedValueVisitorException
   {
      if (argIndex >= numLambdaCapturedArgs && !checkIsInQueryStreamSource(argIndex))
      {
         return handleLambdaSubQueryArg(argIndex - numLambdaCapturedArgs, argType);
      }
      throw new TypedValueVisitorException("Cannot use parameters as a subquery");
   }

   protected ColumnExpressions<?> handleIndirectThisFieldRead(String name, Type argType) throws TypedValueVisitorException
   {
      // The actual value for the parameter is not available because this is a sub-lambda.
      // Extract the parent scope to see how the parameter is used in the parent lambda
      TypedValue paramVal = lambda.getIndirectFieldValue(name);
     
      // Right now, we only support sub-lambda parameters that are simply passthroughs for
      // parameters defined in the parent lambda.
      if (paramVal instanceof TypedValue.ArgValue)
      {
         TypedValue.ArgValue paramArg = (TypedValue.ArgValue)paramVal;
         int parentArgIndex = paramArg.getIndex();
         if (parentArgumentScope == null)
            throw new TypedValueVisitorException("Cannot find a parent scope to determine how to access as sublambda's parent parameters.");
         // TODO: Right now, we need to be careful about the scope of parent lambdas. Since we only support
         // limited usage of parameters for sublambdas, it's not a problem yet, but more complicated usages
         // might be problematic. (Might have to pass additional parameteres to handleArg etc.)
         return parentArgumentScope.handleArg(parentArgIndex, argType);
      }
      else if (paramVal instanceof TypedValue.GetFieldValue)
      {
         TypedValue.GetFieldValue paramArg = (TypedValue.GetFieldValue)paramVal;
         String parentFieldName = paramArg.name;
         if (parentArgumentScope == null)
            throw new TypedValueVisitorException("Cannot find a parent scope to determine how to access as sublambda's parent parameters.");
         // TODO: Right now, we need to be careful about the scope of parent lambdas. Since we only support
         // limited usage of parameters for sublambdas, it's not a problem yet, but more complicated usages
         // might be problematic. (Might have to pass additional parameteres to handleArg etc.)
         return parentArgumentScope.handleThisFieldRead(parentFieldName, argType);
      }
      else
      {
         throw new TypedValueVisitorException("Jinq can only passthrough parent lambda parameters directly to sub-lambdas. Sublambdas cannot take parameters that involve computation.");
      }
   }

   @Override
   public ColumnExpressions<?> handleThisFieldRead(String name, Type argType)
         throws TypedValueVisitorException
   {
      if (lambda.usesParametersAsFields())
      {
         if (lambda.usesIndirectFields())
         {
            return handleIndirectThisFieldRead(name, argType);
         }
        
         // Currently, we only support parameters of a few small simple types.
         // We should also support more complex types (e.g. entities) and allow
         // fields/methods of those entities to be called in the query (code
         // motion will be used to push those field accesses or method calls
         // outside the query where they will be evaluated and then passed in
         // as a parameter)
         if (!ALLOWED_QUERY_PARAMETER_TYPES.contains(argType) && !metamodel.isKnownEnumType(argType.getInternalName()))
            throw new TypedValueVisitorException("Accessing a field with unhandled type");

         return ColumnExpressions.singleColumn(new SimpleRowReader<>(),
               new ParameterFieldExpression(lambda.getLambdaIndex(), name));
      }
      throw new TypedValueVisitorException("Cannot read fields of this lambda");
   }

   @Override
   public JPQLQuery<?> handleSubQueryThisFieldRead(String name, Type argType)
         throws TypedValueVisitorException
   {
      throw new TypedValueVisitorException("Cannot read fields of this lambda for a subquery");
   }

  
   @Override public boolean checkIsInQueryStreamSource(int argIndex)
   {
      return hasInQueryStreamSource && argIndex == numLambdaArgs - 1;
   }
}
TOP

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

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.