/* * 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.Arrays; import java.util.IdentityHashMap; import java.util.Map; import javax.annotation.Nullable; /** * {@code Templates} provides operator patterns for query expression serialization * * @author tiwe */ public class Templates { /** * Precedence order based on Java language operator precedence */ protected static class Precedence { public static final int HIGHEST = -1; public static final int DOT = 5; public static final int NOT_HIGH = 10; public static final int NEGATE = 20; public static final int ARITH_HIGH = 30; public static final int ARITH_LOW = 40; public static final int COMPARISON = 50; public static final int EQUALITY = 60; public static final int CASE = 70, LIST = 70; public static final int NOT = 80; public static final int AND = 90; public static final int XOR = 100, XNOR = 100; public static final int OR = 110; } public static final Templates DEFAULT = new Templates(); private final Map<Operator, Template> templates = new IdentityHashMap<Operator, Template>(150); private final Map<Operator, Integer> precedence = new IdentityHashMap<Operator, Integer>(150); private final TemplateFactory templateFactory; private final char escape; protected Templates() { this('\\'); } protected Templates(char escape) { this.escape = escape; templateFactory = new TemplateFactory(escape) { @Override public String escapeForLike(String str) { return Templates.this.escapeForLike(str); } }; //CHECKSTYLE:OFF add(Ops.LIST, "{0}, {1}", Precedence.LIST); add(Ops.SET, "{0}, {1}", Precedence.LIST); add(Ops.SINGLETON, "{0}", Precedence.LIST); add(Ops.WRAPPED, "({0})"); add(Ops.ORDER, "order()"); // boolean add(Ops.AND, "{0} && {1}", Precedence.AND); add(Ops.NOT, "!{0}", Precedence.NOT_HIGH); add(Ops.OR, "{0} || {1}", Precedence.OR); add(Ops.XNOR, "{0} xnor {1}", Precedence.XNOR); add(Ops.XOR, "{0} xor {1}", Precedence.XOR); // collection add(Ops.COL_IS_EMPTY, "empty({0})"); add(Ops.COL_SIZE, "size({0})"); // array add(Ops.ARRAY_SIZE, "size({0})"); // map add(Ops.MAP_SIZE, "size({0})"); add(Ops.MAP_IS_EMPTY, "empty({0})"); add(Ops.CONTAINS_KEY, "containsKey({0},{1})"); add(Ops.CONTAINS_VALUE, "containsValue({0},{1})"); // comparison add(Ops.BETWEEN, "{0} between {1} and {2}", Precedence.COMPARISON); add(Ops.GOE, "{0} >= {1}", Precedence.COMPARISON); add(Ops.GT, "{0} > {1}", Precedence.COMPARISON); add(Ops.LOE, "{0} <= {1}", Precedence.COMPARISON); add(Ops.LT, "{0} < {1}", Precedence.COMPARISON); // numeric add(Ops.NEGATE, "-{0}", Precedence.NEGATE); add(Ops.ADD, "{0} + {1}", Precedence.ARITH_LOW); add(Ops.DIV, "{0} / {1}", Precedence.ARITH_HIGH); add(Ops.MOD, "{0} % {1}", Precedence.ARITH_HIGH); add(Ops.MULT, "{0} * {1}", Precedence.ARITH_HIGH); add(Ops.SUB, "{0} - {1}", Precedence.ARITH_LOW); // various add(Ops.EQ, "{0} = {1}", Precedence.EQUALITY); add(Ops.EQ_IGNORE_CASE, "eqIc({0},{1})", Precedence.EQUALITY); add(Ops.INSTANCE_OF, "{0} instanceof {1}", Precedence.COMPARISON); add(Ops.NE, "{0} != {1}", Precedence.EQUALITY); add(Ops.IN, "{0} in {1}", Precedence.COMPARISON); add(Ops.NOT_IN, "{0} not in {1}", Precedence.COMPARISON); add(Ops.IS_NULL, "{0} is null", Precedence.COMPARISON); add(Ops.IS_NOT_NULL, "{0} is not null", Precedence.COMPARISON); add(Ops.ALIAS, "{0} as {1}", 0); add(Ops.NUMCAST, "cast({0},{1})"); add(Ops.STRING_CAST, "str({0})"); // string add(Ops.CONCAT, "{0} + {1}", Precedence.ARITH_LOW); add(Ops.LOWER, "lower({0})"); add(Ops.SUBSTR_1ARG, "substring({0},{1})"); add(Ops.SUBSTR_2ARGS, "substring({0},{1},{2})"); add(Ops.TRIM, "trim({0})"); add(Ops.UPPER, "upper({0})"); add(Ops.MATCHES, "matches({0},{1})"); add(Ops.MATCHES_IC, "matchesIgnoreCase({0},{1})"); add(Ops.STARTS_WITH, "startsWith({0},{1})"); add(Ops.STARTS_WITH_IC, "startsWithIgnoreCase({0},{1})"); add(Ops.ENDS_WITH, "endsWith({0},{1})"); add(Ops.ENDS_WITH_IC, "endsWithIgnoreCase({0},{1})"); add(Ops.STRING_CONTAINS, "contains({0},{1})"); add(Ops.STRING_CONTAINS_IC, "containsIc({0},{1})"); add(Ops.CHAR_AT, "charAt({0},{1})"); add(Ops.STRING_LENGTH, "length({0})"); add(Ops.INDEX_OF, "indexOf({0},{1})"); add(Ops.INDEX_OF_2ARGS, "indexOf({0},{1},{2})"); add(Ops.STRING_IS_EMPTY, "empty({0})"); add(Ops.LIKE, "{0} like {1}", Precedence.COMPARISON); add(Ops.LIKE_IC, "{0l} like {1l}", Precedence.COMPARISON); add(Ops.LIKE_ESCAPE, "{0} like {1} escape '{2s}'", Precedence.COMPARISON); add(Ops.LIKE_ESCAPE_IC, "{0l} like {1l} escape '{2s}'", Precedence.COMPARISON); add(Ops.StringOps.LEFT, "left({0},{1})"); add(Ops.StringOps.RIGHT, "right({0},{1})"); add(Ops.StringOps.LTRIM, "ltrim({0})"); add(Ops.StringOps.RTRIM, "rtrim({0})"); add(Ops.StringOps.LOCATE, "locate({0},{1})"); add(Ops.StringOps.LOCATE2, "locate({0},{1},{2s})"); add(Ops.StringOps.LPAD, "lpad({0},{1})"); add(Ops.StringOps.RPAD, "rpad({0},{1})"); add(Ops.StringOps.LPAD2, "lpad({0},{1},'{2s}')"); add(Ops.StringOps.RPAD2, "rpad({0},{1},'{2s}')"); // date time add(Ops.DateTimeOps.SYSDATE, "sysdate"); add(Ops.DateTimeOps.CURRENT_DATE, "current_date()"); add(Ops.DateTimeOps.CURRENT_TIME, "current_time()"); add(Ops.DateTimeOps.CURRENT_TIMESTAMP, "current_timestamp()"); add(Ops.DateTimeOps.DATE, "date({0})"); add(Ops.DateTimeOps.MILLISECOND, "millisecond({0})"); add(Ops.DateTimeOps.SECOND, "second({0})"); add(Ops.DateTimeOps.MINUTE, "minute({0})"); add(Ops.DateTimeOps.HOUR, "hour({0})"); add(Ops.DateTimeOps.WEEK, "week({0})"); add(Ops.DateTimeOps.MONTH, "month({0})"); add(Ops.DateTimeOps.YEAR, "year({0})"); add(Ops.DateTimeOps.YEAR_MONTH, "yearMonth({0})"); add(Ops.DateTimeOps.YEAR_WEEK, "yearweek({0})"); add(Ops.DateTimeOps.DAY_OF_WEEK, "dayofweek({0})"); add(Ops.DateTimeOps.DAY_OF_MONTH, "dayofmonth({0})"); add(Ops.DateTimeOps.DAY_OF_YEAR, "dayofyear({0})"); add(Ops.DateTimeOps.ADD_YEARS, "add_years({0},{1})"); add(Ops.DateTimeOps.ADD_MONTHS, "add_months({0},{1})"); add(Ops.DateTimeOps.ADD_WEEKS, "add_weeks({0},{1})"); add(Ops.DateTimeOps.ADD_DAYS, "add_days({0},{1})"); add(Ops.DateTimeOps.ADD_HOURS, "add_hours({0},{1})"); add(Ops.DateTimeOps.ADD_MINUTES, "add_minutes({0},{1})"); add(Ops.DateTimeOps.ADD_SECONDS, "add_seconds({0},{1})"); add(Ops.DateTimeOps.DIFF_YEARS, "diff_years({0},{1})"); add(Ops.DateTimeOps.DIFF_MONTHS, "diff_months({0},{1})"); add(Ops.DateTimeOps.DIFF_WEEKS, "diff_weeks({0},{1})"); add(Ops.DateTimeOps.DIFF_DAYS, "diff_days({0},{1})"); add(Ops.DateTimeOps.DIFF_HOURS, "diff_hours({0},{1})"); add(Ops.DateTimeOps.DIFF_MINUTES, "diff_minutes({0},{1})"); add(Ops.DateTimeOps.DIFF_SECONDS, "diff_seconds({0},{1})"); add(Ops.DateTimeOps.TRUNC_YEAR, "trunc_year({0})"); add(Ops.DateTimeOps.TRUNC_MONTH, "trunc_month({0})"); add(Ops.DateTimeOps.TRUNC_WEEK, "trunc_week({0})"); add(Ops.DateTimeOps.TRUNC_DAY, "trunc_day({0})"); add(Ops.DateTimeOps.TRUNC_HOUR, "trunc_hour({0})"); add(Ops.DateTimeOps.TRUNC_MINUTE, "trunc_minute({0})"); add(Ops.DateTimeOps.TRUNC_SECOND, "trunc_second({0})"); // math add(Ops.MathOps.ABS, "abs({0})"); add(Ops.MathOps.ACOS, "acos({0})"); add(Ops.MathOps.ASIN, "asin({0})"); add(Ops.MathOps.ATAN, "atan({0})"); add(Ops.MathOps.CEIL, "ceil({0})"); add(Ops.MathOps.COS, "cos({0})"); add(Ops.MathOps.COSH, "cosh({0})"); add(Ops.MathOps.COT, "cot({0})"); add(Ops.MathOps.COTH, "coth({0})"); add(Ops.MathOps.DEG, "degrees({0})"); add(Ops.MathOps.TAN, "tan({0})"); add(Ops.MathOps.TANH, "tanh({0})"); add(Ops.MathOps.SQRT, "sqrt({0})"); add(Ops.MathOps.SIGN, "sign({0})"); add(Ops.MathOps.SIN, "sin({0})"); add(Ops.MathOps.SINH, "sinh({0})"); add(Ops.MathOps.ROUND, "round({0})"); add(Ops.MathOps.ROUND2, "round({0},{1})"); add(Ops.MathOps.RAD, "radians({0})"); add(Ops.MathOps.RANDOM, "random()"); add(Ops.MathOps.RANDOM2, "random({0})"); add(Ops.MathOps.POWER, "pow({0},{1})"); add(Ops.MathOps.MIN, "min({0},{1})"); add(Ops.MathOps.MAX, "max({0},{1})"); add(Ops.MathOps.LOG, "log({0},{1})"); add(Ops.MathOps.LN, "ln({0})"); add(Ops.MathOps.FLOOR, "floor({0})"); add(Ops.MathOps.EXP, "exp({0})"); // path types add(PathType.PROPERTY, "{0}.{1s}"); add(PathType.VARIABLE, "{0s}"); add(PathType.DELEGATE, "{0}"); add(Ops.ORDINAL, "ordinal({0})"); // add(Ops.DELEGATE, "{0}"); for (PathType type : new PathType[] { PathType.LISTVALUE, PathType.MAPVALUE, PathType.MAPVALUE_CONSTANT }) { add(type, "{0}.get({1})"); } add(PathType.ARRAYVALUE, "{0}[{1}]"); add(PathType.COLLECTION_ANY, "any({0})"); add(PathType.LISTVALUE_CONSTANT, "{0}.get({1s})"); // serialized constant add(PathType.ARRAYVALUE_CONSTANT, "{0}[{1s}]"); // serialized constant // case add(Ops.CASE, "case {0} end", Precedence.CASE); add(Ops.CASE_WHEN, "when {0} then {1} {2}", Precedence.CASE); add(Ops.CASE_ELSE, "else {0}", Precedence.CASE); // case for add(Ops.CASE_EQ, "case {0} {1} end", Precedence.CASE); add(Ops.CASE_EQ_WHEN, "when {1} then {2} {3}", Precedence.CASE); add(Ops.CASE_EQ_ELSE, "else {0}", Precedence.CASE); // coalesce add(Ops.COALESCE, "coalesce({0})"); add(Ops.NULLIF, "nullif({0},{1})"); // subquery add(Ops.EXISTS, "exists {0}", 0); // numeric aggregates add(Ops.AggOps.BOOLEAN_ALL, "all({0})"); add(Ops.AggOps.BOOLEAN_ANY, "any({0})"); add(Ops.AggOps.AVG_AGG, "avg({0})"); add(Ops.AggOps.MAX_AGG, "max({0})"); add(Ops.AggOps.MIN_AGG, "min({0})"); add(Ops.AggOps.SUM_AGG, "sum({0})"); add(Ops.AggOps.COUNT_AGG, "count({0})"); add(Ops.AggOps.COUNT_DISTINCT_AGG, "count(distinct {0})"); add(Ops.AggOps.COUNT_DISTINCT_ALL_AGG, "count(distinct *)"); add(Ops.AggOps.COUNT_ALL_AGG, "count(*)"); // quantified expressions add(Ops.QuantOps.AVG_IN_COL, "avg({0})"); add(Ops.QuantOps.MAX_IN_COL, "max({0})"); add(Ops.QuantOps.MIN_IN_COL, "min({0})"); add(Ops.QuantOps.ANY, "any {0}"); add(Ops.QuantOps.ALL, "all {0}"); //CHECKSTYLE:ON } protected final void add(Operator op, String pattern) { templates.put(op, templateFactory.create(pattern)); if (!precedence.containsKey(op)) { precedence.put(op, -1); } } protected final void add(Operator op, String pattern, int pre) { templates.put(op, templateFactory.create(pattern)); precedence.put(op, pre); } protected final void add(Map<Operator, String> ops) { for (Map.Entry<Operator, String> entry : ops.entrySet()) { add(entry.getKey(), entry.getValue()); } } public final char getEscapeChar() { return escape; } protected String escapeForLike(String str) { final StringBuilder rv = new StringBuilder(str.length() + 3); for (char ch : str.toCharArray()) { if (ch == escape || ch == '%' || ch == '_') { rv.append(escape); } rv.append(ch); } return rv.toString(); } @Nullable public final Template getTemplate(Operator op) { return templates.get(op); } public final int getPrecedence(Operator op) { return precedence.get(op); } protected void setPrecedence(int p, Operator... ops) { setPrecedence(p, Arrays.asList(ops)); } protected void setPrecedence(int p, Iterable<? extends Operator> ops) { for (Operator op : ops) { precedence.put(op, p); } } }