/* * Copyright 2007 The Fornax Project Team, including the original * author or authors. * * 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 placeholders OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sculptor.generator.util; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Converts a given "jpql" query string to a query dsl using simple text * replacement. Actually only ConditionalCriteria and a subset of jpql is * supported. * * @author Oliver Ringel * */ public interface QueryConverter { public String toQueryDsl(); static abstract class AbstractQueryConverter implements QueryConverter { protected String query = ""; protected String aggregateRoot = null; protected String select = ""; protected String from = ""; protected String orderBy = ""; protected String groupBy = ""; protected String where = ""; protected AbstractQueryConverter(String query, String aggregateRoot) { this.query = query; this.aggregateRoot = aggregateRoot; extractSqlParts(); } private void extractSqlParts() { select = extract("(?:select)(.*?)(?:(from|where|group by|order by|$))"); from = extract("(?:from)(.*?)(?:(select|where|group by|order by|$))"); where = extract("(?:where)(.*?)(?:(select|from|group by|order by|$))"); groupBy = extract("(?:group by)(.*?)(?:(select|from|where|order by|$))"); orderBy = extract("(?:order by)(.*?)(?:(select|from|where|group by|$))"); if (!hasSqlParts()) { where = query; // query only contains conditions } } private String extract(String regex) { Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(query); if (matcher.find()) { return matcher.group(1).trim(); } return ""; } public abstract String toQueryDsl(); @Override public String toString() { return String.format("select=%1$s from=%2$s where=%3$s group by=%4$s order by=%5$s", select, from, where, groupBy, orderBy); } private boolean hasSqlParts() { if (!(select + from + where + groupBy + orderBy).isEmpty()) { return true; } return false; } } static class ConditionalCriteriaStrategy extends AbstractQueryConverter { private String aggregateRootProperties = ""; private Map<String, String> placeholders = new HashMap<String, String>(); private int placeholdersCounter = 0; private static final List<Expression> expressions = new ArrayList<Expression>(); static { expressions.add(new Expression("in", ".withProperty(%1$s).in(%2$s)")); expressions.add(new Expression("between", ".withProperty(%1$s).between(%2$s,%3$s)")); expressions.add(new Expression("not between", ".not().withProperty(%1$s).between(%2$s,%3$s)")); expressions.add(new Expression("=", ".withProperty(%1$s).eq(%2$s)")); expressions.add(new Expression("i=", ".withProperty(%1$s).ignoreCaseEq(%2$s)")); expressions.add(new Expression("!=", ".not().withProperty(%1$s).eq(%2$s)")); expressions.add(new Expression("<>", ".not().withProperty(%1$s).eq(%2$s)")); expressions.add(new Expression(">", ".withProperty(%1$s).greaterThan(%2$s)")); expressions.add(new Expression(">=", ".withProperty(%1$s).greaterThanOrEqual(%2$s)")); expressions.add(new Expression("<", ".withProperty(%1$s).lessThan(%2$s)")); expressions.add(new Expression("<=", ".withProperty(%1$s).lessThanOrEqual(%2$s)")); expressions.add(new Expression("like", ".withProperty(%1$s).like(%2$s)")); expressions.add(new Expression("ilike", ".withProperty(%1$s).ignoreCaseLike(%2$s)")); expressions.add(new Expression("not like", ".not().withProperty(%1$s).like(%2$s)")); expressions.add(new Expression("is null", ".withProperty(%1$s).isNull()")); expressions.add(new Expression("is not null", ".withProperty(%1$s).isNotNull()")); expressions.add(new Expression("is empty", ".withProperty(%1$s).isEmpty()")); expressions.add(new Expression("is not empty", ".withProperty(%1$s).isNotEmpty()")); } public ConditionalCriteriaStrategy(String query, String aggregateRoot) { super(query, aggregateRoot); this.aggregateRootProperties = aggregateRoot + "Properties"; } @Override public String toQueryDsl() { prepareSelections(); prepareWhere(); prepareGroupBy(); prepareOrderBy(); return select + where + groupBy + orderBy; } private void prepareSelections() { if (!select.isEmpty()) convertSelections(); } // private void prepareFrom() { // if (!from.isEmpty()) // convertSelections(); // } private void prepareWhere() { if (!where.isEmpty()) { convertLiterals(); convertExpressions(); convertStaticText(); insertExpressions(); insertLiterals(); } } private void prepareGroupBy() { if (!groupBy.isEmpty()) convertGroupBy(); } private void prepareOrderBy() { if (!orderBy.isEmpty()) convertOrderBy(); } private void convertSelections() { String[] selections = select.split(","); select = ""; for (String selection : selections) { select += String.format(".select(%1$s)%2$s%3$s", convertAttribute(attribute(selection)), convertAlias(alias(selection)), convertAggregate(aggregate(selection))); } } private void convertGroupBy() { String[] groups = groupBy.split(","); groupBy = ""; for (String group : groups) { groupBy += String.format(".groupBy(%1$s)", convertAttribute(attribute(group))); } } private void convertOrderBy() { String[] orders = orderBy.split(","); orderBy = ""; for (String order : orders) { orderBy += String.format(".orderBy(%1$s)%2$s", convertAttribute(attribute(order)), convertOrderDirection(orderDirection(order))); } } private void convertLiterals() { addPlaceholders("'(.*?)'", "'", "\""); } private void convertExpressions() { for (Expression expression : expressions) { Pattern pattern = Pattern.compile(expression.getRegex()); Matcher matcher = pattern.matcher(" " + where + " "); while (matcher.find()) { Object[] operants = new Object[expression.countOperants()]; for (int i = 0; i < operants.length; i++) { operants[i] = convertOperant(matcher.group(i + 1).replaceAll(expression.getReplacement(), "")); } addPlaceholder(matcher.group(0).replaceAll(expression.getReplacement(), "").trim(), String.format(expression.getTransformation(), operants)); } } } private String convertOperant(String operant) { if (operant.startsWith("$P")) { // ignore placeholders (literals) at this point } else if (operant.startsWith(":")) { operant = operant.replace(":", ""); // parameters } else { operant = convertAttribute(operant); } return operant; } private String convertAttribute(String attribute) { if (attribute.isEmpty()) return ""; String alias = alias(from) + "."; if (attribute.startsWith(alias)) attribute = attribute.replace(alias, ""); return aggregateRootProperties + "." + attribute.replace(".", "().") + "()"; } private String convertAlias(String alias) { if (alias.isEmpty()) return ""; return ".alias(\"" + alias + "\")"; } private String convertAggregate(String aggregate) { if (aggregate.isEmpty()) return ""; return "." + aggregate + "()"; } private String convertOrderDirection(String order) { if (order.isEmpty()) return ""; return "." + order + "()"; } private void convertStaticText() { where = where.replace("(", ".lbrace").replace(")", ".rbrace").replace("lbrace", "lbrace()") .replace("rbrace", "rbrace()").replace("and", ".and()").replace("or", ".or()").replace("!", ".not()"); } private void insertExpressions() { where = insertPlaceholders(where).replaceAll("\\s+\\.", ".").trim(); } private void insertLiterals() { where = insertPlaceholders(where); } private void addPlaceholder(String replace, String with) { String placeholder = "$P" + (++placeholdersCounter); where = where.replace(replace, placeholder); placeholders.put(placeholder, with); } private void addPlaceholder(String replace) { addPlaceholder(replace, replace); } @SuppressWarnings("unused") private void addPlaceholders(String regex) { Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(where); while (matcher.find()) { addPlaceholder(matcher.group(0)); } } private void addPlaceholders(String regex, String replace, String with) { Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(where); while (matcher.find()) { addPlaceholder(matcher.group(0), matcher.group(0).replace(replace, with)); } } private String insertPlaceholders(String expression) { for (String placeholder : placeholders.keySet()) { if (expression.contains(placeholder)) { expression = expression.replace(placeholder, placeholders.get(placeholder)); } } return expression; } private String alias(String expression) { if (expression.contains(" as ")) { return expression.split(" as ")[1].trim(); } else if (expression.trim().contains(" ")) { return expression.split(" ")[1].trim(); } return ""; } private String attribute(String expression) { String attribute = expression.trim(); if (expression.contains(" as ")) { attribute = expression.split(" as ")[0].trim(); } if (expression.contains(" desc")) { attribute = expression.split(" desc")[0].trim(); } if (expression.contains(" asc")) { attribute = expression.split(" asc")[0].trim(); } if (attribute.contains("(") && attribute.contains(")")) { attribute = attribute.split("\\(|\\)")[1].trim(); } return attribute; } private String aggregate(String expression) { String expr = expression.trim(); if (expr.contains("(")) { return expr.split("\\(")[0].trim(); } return ""; } private String orderDirection(String expression) { String expr = expression.trim(); if (expr.contains(" desc")) { return "descending"; } return "ascending"; } private static class Expression { private String operator; private String transformation; private String regex; private String replacement = "\\(|\\)"; public Expression(String operator, String transformation) { this.operator = operator; this.transformation = transformation; switch (countOperants()) { case 3: // (not) between expression regex = "(\\S+?)\\s+" + operator + "\\s+(\\S+?)\\sand\\s+(\\S+?)\\s+"; break; case 2: regex = "(\\S+?)\\s+" + operator + "\\s+(\\S+?)(?=\\s+)"; break; case 1: regex = "(\\S+?)\\s+" + operator + "\\s+"; break; default: break; } if (this.operator.equals("in")) { replacement = ""; regex = "(\\S+?)\\s+in\\s+\\(+(.*?)\\)+"; } } @SuppressWarnings("unused") public String getOperator() { return operator; } public String getTransformation() { return transformation; } public String getRegex() { return regex; } public String getReplacement() { return replacement; } public int countOperants() { if (transformation.contains("%3")) return 3; else if (transformation.contains("%2")) return 2; else if (transformation.contains("%1")) return 1; return 0; } } } }