/* * 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.util.*; import javax.annotation.Nullable; import com.google.common.collect.ImmutableList; import com.querydsl.core.BooleanBuilder; import com.querydsl.core.QueryException; /** * {@code ExpressionUtils} provides utilities for constructing common operation instances. This class is * used internally in Querydsl and is not suitable to be used in cases where DSL methods are needed, * since the Expression implementations used in this class are minimal internal implementations. * * @author tiwe * */ public final class ExpressionUtils { private static final class UnderscoreTemplates extends Templates { private UnderscoreTemplates() { add(PathType.PROPERTY, "{0}_{1}"); add(PathType.COLLECTION_ANY, "{0}"); add(PathType.LISTVALUE, "{0}_{1}"); add(PathType.LISTVALUE_CONSTANT, "{0}_{1}"); } } private static final Templates TEMPLATES = new UnderscoreTemplates(); /** * Create a new Operation expression * * @param type type of expression * @param operator operator * @param args operation arguments * @return operation expression */ public static <T> Operation<T> operation(Class<? extends T> type, Operator operator, Expression<?>... args) { return operation(type, operator, ImmutableList.copyOf(args)); } /** * Create a new Operation expression * * @param type type of expression * @param operator operator * @param args operation arguments * @return operation expression */ @SuppressWarnings("unchecked") public static <T> Operation<T> operation(Class<? extends T> type, Operator operator, ImmutableList<Expression<?>> args) { if (type.equals(Boolean.class)) { return (Operation<T>) new PredicateOperation(operator, args); } else { return new OperationImpl<T>(type, operator, args); } } /** * Create a new Operation expression * * @param operator operator * @param args operation arguments * @return operation expression */ public static PredicateOperation predicate(Operator operator, Expression<?>... args) { return predicate(operator, ImmutableList.copyOf(args)); } /** * Create a new Operation expression * * @param operator operator * @param args operation arguments * @return operation expression */ public static PredicateOperation predicate(Operator operator, ImmutableList<Expression<?>> args) { return new PredicateOperation(operator, args); } /** * Create a new Path expression * * @param type type of expression * @param variable variable name * @return path expression */ public static <T> Path<T> path(Class<? extends T> type, String variable) { return new PathImpl<T>(type, variable); } /** * Create a new Path expression * * @param type type of expression * @param parent parent path * @param property property name * @return property path */ public static <T> Path<T> path(Class<? extends T> type, Path<?> parent, String property) { return new PathImpl<T>(type, parent, property); } /** * Create a new Path expression * * @param type type of expression * @param metadata path metadata * @param <T> type of expression * @return path expression */ public static <T> Path<T> path(Class<? extends T> type, PathMetadata metadata) { return new PathImpl<T>(type, metadata); } /** * Create a new Template expression * * @param template template * @param args template parameters * @return template expression */ public static PredicateTemplate predicateTemplate(String template, Object... args) { return predicateTemplate(TemplateFactory.DEFAULT.create(template), ImmutableList.copyOf(args)); } /** * Create a new Template expression * * @deprecated Use {@link #predicateTemplate(String, List)} instead. * * @param template template * @param args template parameters * @return template expression */ @Deprecated public static PredicateTemplate predicateTemplate(String template, ImmutableList<?> args) { return predicateTemplate(TemplateFactory.DEFAULT.create(template), args); } /** * Create a new Template expression * * @param template template * @param args template parameters * @return template expression */ public static PredicateTemplate predicateTemplate(String template, List<?> args) { return predicateTemplate(TemplateFactory.DEFAULT.create(template), args); } /** * Create a new Template expression * * @param template template * @param args template parameters * @return template expression */ public static PredicateTemplate predicateTemplate(Template template, Object... args) { return predicateTemplate(template, ImmutableList.copyOf(args)); } /** * Create a new Template expression * * @deprecated Use {@link #predicateTemplate(Template, List)} instead. * * @param template template * @param args template parameters * @return template expression */ @Deprecated public static PredicateTemplate predicateTemplate(Template template, ImmutableList<?> args) { return new PredicateTemplate(template, args); } /** * Create a new Template expression * * @param template template * @param args template parameters * @return template expression */ public static PredicateTemplate predicateTemplate(Template template, List<?> args) { return new PredicateTemplate(template, ImmutableList.copyOf(args)); } /** * Create a new Template expression * * @param cl type of expression * @param template template * @param args template parameters * @return template expression */ public static <T> TemplateExpression<T> template(Class<? extends T> cl, String template, Object... args) { return template(cl, TemplateFactory.DEFAULT.create(template), ImmutableList.copyOf(args)); } /** * Create a new Template expression * * @deprecated Use {@link #template(Class, String, List)} instead. * * @param cl type of expression * @param template template * @param args template parameters * @return template expression */ @Deprecated public static <T> TemplateExpression<T> template(Class<? extends T> cl, String template, ImmutableList<?> args) { return template(cl, TemplateFactory.DEFAULT.create(template), args); } /** * Create a new Template expression * * @param cl type of expression * @param template template * @param args template parameters * @return template expression */ public static <T> TemplateExpression<T> template(Class<? extends T> cl, String template, List<?> args) { return template(cl, TemplateFactory.DEFAULT.create(template), args); } /** * Create a new Template expression * * @param cl type of expression * @param template template * @param args template parameters * @return template expression */ public static <T> TemplateExpression<T> template(Class<? extends T> cl, Template template, Object... args) { return template(cl, template, ImmutableList.copyOf(args)); } /** * Create a new Template expression * * @deprecated Use {@link #template(Class, Template, List)} instead. * * @param cl type of expression * @param template template * @param args template parameters * @return template expression */ @SuppressWarnings("unchecked") @Deprecated public static <T> TemplateExpression<T> template(Class<? extends T> cl, Template template, ImmutableList<?> args) { if (cl.equals(Boolean.class)) { return (TemplateExpression<T>) new PredicateTemplate(template, args); } else { return new TemplateExpressionImpl<T>(cl, template, args); } } /** * Create a new Template expression * * @param cl type of expression * @param template template * @param args template parameters * @return template expression */ @SuppressWarnings("unchecked") public static <T> TemplateExpression<T> template(Class<? extends T> cl, Template template, List<?> args) { if (cl.equals(Boolean.class)) { return (TemplateExpression<T>) new PredicateTemplate(template, ImmutableList.copyOf(args)); } else { return new TemplateExpressionImpl<T>(cl, template, ImmutableList.copyOf(args)); } } /** * Create a {@code all col} expression * * @param col collection expression * @return all col */ @SuppressWarnings("unchecked") public static <T> Expression<T> all(CollectionExpression<?, ? super T> col) { return new OperationImpl<T>((Class<T>) col.getParameter(0), Ops.QuantOps.ALL, ImmutableList.<Expression<?>>of(col)); } /** * Create a {@code any col} expression * * @param col collection expression * @return any col */ @SuppressWarnings("unchecked") public static <T> Expression<T> any(CollectionExpression<?, ? super T> col) { return new OperationImpl<T>((Class<T>) col.getParameter(0), Ops.QuantOps.ANY, ImmutableList.<Expression<?>>of(col)); } /** * Create a {@code all col} expression * * @param col subquery expression * @return all col */ @SuppressWarnings("unchecked") public static <T> Expression<T> all(SubQueryExpression<? extends T> col) { return new OperationImpl<T>(col.getType(), Ops.QuantOps.ALL, ImmutableList.<Expression<?>>of(col)); } /** * Create a {@code any col} expression * * @param col subquery expression * @return any col */ @SuppressWarnings("unchecked") public static <T> Expression<T> any(SubQueryExpression<? extends T> col) { return new OperationImpl<T>(col.getType(), Ops.QuantOps.ANY, ImmutableList.<Expression<?>>of(col)); } /** * Create the intersection of the given arguments * * @param exprs predicates * @return intersection */ @Nullable public static Predicate allOf(Collection<Predicate> exprs) { Predicate rv = null; for (Predicate b : exprs) { if (b != null) { rv = rv == null ? b : ExpressionUtils.and(rv,b); } } return rv; } /** * Create the intersection of the given arguments * * @param exprs predicates * @return intersection */ @Nullable public static Predicate allOf(Predicate... exprs) { Predicate rv = null; for (Predicate b : exprs) { if (b != null) { rv = rv == null ? b : ExpressionUtils.and(rv,b); } } return rv; } /** * Create the intersection of the given arguments * * @param left lhs of expression * @param right rhs of expression * @return left and right */ public static Predicate and(Predicate left, Predicate right) { left = (Predicate) extract(left); right = (Predicate) extract(right); if (left == null) { return right; } else if (right == null) { return left; } else { return predicate(Ops.AND, left, right); } } /** * Create the union of the given arguments * * @param exprs predicate * @return union */ @Nullable public static Predicate anyOf(Collection<Predicate> exprs) { Predicate rv = null; for (Predicate b : exprs) { if (b != null) { rv = rv == null ? b : ExpressionUtils.or(rv,b); } } return rv; } /** * Create the union of the given arguments * * @param exprs predicates * @return union */ @Nullable public static Predicate anyOf(Predicate... exprs) { Predicate rv = null; for (Predicate b : exprs) { if (b != null) { rv = rv == null ? b : ExpressionUtils.or(rv,b); } } return rv; } /** * Create an alias expression with the given source and alias * * @param <D> type of expression * @param source source * @param alias alias * @return source as alias */ public static <D> Expression<D> as(Expression<D> source, Path<D> alias) { return operation(alias.getType(), Ops.ALIAS, source, alias); } /** * Create an alias expression with the given source and alias * * @param <D> type of expression * @param source source * @param alias alias * @return source as alias */ public static <D> Expression<D> as(Expression<D> source, String alias) { return as(source, path(source.getType(), alias)); } /** * Create a {@code count(source)} expression * * @param source source * @return count(source) */ public static Expression<Long> count(Expression<?> source) { return operation(Long.class, Ops.AggOps.COUNT_AGG, source); } /** * Create a {@code left == constant} expression * * @param <D> type of expressions * @param left lhs of expression * @param constant rhs of expression * @return left == constant */ public static <D> Predicate eqConst(Expression<D> left, D constant) { return eq(left, ConstantImpl.create(constant)); } /** * Create a {@code left == right} expression * * @param <D> type of expressions * @param left lhs of expression * @param right rhs of expression * @return left == right */ public static <D> Predicate eq(Expression<D> left, Expression<? extends D> right) { return predicate(Ops.EQ, left, right); } /** * Create a {@code left in right} expression * * @param <D> type of expressions * @param left lhs of expression * @param right rhs of expression * @return left in right */ public static <D> Predicate in(Expression<D> left, CollectionExpression<?,? extends D> right) { return predicate(Ops.IN, left, right); } /** * Create a {@code left in right} expression * * @param <D> type of expressions * @param left lhs of expression * @param right rhs of expression * @return left in right */ public static <D> Predicate in(Expression<D> left, SubQueryExpression<? extends D> right) { return predicate(Ops.IN, left, right); } /** * Create a {@code left in right} expression * * @param <D> element type * @param left lhs of expression * @param right rhs of expression * @return left in right */ public static <D> Predicate in(Expression<D> left, Collection<? extends D> right) { if (right.size() == 1) { return eqConst(left, right.iterator().next()); } else { return predicate(Ops.IN, left, ConstantImpl.create(right)); } } /** * Create a {@code left in right or...} expression for each list * * @param <D> element type * @param left * @param lists * @return a {@code left in right or...} expression */ public static <D> Predicate inAny(Expression<D> left, Iterable<? extends Collection<? extends D>> lists) { BooleanBuilder rv = new BooleanBuilder(); for (Collection<? extends D> list : lists) { rv.or(in(left, list)); } return rv; } /** * Create a {@code left is null} expression * * @param left operation argument * @return left is null */ public static Predicate isNull(Expression<?> left) { return predicate(Ops.IS_NULL, left); } /** * Create a {@code left is not null} expression * * @param left operation argument * @return left is null */ public static Predicate isNotNull(Expression<?> left) { return predicate(Ops.IS_NOT_NULL, left); } /** * Convert the given like pattern to a regex pattern * * @param expr expression to convert * @return converted expression */ public static Expression<String> likeToRegex(Expression<String> expr) { return likeToRegex(expr, true); } /** * Convert the given like pattern to a regex pattern * * @param expr expression to be converted * @param matchStartAndEnd if start and end should be matched as well * @return converted expression */ @SuppressWarnings("unchecked") public static Expression<String> likeToRegex(Expression<String> expr, boolean matchStartAndEnd) { // TODO : this should take the escape character into account if (expr instanceof Constant<?>) { final String like = expr.toString(); final StringBuilder rv = new StringBuilder(like.length() + 4); if (matchStartAndEnd && !like.startsWith("%")) { rv.append('^'); } for (int i = 0; i < like.length(); i++) { char ch = like.charAt(i); if (ch == '.' || ch == '*' || ch == '?') { rv.append('\\'); } else if (ch == '%') { rv.append(".*"); continue; } else if (ch == '_') { rv.append('.'); continue; } rv.append(ch); } if (matchStartAndEnd && !like.endsWith("%")) { rv.append('$'); } if (!like.equals(rv.toString())) { return ConstantImpl.create(rv.toString()); } } else if (expr instanceof Operation<?>) { Operation<?> o = (Operation<?>) expr; if (o.getOperator() == Ops.CONCAT) { Expression<String> lhs = likeToRegex((Expression<String>) o.getArg(0), false); Expression<String> rhs = likeToRegex((Expression<String>) o.getArg(1), false); if (lhs != o.getArg(0) || rhs != o.getArg(1)) { return operation(String.class, Ops.CONCAT, lhs, rhs); } } } return expr; } /** * Create a list expression for the given arguments * * @param exprs list elements * @return list expression */ public static <T> Expression<T> list(Class<T> clazz, Expression<?>... exprs) { return list(clazz, ImmutableList.copyOf(exprs)); } /** * Create a list expression for the given arguments * * @param exprs list elements * @return list expression */ @SuppressWarnings("unchecked") public static <T> Expression<T> list(Class<T> clazz, List<? extends Expression<?>> exprs) { Expression<T> rv = (Expression<T>) exprs.get(0); if (exprs.size() == 1) { rv = operation(clazz, Ops.SINGLETON, rv, exprs.get(0)); } else { for (int i = 1; i < exprs.size(); i++) { rv = operation(clazz, Ops.LIST, rv, exprs.get(i)); } } return rv; } /** * Convert the given expression from regex form to like * * @param expr expression to convert * @return converted expression */ @SuppressWarnings("unchecked") public static Expression<String> regexToLike(Expression<String> expr) { if (expr instanceof Constant<?>) { final String str = expr.toString(); final StringBuilder rv = new StringBuilder(str.length() + 2); boolean escape = false; for (int i = 0; i < str.length(); i++) { final char ch = str.charAt(i); if (!escape && ch == '.') { if (i < str.length() - 1 && str.charAt(i + 1) == '*') { rv.append('%'); i++; } else { rv.append('_'); } continue; } else if (!escape && ch == '\\') { escape = true; continue; } else if (!escape && (ch == '[' || ch == ']' || ch == '^' || ch == '.' || ch == '*')) { throw new QueryException("'" + str + "' can't be converted to like form"); } else if (escape && (ch == 'd' || ch == 'D' || ch == 's' || ch == 'S' || ch == 'w' || ch == 'W')) { throw new QueryException("'" + str + "' can't be converted to like form"); } rv.append(ch); escape = false; } if (!rv.toString().equals(str)) { return ConstantImpl.create(rv.toString()); } } else if (expr instanceof Operation<?>) { Operation<?> o = (Operation<?>) expr; if (o.getOperator() == Ops.CONCAT) { Expression<String> lhs = regexToLike((Expression<String>) o.getArg(0)); Expression<String> rhs = regexToLike((Expression<String>) o.getArg(1)); if (lhs != o.getArg(0) || rhs != o.getArg(1)) { return operation(String.class, Ops.CONCAT, lhs, rhs); } } } return expr; } /** * Create a {@code left != constant} expression * * @param <D> type of expression * @param left lhs of expression * @param constant rhs of expression * @return left != constant */ public static <D> Predicate neConst(Expression<D> left, D constant) { return ne(left, ConstantImpl.create(constant)); } /** * Create a {@code left != right} expression * * @param <D> type of expressions * @param left lhs of expression * @param right rhs of expression * @return left != right */ public static <D> Predicate ne(Expression<D> left, Expression<? super D> right) { return predicate(Ops.NE, left, right); } /** * Create a {@code left not in right} expression * * @param <D> type of expressions * @param left lhs of expression * @param right rhs of expression * @return left not in right */ public static <D> Predicate notIn(Expression<D> left, CollectionExpression<?,? extends D> right) { return predicate(Ops.NOT_IN, left, right); } /** * Create a {@code left not in right} expression * * @param <D> type of expressions * @param left lhs of expression * @param right rhs of expression * @return left not in right */ public static <D> Predicate notIn(Expression<D> left, SubQueryExpression<? extends D> right) { return predicate(Ops.NOT_IN, left, right); } /** * Create a {@code left not in right} expression * * @param <D> type of expressions * @param left lhs of expression * @param right rhs of expression * @return left not in right */ public static <D> Predicate notIn(Expression<D> left, Collection<? extends D> right) { if (right.size() == 1) { return neConst(left, right.iterator().next()); } else { return predicate(Ops.NOT_IN, left, ConstantImpl.create(right)); } } /** * Create a {@code left not in right and...} expression for each list * * @param <D> * @param left * @param lists * @return a {@code left not in right and...} expression */ public static <D> Predicate notInAny(Expression<D> left, Iterable<? extends Collection<? extends D>> lists) { BooleanBuilder rv = new BooleanBuilder(); for (Collection<? extends D> list : lists) { rv.and(notIn(left, list)); } return rv; } /** * Create a {@code left or right} expression * * @param left lhs of expression * @param right rhs of expression * @return left or right */ public static Predicate or(Predicate left, Predicate right) { left = (Predicate) extract(left); right = (Predicate) extract(right); if (left == null) { return right; } else if (right == null) { return left; } else { return predicate(Ops.OR, left, right); } } /** * Create a distinct list of the given args * * @param args elements * @return list with distinct elements */ public static ImmutableList<Expression<?>> distinctList(Expression<?>... args) { final ImmutableList.Builder<Expression<?>> builder = ImmutableList.builder(); final Set<Expression<?>> set = new HashSet<Expression<?>>(args.length); for (Expression<?> arg : args) { if (set.add(arg)) { builder.add(arg); } } return builder.build(); } /** * Create a distinct list of the concatenated array contents * * @param args elements * @return list with distinct elements */ public static ImmutableList<Expression<?>> distinctList(Expression<?>[]... args) { final ImmutableList.Builder<Expression<?>> builder = ImmutableList.builder(); final Set<Expression<?>> set = new HashSet<Expression<?>>(); for (Expression<?>[] arr : args) { for (Expression<?> arg : arr) { if (set.add(arg)) { builder.add(arg); } } } return builder.build(); } /** * Get the potentially wrapped expression * * @param expr expression to analyze * @return inner expression */ @SuppressWarnings("unchecked") public static <T> Expression<T> extract(Expression<T> expr) { if (expr != null) { final Class<?> clazz = expr.getClass(); if (clazz == PathImpl.class || clazz == PredicateOperation.class || clazz == ConstantImpl.class) { return expr; } else { return (Expression<T>) expr.accept(ExtractorVisitor.DEFAULT, null); } } else { return null; } } /** * Create a new root variable based on the given path and suffix * * @param path base path * @param suffix suffix for variable name * @return path expression */ public static String createRootVariable(Path<?> path, int suffix) { String variable = path.accept(ToStringVisitor.DEFAULT, TEMPLATES); return variable + "_" + suffix; } /** * Create a new root variable based on the given path * * @param path base path * @return variable name */ public static String createRootVariable(Path<?> path) { return path.accept(ToStringVisitor.DEFAULT, TEMPLATES); } /** * Converts the given object to an Expression * * <p>Casts expressions and wraps everything else into co</p> * * @param o object to convert * @return converted argument */ public static Expression<?> toExpression(Object o) { if (o instanceof Expression) { return (Expression<?>) o; } else { return ConstantImpl.create(o); } } /** * Converts the given expression to lower(expression) * * <p>Constants are lower()ed at creation time</p> * * @param stringExpression the string to lower() * @return lower(stringExpression) */ public static Expression<String> toLower(Expression<String> stringExpression) { if (stringExpression instanceof Constant) { Constant<String> constantExpression = (Constant<String>) stringExpression; return ConstantImpl.create(constantExpression.getConstant().toLowerCase()); } else { return operation(String.class, Ops.LOWER, stringExpression); } } /** * Create an expression out of the given order specifiers * * @param args order * @return expression for order */ public static Expression<?> orderBy(List<OrderSpecifier<?>> args) { return operation(Object.class, Ops.ORDER, ConstantImpl.create(args)); } private ExpressionUtils() { } }