|| 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)
{