Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
co.streamx.fluent.JPA.DSLInterpreter Maven / Gradle / Ivy
package co.streamx.fluent.JPA;
import static co.streamx.fluent.JPA.JPAHelpers.calcOverrides;
import static co.streamx.fluent.JPA.JPAHelpers.getAssociation;
import static co.streamx.fluent.JPA.JPAHelpers.getAssociationElementCollection;
import static co.streamx.fluent.JPA.JPAHelpers.getAssociationMTM;
import static co.streamx.fluent.JPA.JPAHelpers.getColumnNameFromProperty;
import static co.streamx.fluent.JPA.JPAHelpers.getECTableName;
import static co.streamx.fluent.JPA.JPAHelpers.getInheritanceBaseType;
import static co.streamx.fluent.JPA.JPAHelpers.getJoinTableName;
import static co.streamx.fluent.JPA.JPAHelpers.getSecondaryTable;
import static co.streamx.fluent.JPA.JPAHelpers.getTableName;
import static co.streamx.fluent.JPA.JPAHelpers.getTargetForEC;
import static co.streamx.fluent.JPA.JPAHelpers.isCollection;
import static co.streamx.fluent.JPA.JPAHelpers.isEmbeddable;
import static co.streamx.fluent.JPA.JPAHelpers.isEmbedded;
import static co.streamx.fluent.JPA.JPAHelpers.isEntityLike;
import static co.streamx.fluent.JPA.JPAHelpers.isScalar;
import static co.streamx.fluent.JPA.JPAHelpers.wrap;
import java.lang.annotation.Annotation;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jakarta.persistence.DiscriminatorColumn;
import jakarta.persistence.DiscriminatorType;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.SecondaryTable;
import co.streamx.fluent.JPA.JPAHelpers.Association;
import co.streamx.fluent.JPA.util.ScopedHashMap;
import co.streamx.fluent.extree.expression.BinaryExpression;
import co.streamx.fluent.extree.expression.BlockExpression;
import co.streamx.fluent.extree.expression.ConstantExpression;
import co.streamx.fluent.extree.expression.DelegateExpression;
import co.streamx.fluent.extree.expression.Expression;
import co.streamx.fluent.extree.expression.ExpressionType;
import co.streamx.fluent.extree.expression.ExpressionVisitor;
import co.streamx.fluent.extree.expression.InvocableExpression;
import co.streamx.fluent.extree.expression.InvocationExpression;
import co.streamx.fluent.extree.expression.LambdaExpression;
import co.streamx.fluent.extree.expression.MemberExpression;
import co.streamx.fluent.extree.expression.NewArrayInitExpression;
import co.streamx.fluent.extree.expression.ParameterExpression;
import co.streamx.fluent.extree.expression.UnaryExpression;
import co.streamx.fluent.notation.Alias;
import co.streamx.fluent.notation.Capability;
import co.streamx.fluent.notation.CommonTableExpression;
import co.streamx.fluent.notation.CommonTableExpressionType;
import co.streamx.fluent.notation.Context;
import co.streamx.fluent.notation.Keyword;
import co.streamx.fluent.notation.Literal;
import co.streamx.fluent.notation.NoOp;
import co.streamx.fluent.notation.Operator;
import co.streamx.fluent.notation.Parameter;
import co.streamx.fluent.notation.ParameterContext;
import co.streamx.fluent.notation.SubQuery;
import co.streamx.fluent.notation.TableCollection;
import co.streamx.fluent.notation.TableCollection.Property;
import co.streamx.fluent.notation.TableExtension;
import co.streamx.fluent.notation.TableExtensionType;
import co.streamx.fluent.notation.TableJoin;
import co.streamx.fluent.notation.ViewDeclaration;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
final class DSLInterpreter
implements ExpressionVisitor, Function, CharSequence>>>,
DSLInterpreterHelpers {
private static final String DTYPE = "DTYPE";
private static final Optional OPTIONAL_FALSE = Optional.of(false);
static final char UNDERSCORE_CHAR = '_';
static final char COMMA_CHAR = ',';
static final String AS = "AS";
static final String NEW_LINE = "\n";
static final String STAR = "*";
static final char QUESTION_MARK_CHAR = '?';
static final char DOT_CHAR = IdentifierPath.DOT;
static final String DOT = "" + DOT_CHAR;
static final String AND = "AND";
static final String EQUAL_SIGN = "=";
static final String KEYWORD_DELIMITER = " ";
static final String SEP_AS_SEP = KEYWORD_DELIMITER + AS + KEYWORD_DELIMITER;
static final char KEYWORD_DELIMITER_CHAR = ' ';
static final String TABLE_ALIAS_PREFIX = "t";
static final String SUB_QUERY_ALIAS_PREFIX = "q";
static final String LEFT_PARAN = "(";
static final String RIGHT_PARAN = ")";
static final char SINGLE_QUOTE_CHAR = '\'';
@NonNull
private final Set capabilities;
private SubQueryManager subQueries_ = new SubQueryManager(Collections.emptyList());
// PRIMARY value - primary, empty string - the only secondary, otherwise - named secondary
private static final String PRIMARY = new String("PRIMARY");
private final Map tableRefs = new HashMap<>();
private Map> tableSecondaryRefs = Collections.emptyMap();
private Map aliases_ = Collections.emptyMap();
private CharSequence subQueryAlias;
private List undeclaredAliases = Collections.emptyList();
private Expression argumentsTarget; // used to differentiate invocation vs. reference
// join table
private Map joinTables = Collections.emptyMap();
private Map joinTablesForFROM = Collections.emptyMap();
private Map collectionTables = Collections.emptyMap();
private Map collectionTablesForFROM = Collections.emptyMap();
private final Map parameterBackwardMap = new HashMap<>();
// View
private Map views = Collections.emptyMap();
@Getter
private final List indexedParameters = new ArrayList<>();
private ParameterContext renderingContext = ParameterContext.EXPRESSION;
private Optional collectingParameters = OPTIONAL_FALSE;
private int parameterCounter;
private int subQueriesCounter;
private boolean renderingAssociation;
private void addUndeclaredAlias(CharSequence alias) {
if (undeclaredAliases.isEmpty())
undeclaredAliases = new ArrayList<>();
undeclaredAliases.add(alias);
}
private ParameterContext getParameterContext(co.streamx.fluent.notation.Function f) {
for (Capability cap : f.parameterContextCapabilities()) {
if (capabilities.contains(cap))
return (ParameterContext) cap.getHint();
}
return f.parameterContext();
}
protected SubQueryManager getSubQueries() {
return subQueries_;
}
protected Map getAliases() {
return aliases_;
}
@Override
public Function, Function, CharSequence>> visit(BinaryExpression e) {
return eargs -> {
Expression first = bind(eargs, e.getFirst());
Expression second = bind(eargs, e.getSecond());
Function, Function, CharSequence>> efirst = first.accept(this);
Function, Function, CharSequence>> esecond = second.accept(this);
Function, CharSequence> left = efirst.apply(eargs);
Function, CharSequence> right = esecond.apply(eargs);
switch (e.getExpressionType()) {
case ExpressionType.Equal:
Map aliases = getAliases();
return (args) -> {
boolean isAssoc = (isEntityLike(first.getResultType()) || isCollection(first.getResultType()))
&& (isEntityLike(second.getResultType()) || isCollection(second.getResultType()));
if (isAssoc)
renderingAssociation = true;
CharSequence lseq = left.apply(args);
CharSequence rseq = right.apply(args);
if (isAssoc) {
renderingAssociation = false;
return renderAssociation(new StringBuilder(), getAssociation(first, second), aliases, lseq,
rseq);
}
return renderBinaryOperator(lseq, EQUAL_SIGN, rseq);
};
case ExpressionType.Add:
case ExpressionType.BitwiseAnd:
case ExpressionType.BitwiseOr:
case ExpressionType.Divide:
case ExpressionType.ExclusiveOr:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.LeftShift:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.LogicalAnd:
case ExpressionType.LogicalOr:
case ExpressionType.Modulo:
case ExpressionType.Multiply:
case ExpressionType.NotEqual:
case ExpressionType.RightShift:
case ExpressionType.Subtract:
return (args) -> {
CharSequence lseq = left.apply(args);
CharSequence rseq = right.apply(args);
String op = getOperatorSign(e.getExpressionType());
return renderBinaryOperator(lseq, op, rseq);
};
default:
throw new IllegalArgumentException(
TranslationError.UNSUPPORTED_EXPRESSION_TYPE.getError(getOperatorSign(e.getExpressionType())));
}
};
}
private StringBuilder renderAssociation(StringBuilder out,
Association assoc,
Map aliases,
CharSequence lseq,
CharSequence rseq) {
out.append(LEFT_PARAN);
for (int i = 0; i < assoc.getCardinality(); i++) {
if (out.length() > 1)
out.append(KEYWORD_DELIMITER + AND + KEYWORD_DELIMITER);
if (IdentifierPath.isResolved(lseq))
out.append(lseq);
else
out.append(resolveLabel(aliases, lseq)).append(DOT).append(assoc.getLeft().get(i));
out.append(KEYWORD_DELIMITER + EQUAL_SIGN + KEYWORD_DELIMITER);
if (IdentifierPath.isResolved(rseq))
out.append(rseq);
else
out.append(resolveLabel(aliases, rseq)).append(DOT).append(assoc.getRight().get(i));
}
return out.append(RIGHT_PARAN);
}
@Override
public Function, Function, CharSequence>> visit(ConstantExpression e) {
Object value = e.getValue();
if (value instanceof Expression) {
return eargs -> {
Expression ex = (Expression) value;
argumentsTarget = ex;
return ex.accept(this).apply(eargs);
};
}
return eargs -> {
if (value instanceof Object[] && ((Object[]) value).length == 0)
return t -> new PackedInitializers(Collections.emptyList(), Collections.emptyList(),
Collections.emptyList(), this);
if (!(value instanceof Keyword) && collectingParameters.orElse(false))
return args -> registerParameter(value);
return args -> new DynamicConstant(value, this);
};
}
public CharSequence registerParameter(Object value) {
return new ParameterRef(value, indexedParameters);
}
@Override
public Function, Function, CharSequence>> visit(InvocationExpression e) {
InvocableExpression target = e.getTarget();
Function, Function, CharSequence>> ftarget = target.accept(this);
List allArgs = e.getArguments();
Stream, Function, CharSequence>>> args = allArgs.stream()
.map(p -> isLambda(p) ? x -> y -> null : p.accept(this));
return eargs -> {
boolean isSubQuery = false;
CharSequence previousSubQueryAlias = null;
if (target.getExpressionType() == ExpressionType.MethodAccess) {
Method method = (Method) ((MemberExpression) target).getMember();
isSubQuery = method.isAnnotationPresent(SubQuery.class);
if (isSubQuery) {
previousSubQueryAlias = subQueryAlias;
subQueryAlias = new StringBuilder(SUB_QUERY_ALIAS_PREFIX).append(subQueriesCounter++);
}
}
// only for the first time
if (collectingParameters.isPresent())
collectingParameters = Optional.of(true);
List, CharSequence>> fargs = args.map(arg -> arg.apply(eargs))
.collect(Collectors.toList());
collectingParameters = Optional.empty();
Function, List> params = pp -> fargs.stream()
.map(arg -> arg.apply(pp))
.collect(Collectors.toList());
List curArgs = bind(allArgs, eargs);
argumentsTarget = target;
Function, CharSequence> fmember = ftarget.apply(curArgs);
if (target.getExpressionType() == ExpressionType.Lambda) {
return fmember.compose(params);
}
@SuppressWarnings("unchecked")
Function, Function, Function, CharSequence>>> m1 = (Function, Function, Function, CharSequence>>>) (Object) fmember;
Function, Function, CharSequence>> m = m1.apply(eargs);
if (isSubQuery) {
subQueryAlias = previousSubQueryAlias;
}
return pp -> m.apply(pp).apply(params.apply(pp));
};
}
private List bind(List allArgs,
List eargs) {
List curArgs = allArgs;
if (!eargs.isEmpty() && !curArgs.isEmpty()) {
curArgs = new ArrayList<>(curArgs);
for (int i = 0; i < curArgs.size(); i++) {
Expression a = curArgs.get(i);
if (a instanceof ParameterExpression) {
int index = ((ParameterExpression) a).getIndex();
if (index >= eargs.size())
continue;
Expression bound = eargs.get(index);
int boundExpressionType = bound.getExpressionType();
if (boundExpressionType == ExpressionType.Parameter
|| boundExpressionType == ExpressionType.Constant) {
if ((bound.getResultType() == Object.class)
|| (wrap(bound.getResultType()) == wrap(a.getResultType())))
continue;
if (boundExpressionType == ExpressionType.Constant)
bound = Expression.parameter(bound.getResultType(), index);
}
curArgs.set(i, bound);
}
}
}
return curArgs;
}
private List prepareLambdaParameters(List declared,
List arguments) {
List result = new ArrayList<>(declared);
for (int i = 0; i < arguments.size(); i++) {
if (i >= declared.size())
break;
Expression original = arguments.get(i);
Expression arg = original;
while (arg instanceof UnaryExpression)
arg = ((UnaryExpression) arg).getFirst();
if (arg.getResultType() == Object.class)
continue; // better leave parameter
// don't forward anything except LambdaExpression
if (arg instanceof ConstantExpression) {
if (!(((ConstantExpression) arg).getValue() instanceof LambdaExpression))
arg = Expression.parameter(original.getResultType(), i);
} else if (!(arg instanceof LambdaExpression)) {
ParameterExpression newParam = Expression.parameter(original.getResultType(), i);
if (arg instanceof ParameterExpression)
parameterBackwardMap.put(newParam, (ParameterExpression) arg);
arg = newParam;
}
result.set(i, arg);
}
return result;
}
@Override
public Function, Function, CharSequence>> visit(LambdaExpression> e) {
boolean skipScopeAllocation = e.getResultType() != Void.TYPE;
Function, Function, CharSequence>> ff = e.getBody().accept(this);
Stream, Function, CharSequence>>> flocals = e.getLocals()
.stream()
.map(l -> l != null ? l.accept(this) : null);
Stream, Function, CharSequence>>> fparams = e.getParameters()
.stream()
.map(p -> p.accept(this));
return eargs -> {
Map currentAliases;
SubQueryManager currentSubQueries, capturedSubQueries;
if (!skipScopeAllocation) {
currentAliases = aliases_;
aliases_ = new ScopedHashMap<>(currentAliases);
currentSubQueries = subQueries_;
capturedSubQueries = subQueries_ = new SubQueryManager(currentSubQueries);
} else {
currentAliases = null;
currentSubQueries = capturedSubQueries = null;
}
try {
List eargsFinal = argumentsTarget == e ? eargs : Collections.emptyList();
argumentsTarget = e.getBody();
List eargsPrepared = prepareLambdaParameters(e.getParameters(), eargsFinal);
Function, CharSequence> f = ff.apply(eargsPrepared);
List, CharSequence>> ple = flocals
.map(p -> p != null ? p.apply(eargsPrepared) : null)
.collect(Collectors.toList());
if (!ple.isEmpty()) {
f = f.compose(pp -> {
List npe = new ArrayList<>(pp);
ple.forEach(le -> npe.add(le != null ? le.apply(npe) : null));
return npe;
});
}
return f.compose(visitParameters(e.getParameters(), fparams, eargsFinal)).andThen(seq -> {
try {
return seq;
} finally {
if (!skipScopeAllocation)
capturedSubQueries.close();
}
});
} finally {
if (!skipScopeAllocation) {
aliases_ = currentAliases;
subQueries_ = currentSubQueries;
}
}
};
}
private Function, List> visitParameters(List original,
Stream, Function, CharSequence>>> parameters,
List eargs) {
List, CharSequence>> ppe = parameters.map(p -> p.apply(eargs))
.collect(Collectors.toList());
Function, List> params = pp -> {
pp = pp.subList(0, eargs.size());
CharSequence[] r = new CharSequence[ppe.size()];
for (int index = 0; index < r.length; index++) {
Function, CharSequence> pe = ppe.get(index);
r[original.get(index).getIndex()] = pe.apply(pp);
}
return Arrays.asList(r);
};
return params;
}
@SuppressWarnings("unchecked")
@Override
public Function, Function, CharSequence>> visit(DelegateExpression e) {
return (Function, Function, CharSequence>>) (Object) visitDelegateExpression(
e);
}
private Function, Function, Function, Function, CharSequence>>>> visitDelegateExpression(DelegateExpression e) {
Function fdelegate = LambdaExpression.compile(e.getDelegate());
return invocationArguments -> instanceArguments -> {
Expression delegate = (Expression) fdelegate.apply(instanceArguments.toArray());
argumentsTarget = delegate;
Function, CharSequence> x = delegate.accept(this).apply(invocationArguments);
return ipp -> x;
};
}
@SuppressWarnings("unchecked")
@Override
public Function, Function, CharSequence>> visit(MemberExpression e) {
return (Function, Function, CharSequence>>) (Object) visitMemberExpression(
e);
}
private Function setupParameterRenderingContext(ParameterContext newContext,
Function parameterRenderer) {
if (newContext == null)
return parameterRenderer;
return seq -> {
ParameterContext current = renderingContext;
renderingContext = newContext;
try {
return parameterRenderer.apply(seq);
} finally {
renderingContext = current;
}
};
}
private CharSequence resolveLabel(Map aliases,
CharSequence seq) {
CharSequence label = aliases.get(seq);
if (label != null)
return label;
return SubQueryManager.isSubQuery(seq) ? SubQueryManager.getName(seq) : seq;
}
private Function, Function, Function, Function, CharSequence>>>> visitMemberExpression(MemberExpression e) {
// TODO: field support ?!
final Method m = (Method) e.getMember();
Expression ei = e.getInstance();
final Function, Function, CharSequence>> finstance = ei != null
? ei.accept(this)
: null;
return invocationArguments -> instanceArguments -> {
Function, CharSequence> instance = finstance != null ? finstance.apply(instanceArguments)
: null;
Alias alias = m.getAnnotation(Alias.class);
if (alias != null) {
if (alias.value()) {
if (invocationArguments.size() > 2)
throw new UnsupportedOperationException();
Map aliases = getAliases();
return ipp -> {
CharSequence inst = instance != null ? instance.apply(ipp) : null;
return pp -> {
CharSequence aliased = inst != null ? inst : pp.get(0);
int numOfArgs = pp.size();
if (numOfArgs == 0)
return aliased;
int labelIndex = numOfArgs - 1;
CharSequence label = pp.get(labelIndex);
label = extractColumnName(label);
aliases.put(aliased, label);
return aliased;
};
};
} else {
return ipp -> pp -> {
if (!pp.isEmpty())
expandVarArgs(pp).forEach(this::addUndeclaredAlias);
return "";
};
}
}
if (m.isAnnotationPresent(Parameter.class)) {
return ipp -> pp -> {
CharSequence seq = pp.get(0);
if (!Strings.isNullOrEmpty(seq) && seq.charAt(0) == QUESTION_MARK_CHAR)
return seq;
if (!(seq instanceof DynamicConstant))
throw TranslationError.REQUIRES_EXTERNAL_PARAMETER.getError(seq);
return ((DynamicConstant) seq).registerAsParameter();
};
}
TableJoin tableJoin = m.getAnnotation(TableJoin.class);
Member tableJoinMember = (tableJoin != null)
? getJoinMember(ei, invocationArguments, instanceArguments, () -> {
if (joinTables.isEmpty()) {
joinTables = new HashMap<>();
joinTablesForFROM = new HashMap<>();
}
return joinTables;
})
: null;
TableCollection tableCol = m.getAnnotation(TableCollection.class);
Member tableColMember = (tableCol != null)
? getJoinMember(ei, invocationArguments, instanceArguments, () -> {
if (collectionTables.isEmpty()) {
collectionTables = new HashMap<>();
collectionTablesForFROM = new HashMap<>();
}
return collectionTables;
})
: null;
SubQueryManager subQueries = getSubQueries();
Map aliases = getAliases();
if (m.isAnnotationPresent(Alias.Use.class)) {
return ipp -> pp -> getAliased(pp.get(0), aliases);
}
CommonTableExpression cte = m.getAnnotation(CommonTableExpression.class);
if (cte != null) {
if (cte.value() == CommonTableExpressionType.SELF) {
CharSequence subQueryAlias = this.subQueryAlias;
return ipp -> pp -> {
return subQueries.put(pp.get(0), subQueryAlias, false);
};
}
if (cte.value() == CommonTableExpressionType.DECLARATION) {
return ipp -> pp -> {
StringBuilder with = new StringBuilder(m.getName()).append(KEYWORD_DELIMITER_CHAR);
int startLength = with.length();
for (CharSequence subQueryAlias : expandVarArgs(pp)) {
if (with.length() > startLength)
with.append(COMMA_CHAR);
CharSequence subQuery = subQueries.sealName(subQueryAlias);
if (subQuery == null) {
if (subQueryAlias instanceof DynamicConstant) {
with.append(subQueryAlias).append(KEYWORD_DELIMITER_CHAR);
startLength = with.length();
continue;
} else
throw new IllegalArgumentException("Parameter must be subQuery: " + subQueryAlias);
}
with.append(subQueryAlias)
.append(SEP_AS_SEP + NEW_LINE + LEFT_PARAN)
.append(subQuery)
.append(RIGHT_PARAN + NEW_LINE);
}
return with;
};
}
if (cte.value() == CommonTableExpressionType.REFERENCE) {
return ipp -> pp -> {
CharSequence seq = pp.get(0);
return SubQueryManager.isSubQuery(seq) ? SubQueryManager.getName(seq) : seq;
};
}
}
if (m.isAnnotationPresent(TableExtension.DiscriminatorFilter.class)) {
return ipp -> pp -> {
boolean isDiscrNumeric;
String discrColumnName;
String discrColumnValue;
Class> baseType = invocationArguments.get(0).getResultType();
DiscriminatorColumn discrColumn = baseType.getAnnotation(DiscriminatorColumn.class);
if (discrColumn != null) {
isDiscrNumeric = discrColumn.discriminatorType() == DiscriminatorType.INTEGER;
discrColumnName = discrColumn.name();
} else {
isDiscrNumeric = false;
discrColumnName = DTYPE;
}
Expression derived = invocationArguments.get(1);
Class> derivedType = derived.getResultType();
if (derivedType == Class.class) {
derivedType = (Class>) ((ConstantExpression) derived).getValue();
}
DiscriminatorValue discrValue = derivedType.getAnnotation(DiscriminatorValue.class);
if (discrValue != null)
discrColumnValue = discrValue.value();
else {
discrColumnValue = derivedType.getAnnotation(Entity.class).name();
if (discrColumnValue.isEmpty())
discrColumnValue = derivedType.getName();
}
StringBuilder out = new StringBuilder();
out.append(getAliased(pp.get(0), aliases))
.append(DOT_CHAR)
.append(discrColumnName)
.append(KEYWORD_DELIMITER_CHAR + EQUAL_SIGN + KEYWORD_DELIMITER_CHAR);
if (!isDiscrNumeric)
out.append(SINGLE_QUOTE_CHAR);
out.append(discrColumnValue);
if (!isDiscrNumeric)
out.append(SINGLE_QUOTE_CHAR);
return out;
};
}
co.streamx.fluent.notation.Function function = m.getAnnotation(co.streamx.fluent.notation.Function.class);
ParameterContext functionContext = function == null ? ParameterContext.INHERIT
: getParameterContext(function);
Annotation[][] parametersAnnotations = m.getParameterAnnotations();
List>> argsBuilder = new ArrayList<>(
invocationArguments.size());
for (int i = 0; i < invocationArguments.size(); i++) {
Expression arg = invocationArguments.get(i);
Annotation[] parameterAnnotations = parametersAnnotations[i];
Context contextAnnotation = getAnnotation(parameterAnnotations, Context.class);
ParameterContext context = calculateContext(contextAnnotation, functionContext);
if (context == ParameterContext.FROM || context == ParameterContext.FROM_WITHOUT_ALIAS)
argsBuilder.add(ex -> setupParameterRenderingContext(context,
tableReference(ex != null ? ex : arg, subQueries, aliases)));
else {
Literal literal = getAnnotation(parameterAnnotations, Literal.class);
argsBuilder.add(ex -> {
Function renderer = setupParameterRenderingContext(context,
expression(ex != null ? ex : arg, subQueries, aliases));
return literal != null
? renderer.andThen(seq -> new StringBuilder(seq.length() + 2).append(SINGLE_QUOTE_CHAR)
.append(seq)
.append(SINGLE_QUOTE_CHAR))
: renderer;
});
}
}
boolean isSubQuery = m.isAnnotationPresent(SubQuery.class);
CharSequence subQueryAlias = isSubQuery ? this.subQueryAlias : null;
return ipp -> {
StringBuilder out = new StringBuilder();
CharSequence instMutating = null;
CharSequence originalInst;
if (instance != null) {
// the table definition must come from JOIN clause
CharSequence inst = instance.apply(ipp);
originalInst = inst;
if (tableJoin != null) {
CharSequence lseq = inst;
return pp -> {
Association association = getAssociationMTM(tableJoinMember, tableJoin.inverse());
return renderAssociation(out, association, aliases, lseq, pp.get(0));
};
}
if (tableCol != null) {
CharSequence lseq = inst;
return pp -> {
Association association = getAssociationElementCollection(tableColMember);
return renderAssociation(out, association, aliases, lseq, pp.get(0));
};
}
if (m.isAnnotationPresent(TableJoin.Property.class)) {
CharSequence lseq = inst;
return pp -> {
Member member = joinTablesForFROM.get(lseq);
if (member == null)
throw TranslationError.ASSOCIATION_NOT_INITED.getError(m);
return new IdentifierPath.MultiColumnIdentifierPath(m.getName(),
clazz -> getAssociationMTM(member,
!member.getDeclaringClass().isAssignableFrom(clazz)),
null).resolveInstance(lseq, tableSecondaryRefs.get(lseq));
};
}
Property tableColProperty = m.getAnnotation(TableCollection.Property.class);
if (tableColProperty != null) {
CharSequence lseq = inst;
Member member = collectionTablesForFROM.get(lseq);
if (member == null)
throw TranslationError.ASSOCIATION_NOT_INITED.getError(m);
if (tableColProperty.owner()) {
return pp -> new IdentifierPath.MultiColumnIdentifierPath(m.getName(),
c -> getAssociationElementCollection(member), null).resolveInstance(lseq,
tableSecondaryRefs.get(lseq));
} else {
return pp -> {
Class> target = getTargetForEC(member);
if (!isEmbeddable(target))
return getColumnNameFromProperty(member).resolveInstance(lseq,
tableSecondaryRefs.get(lseq));
return calcOverrides(lseq, member, tableSecondaryRefs.get(lseq));
};
}
}
TableExtension tableExtension = m.getAnnotation(TableExtension.class);
if (tableExtension != null) {
return pp -> {
String secondary;
if (pp.size() > 1) {
CharSequence secTableName = pp.get(1);
if (!(secTableName instanceof DynamicConstant))
throw TranslationError.SECONDARY_TABLE_NOT_CONSTANT.getError(secTableName);
secondary = String.valueOf(((DynamicConstant) secTableName).getValue());
} else {
secondary = null;
}
Expression derivedExpression = invocationArguments.get(0);
Class> derivedType = derivedExpression.getResultType();
CharSequence derived = pp.get(0);
Association assoc;
if (tableExtension.value() == TableExtensionType.INHERITANCE) {
Class> base = getInheritanceBaseType(derivedType);
tableRefs.put(originalInst, getTableName(base));
registerSecondaryTable(derived, base.getName(), originalInst);
assoc = getAssociation(derivedExpression, derivedExpression);
} else {
SecondaryTable secondaryTable = getSecondaryTable(derivedType, secondary);
tableRefs.put(originalInst, getTableName(secondaryTable));
registerSecondaryTable(derived, secondaryTable.name(), originalInst);
assoc = getAssociation(derivedType, secondaryTable);
}
return renderAssociation(new StringBuilder(), assoc, aliases, originalInst, derived);
};
}
inst = resolveLabel(aliases, inst);
if (!undeclaredAliases.contains(inst)) {
out.append(IdentifierPath.current(inst));
instMutating = inst;
}
} else {
originalInst = null;
}
CharSequence instFinal = instMutating;
if (function != null) {
CharSequence functionName = function.name()
.equals(co.streamx.fluent.notation.Function.USE_METHOD_NAME)
? function.underscoresAsBlanks()
? m.getName().replace(UNDERSCORE_CHAR, KEYWORD_DELIMITER_CHAR)
: m.getName()
: function.name();
Operator operator = m.getAnnotation(Operator.class);
return pp -> {
if (m.isAnnotationPresent(ViewDeclaration.Columns.class)) {
View view = views.get(originalInst);
argsBuilder.add(ex -> p -> view.getColumns());
out.setLength(0);
}
List originalParams = pp;
CharSequence currentSubQuery = (cte != null
&& cte.value() == CommonTableExpressionType.DECORATOR) ? subQueries.sealName(pp.get(0))
: null;
if (instance != null && out.length() > 0) {
out.append(KEYWORD_DELIMITER_CHAR);
}
List> argsBuilderBound = new ArrayList<>();
pp = expandVarArgs(pp, argsBuilder, argsBuilderBound);
ViewDeclaration.From viewFrom = m.getAnnotation(ViewDeclaration.From.class);
if (viewFrom != null) {
View view = views.get(originalInst);
Function> paramBuilder = limit -> p -> {
if (p != null) {
return (p instanceof ParameterRef)
? view.getSelect(((ParameterRef) p).getValue(), limit, viewFrom.aliased())
: view.getSelect(resolveLabel(aliases, p), limit, viewFrom.aliased());
} else
return view.getSelect();
};
int size = argsBuilderBound.size();
if (argsBuilderBound.isEmpty())
argsBuilderBound.add(paramBuilder.apply(0));
else
argsBuilderBound.set(0, paramBuilder.apply(1 - size));
if (viewFrom.aliased()) {
for (int i = 1; i < size; i++) {
int ip = i - size;
Function bound = argsBuilderBound.get(i);
argsBuilderBound.set(i, bound.andThen(p -> view.getColumn(p, ip)));
}
}
out.setLength(0);
}
if (m.isAnnotationPresent(ViewDeclaration.Alias.class)) {
View view = views.get(originalInst);
argsBuilderBound.clear();
for (int i = 0; i < pp.size(); i++) {
int ip = i;
argsBuilderBound.add(p -> view.getColumn(p, ip));
}
out.setLength(0);
}
Stream args = Streams.zip(pp, argsBuilderBound, (arg,
builder) -> builder.apply(arg));
String delimiter = function.omitArgumentsDelimiter() ? KEYWORD_DELIMITER
: function.argumentsDelimiter() + KEYWORD_DELIMITER;
boolean omitParentheses;
if (operator == null) {
omitParentheses = function.omitParentheses()
|| (function.omitParenthesesIfArgumentless() && argsBuilderBound.isEmpty());
out.append(functionName);
out.append(omitParentheses ? KEYWORD_DELIMITER : LEFT_PARAN);
String collectedArgs = args.collect(Collectors.joining(delimiter));
out.append(collectedArgs);
} else {
omitParentheses = function.omitParentheses();
out.append(omitParentheses ? KEYWORD_DELIMITER : LEFT_PARAN);
Iterator it = args.iterator();
CharSequence next = it.next();
if (operator.right()) {
out.append(next).append(KEYWORD_DELIMITER).append(functionName);
} else {
out.append(functionName).append(KEYWORD_DELIMITER).append(next);
}
if (it.hasNext()) {
out.append(operator.omitParentheses() ? KEYWORD_DELIMITER : LEFT_PARAN);
do {
next = it.next();
out.append(next).append(delimiter);
} while (it.hasNext());
out.setLength(out.length() - delimiter.length());
out.append(operator.omitParentheses() ? KEYWORD_DELIMITER : RIGHT_PARAN);
}
}
out.append(omitParentheses ? KEYWORD_DELIMITER : RIGHT_PARAN);
args.close();
if (m.isAnnotationPresent(NoOp.class)) {
return null;
}
if (currentSubQuery != null) // decorator is optional
return handleView(subQueries.put(out, currentSubQuery), m, originalParams);
if (function.requiresAlias()) {
StringBuilder implicitAlias = new StringBuilder(TABLE_ALIAS_PREFIX)
.append(parameterCounter++);
if (function.omitParentheses()) {
RequiresParenthesesInAS specialCase = new RequiresParenthesesInAS(out);
aliases.put(specialCase, implicitAlias);
return specialCase;
}
aliases.put(out, implicitAlias);
}
return handleView(out, m, originalParams);
};
}
if (isSubQuery) {
return pp -> subQueries.put(subQueryAlias, pp.get(0));
}
return pp -> {
if (renderingAssociation) {
// we cannot resolve other side of the association without this side
// so let's handle them together
return out;
}
if (isEmbeddable(m.getReturnType()) || isCollection(m.getReturnType()) || isEmbedded(m))
// embedded
return calcOverrides(out, (Member) m, tableSecondaryRefs.get(out));
IdentifierPath columnName = getColumnNameFromProperty(m);
if (m.getParameterCount() > 0) // assignment
return new StringBuilder(
columnName.resolveInstance(instFinal, true, tableSecondaryRefs.get(instFinal)))
.append(KEYWORD_DELIMITER + EQUAL_SIGN + KEYWORD_DELIMITER)
.append(pp.get(0));
if (instFinal != null) {
return columnName.resolveInstance(instFinal, tableSecondaryRefs.get(instFinal));
}
out.append(columnName);
return out;
};
};
};
}
private Member getJoinMember(Expression ei,
List invocationArguments,
List instanceArguments,
Supplier> joinMapSupplier) {
Member tableJoinMember;
if (!(ei instanceof ParameterExpression)) {
throw TranslationError.INSTANCE_NOT_JOINTABLE.getError(ei);
}
Expression actual = instanceArguments.get(((ParameterExpression) ei).getIndex());
if (!(actual instanceof ParameterExpression)) {
throw TranslationError.INSTANCE_NOT_JOINTABLE.getError(ei);
}
ParameterExpression actualParam = (ParameterExpression) actual;
Map joinMap = joinMapSupplier.get();
Expression arg = invocationArguments.get(1);
while (arg instanceof UnaryExpression)
arg = ((UnaryExpression) arg).getFirst();
if (!(arg instanceof LambdaExpression)) {
throw TranslationError.NOT_PROPERTY_CALL.getError(arg);
}
LambdaExpression> lambda = (LambdaExpression>) arg;
arg = lambda.getBody();
if (!(arg instanceof InvocationExpression)) {
throw TranslationError.NOT_PROPERTY_CALL.getError(arg);
}
InvocableExpression target = ((InvocationExpression) arg).getTarget();
if (!(target instanceof MemberExpression)) {
throw TranslationError.NOT_PROPERTY_CALL.getError(target);
}
tableJoinMember = ((MemberExpression) target).getMember();
while (actualParam != null) {
joinMap.put(actualParam, tableJoinMember);
actualParam = parameterBackwardMap.get(actualParam);
}
return tableJoinMember;
}
private CharSequence handleView(CharSequence result,
final Method m,
List originalParams) {
if (m.isAnnotationPresent(ViewDeclaration.class)) {
if (views.isEmpty())
views = new HashMap<>();
views.put(result, new View((PackedInitializers) originalParams.get(1)));
}
return result;
}
// terminal function
private Function tableReference(Expression e,
SubQueryManager subQueries,
Map aliases) {
return seq -> {
return handleFromClause(seq, e, aliases, subQueries);
};
}
private CharSequence resolveTableName(CharSequence seq,
Class> resultType) {
Member joinTable = joinTablesForFROM.get(seq);
if (joinTable != null)
return getJoinTableName(joinTable);
joinTable = collectionTablesForFROM.get(seq);
if (joinTable != null)
return getECTableName(joinTable);
return getTableName(resultType);
}
private CharSequence handleFromClause(CharSequence seq,
Expression e,
Map aliases,
SubQueryManager subQueries) {
Class> resultType = e.getResultType();
if (!isCollection(resultType) && !isScalar(resultType) && (Object.class != resultType)
&& !isEntityLike(resultType))
throw TranslationError.INVALID_FROM_PARAM.getError(resultType);
CharSequence label = aliases.get(seq);
boolean hasLabel = label != null;
if (!hasLabel)
label = seq;
if (SubQueryManager.isSubQuery(seq)) {
CharSequence name = SubQueryManager.getName(seq);
if (name != seq) {
if (!hasLabel)
label = name;
StringBuilder fromBuilder = SubQueryManager.isRequiresParentheses(seq)
? new StringBuilder(LEFT_PARAN).append(seq).append(RIGHT_PARAN)
: new StringBuilder(seq);
fromBuilder.append(capabilities.contains(Capability.TABLE_AS_ALIAS) ? SEP_AS_SEP : KEYWORD_DELIMITER);
return fromBuilder.append(label);
}
return label;
}
String refName = tableRefs.get(seq);
CharSequence tableName = refName != null ? refName == PRIMARY ? resolveTableName(seq, resultType) : refName
: null;
if (hasLabel && tableName == null)
tableName = seq instanceof RequiresParenthesesInAS
? new StringBuilder(LEFT_PARAN).append(((RequiresParenthesesInAS) seq).getWrapped())
.append(RIGHT_PARAN)
: seq;
if (tableName != null) {
if (renderingContext == ParameterContext.FROM
|| (renderingContext == ParameterContext.FROM_WITHOUT_ALIAS && hasLabel)) {
undeclaredAliases.remove(label);
StringBuilder fromBuilder = new StringBuilder(tableName);
fromBuilder.append(capabilities.contains(Capability.TABLE_AS_ALIAS) ? SEP_AS_SEP : KEYWORD_DELIMITER);
return fromBuilder.append(label);
} else {
addUndeclaredAlias(label);
return tableName;
}
}
return seq;
}
// terminal function
private Function expression(Expression e,
SubQueryManager subQueries,
Map aliases) {
boolean isEntity = isEntityLike(e.getResultType());
return seq -> {
if (renderingContext == ParameterContext.ALIAS)
return extractColumnName(seq);
if (e instanceof ParameterExpression) {
if (isEntity) {
switch (renderingContext) {
case SELECT:
if (IdentifierPath.isResolved(seq))
break;
seq = resolveLabel(aliases, seq);
return new StringBuilder(seq).append(DOT + STAR);
case FROM:
case FROM_WITHOUT_ALIAS:
return handleFromClause(seq, e, aliases, subQueries);
default:
}
}
}
if (renderingContext == ParameterContext.SELECT) {
CharSequence label = aliases.get(seq);
if (label != null) {
return new StringBuilder(verifyParentheses(seq)).append(SEP_AS_SEP).append(label);
}
}
return verifyParentheses(seq);
};
}
@Override
public Function, Function, CharSequence>> visit(ParameterExpression e) {
return eargs -> {
return t -> {
final int index = e.getIndex();
if (t.isEmpty() || index >= t.size()) {
Class> resultType = e.getResultType();
if (!isEntityLike(resultType))
throw TranslationError.CANNOT_CALCULATE_TABLE_REFERENCE.getError(resultType);
CharSequence tableRef = calcOverrides(
new StringBuilder(TABLE_ALIAS_PREFIX).append(parameterCounter++), resultType, null);
tableRefs.put(tableRef, PRIMARY);
return registerJoinTable(tableRef, e);
}
return registerJoinTable(t.get(index), e);
};
};
}
private void registerSecondaryTable(CharSequence primary,
String secondary,
CharSequence secondaryAlias) {
if (tableSecondaryRefs.isEmpty())
tableSecondaryRefs = new HashMap<>();
tableSecondaryRefs.computeIfAbsent(primary, x -> new HashMap<>()).put(secondary, secondaryAlias);
}
private CharSequence registerJoinTable(CharSequence seq,
Expression e) {
Member joinTable = joinTables.get(e);
if (joinTable != null)
joinTablesForFROM.put(seq, joinTable);
else {
joinTable = collectionTables.get(e);
if (joinTable != null)
collectionTablesForFROM.put(seq, joinTable);
}
return seq;
}
@Override
public Function, Function, CharSequence>> visit(UnaryExpression e) {
Function, Function, CharSequence>> ffirst = e.getFirst().accept(this);
return eargs -> {
Function, CharSequence> first = ffirst.apply(eargs);
switch (e.getExpressionType()) {
case ExpressionType.IsNull:
return (args) -> UnaryOperator.IsNull.eval(first.apply(args));
case ExpressionType.IsNonNull:
return (args) -> UnaryOperator.IsNonNull.eval(first.apply(args));
case ExpressionType.Convert:
return first::apply;
case ExpressionType.LogicalNot:
return (args) -> UnaryOperator.LogicalNot.eval(first.apply(args));
case ExpressionType.Negate:
return (args) -> UnaryOperator.Negate.eval(first.apply(args));
case ExpressionType.BitwiseNot:
return (args) -> UnaryOperator.BitwiseNot.eval(first.apply(args));
default:
throw new IllegalArgumentException(
TranslationError.UNSUPPORTED_EXPRESSION_TYPE.getError(getOperatorSign(e.getExpressionType())));
}
};
}
@Override
public Function, Function, CharSequence>> visit(BlockExpression e) {
Stream, Function, CharSequence>>> fexprs = e.getExpressions()
.stream()
.map(p -> p.accept(this));
return eargs -> {
collectingParameters = Optional.empty();
List, CharSequence>> ppe = fexprs.map(p -> p.apply(eargs))
.collect(Collectors.toList());
return t -> ppe.stream().map(pe -> {
ParameterContext previousRenderingContext = this.renderingContext;
renderingContext = ParameterContext.EXPRESSION;
try {
return Strings.trim(pe.apply(t));
} finally {
renderingContext = previousRenderingContext;
}
}).filter(seq -> !Strings.isNullOrEmpty(seq)).collect(Collectors.joining(NEW_LINE));
};
}
@Override
public Function, Function, CharSequence>> visit(NewArrayInitExpression e) {
List allArgs = e.getInitializers();
Stream, Function, CharSequence>>> fexprs = allArgs.stream()
.map(p -> p.accept(this));
return eargs -> {
List, CharSequence>> ppe = fexprs.map(p -> p.apply(eargs))
.collect(Collectors.toList());
List curArgs = bind(allArgs, eargs);
return t -> new PackedInitializers(curArgs, ppe, t, this);
};
}
}