/* * Copyright 2015, The Querydsl Team (http://www.querydsl.com/team) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.querydsl.core.types; import java.io.Serializable; import java.math.BigDecimal; import java.util.List; import java.util.Set; import javax.annotation.concurrent.Immutable; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.util.MathUtils; /** * {@code Template} provides serialization templates for {@link Operation}, * {@link TemplateExpression} and {@link Path} serialization * * @author tiwe * */ @Immutable public final class Template implements Serializable { private static final long serialVersionUID = -1697705745769542204L; private static final Set<? extends Operator> CONVERTIBLES = Sets.immutableEnumSet(Ops.ADD, Ops.SUB); /** * General template element */ @Immutable public abstract static class Element implements Serializable { private static final long serialVersionUID = 3396877288101929387L; public abstract Object convert(List<?> args); public abstract boolean isString(); } /** * Expression as string */ public static final class AsString extends Element { private static final long serialVersionUID = -655362047873616197L; private final int index; private final String toString; public AsString(int index) { this.index = index; this.toString = index + "s"; } @Override public Object convert(final List<?> args) { final Object arg = args.get(index); return arg instanceof Constant<?> ? arg.toString() : arg; } public int getIndex() { return index; } @Override public boolean isString() { return true; } @Override public String toString() { return toString; } } /** * Static text element */ public static final class StaticText extends Element { private static final long serialVersionUID = -2791869625053368023L; private final String text; private final String toString; public StaticText(String text) { this.text = text; this.toString = "'" + text + "'"; } public String getText() { return text; } @Override public boolean isString() { return true; } @Override public Object convert(List<?> args) { return text; } @Override public String toString() { return toString; } } /** * Transformed expression */ public static final class Transformed extends Element { private static final long serialVersionUID = 702677732175745567L; private final int index; private final transient Function<Object, Object> transformer; private final String toString; public Transformed(int index, Function<Object, Object> transformer) { this.index = index; this.transformer = transformer; this.toString = String.valueOf(index); } public int getIndex() { return index; } @Override public Object convert(final List<?> args) { return transformer.apply(args.get(index)); } @Override public boolean isString() { return false; } @Override public String toString() { return toString; } } /** * Argument by index */ public static final class ByIndex extends Element { private static final long serialVersionUID = 4711323946026029998L; private final int index; private final String toString; public ByIndex(int index) { this.index = index; this.toString = String.valueOf(index); } @Override public Object convert(final List<?> args) { final Object arg = args.get(index); if (arg instanceof Expression) { return ExpressionUtils.extract((Expression<?>) arg); } else { return arg; } } public int getIndex() { return index; } @Override public boolean isString() { return false; } @Override public String toString() { return toString; } } /** * Math operation */ public static final class Operation extends Element { private static final long serialVersionUID = 1400801176778801584L; private final int index1, index2; private final Operator operator; private final boolean asString; public Operation(int index1, int index2, Operator operator, boolean asString) { this.index1 = index1; this.index2 = index2; this.operator = operator; this.asString = asString; } @Override public Object convert(List<?> args) { Object arg1 = args.get(index1); Object arg2 = args.get(index2); if (isNumber(arg1) && isNumber(arg2)) { return MathUtils.result(asNumber(arg1), asNumber(arg2), operator); } else { Expression<?> expr1 = asExpression(arg1); Expression<?> expr2 = asExpression(arg2); if (arg2 instanceof Number) { if (CONVERTIBLES.contains(operator) && expr1 instanceof com.querydsl.core.types.Operation) { com.querydsl.core.types.Operation operation = (com.querydsl.core.types.Operation) expr1; if (CONVERTIBLES.contains(operation.getOperator()) && operation.getArg(1) instanceof Constant) { Number num1 = ((Constant<Number>) operation.getArg(1)).getConstant(); Number num2; if (operator == operation.getOperator()) { num2 = MathUtils.result(num1, (Number) arg2, Ops.ADD); } else if (operator == Ops.ADD) { num2 = MathUtils.result((Number) arg2, num1, Ops.SUB); } else { num2 = MathUtils.result(num1, (Number) arg2, Ops.SUB); } return ExpressionUtils.operation(expr1.getType(), operator, operation.getArg(0), Expressions.constant(num2)); } } } return ExpressionUtils.operation(expr1.getType(), operator, expr1, expr2); } } @Override public boolean isString() { return asString; } @Override public String toString() { return index1 + " " + operator + " " + index2; } } /** * Math operation with constant */ public static final class OperationConst extends Element { private static final long serialVersionUID = 1400801176778801584L; private final int index1; private final Number arg2; private final Expression<Number> expr2; private final Operator operator; private final boolean asString; @Deprecated public OperationConst(int index1, BigDecimal arg2, Operator operator, boolean asString) { this(index1, (Number) arg2, operator, asString); } public OperationConst(int index1, Number arg2, Operator operator, boolean asString) { this.index1 = index1; this.arg2 = arg2; this.expr2 = Expressions.constant(arg2); this.operator = operator; this.asString = asString; } @Override public Object convert(List<?> args) { Object arg1 = args.get(index1); if (isNumber(arg1)) { return MathUtils.result(asNumber(arg1), arg2, operator); } else { Expression<?> expr1 = asExpression(arg1); if (CONVERTIBLES.contains(operator) && expr1 instanceof com.querydsl.core.types.Operation) { com.querydsl.core.types.Operation operation = (com.querydsl.core.types.Operation) expr1; if (CONVERTIBLES.contains(operation.getOperator()) && operation.getArg(1) instanceof Constant) { Number num1 = ((Constant<Number>) operation.getArg(1)).getConstant(); Number num2; if (operator == operation.getOperator()) { num2 = MathUtils.result(num1, arg2, Ops.ADD); } else if (operator == Ops.ADD) { num2 = MathUtils.result(arg2, num1, Ops.SUB); } else { num2 = MathUtils.result(num1, arg2, Ops.SUB); } return ExpressionUtils.operation(expr1.getType(), operator, operation.getArg(0), Expressions.constant(num2)); } } return ExpressionUtils.operation(expr1.getType(), operator, expr1, expr2); } } @Override public boolean isString() { return asString; } @Override public String toString() { return index1 + " " + operator + " " + arg2; } } private final ImmutableList<Element> elements; private final String template; Template(String template, ImmutableList<Element> elements) { this.template = template; this.elements = elements; } public List<Element> getElements() { return elements; } @Override public String toString() { return template; } @Override public boolean equals(Object o) { if (o == this) { return true; } else if (o instanceof Template) { return ((Template) o).template.equals(template); } else { return false; } } @Override public int hashCode() { return template.hashCode(); } private static Number asNumber(Object arg) { if (arg instanceof Number) { return (Number) arg; } else if (arg instanceof Constant) { return (Number) ((Constant) arg).getConstant(); } else { throw new IllegalArgumentException(arg.toString()); } } private static boolean isNumber(Object o) { return o instanceof Number || o instanceof Constant && ((Constant<?>) o).getConstant() instanceof Number; } private static Expression<?> asExpression(Object arg) { if (arg instanceof Expression) { return ExpressionUtils.extract((Expression<?>) arg); } else { return Expressions.constant(arg); } } }