Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Patch] Support for templates with ranges #2140

Closed
TuomasKiviaho opened this issue May 1, 2017 · 1 comment
Closed

[Patch] Support for templates with ranges #2140

TuomasKiviaho opened this issue May 1, 2017 · 1 comment

Comments

@TuomasKiviaho
Copy link
Contributor

I've used the QueryDSL as AST and serializer for RFC-2254. Syntax is a bit unconventional when it comes to to handling parenthesis. It was easier for be to handle them as part of the template without help from QueryDSL. The next issue was AND/OR being not always binary due to usage of prefix notation instead of infix. I could have treated them as such but then the serializer would have created such a mess with the parenthesis here and there that already hard to read syntax would have become quite unreadable. I didn't want to fill my AST with template expressions because the AND/OR operators can still be used in predicate operations supporting arbitrary number of arguments.

Since the dialect doesn't have any separators between indices now that parenthesis are handled by each template, I saw that it's quite easy add range support to templates now that there is operator support already build in to the regular expressions.

I though that args.subList would be the logical component around ranges and I just used - sign to signal that I actually want args.size()-index2. As result I got a syntax that I used below without any major changes.

 add(Ops.AND, "(&{0..-1})", Precedence.AND);
 add(Ops.NOT, "(!{0})", Precedence.NOT_HIGH);
 add(Ops.OR, "(|{0..-1})", Precedence.OR);

PS. Only thing that I miss is a similar support for COALESCE/CONCATENATE style SQL syntax and why not RelationalFunctionCall that has to manually express it's repetition. I used Parboiled as parser and came across GramExp that could be used as basis to add support for repetition in EBNF like notation.


TemplateFactory

@@ -216,6 +216,22 @@
      */
     public static final class Operation extends Element {
 
+        static final Operator RANGE = new Operator() {
+
+            private static final long serialVersionUID = 5137034101957917967L;
+
+            @Override
+            public String name() {
+                return this.getClass().getSimpleName();
+            }
+
+            @Override
+            public Class<?> getType() {
+                return Object.class;
+            }
+
+        };
+
         private static final long serialVersionUID = 1400801176778801584L;
 
         private final int index1, index2;
@@ -233,34 +249,56 @@
 
         @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);
+            if (RANGE.equals(operator)) {
+                int fromIndex = index1;
+                if (index1 < 0) {
+                    fromIndex += args.size();
+                }
+                int toIndex = index2 + 1;
+                if (index2 < 0) {
+                    toIndex += args.size();
+                }
+                StringBuilder stringBuilder = new StringBuilder((toIndex - fromIndex)*3);
+                ImmutableList.Builder<Element> elements = ImmutableList.builder();
+                for (int i = fromIndex; i < toIndex; i++) {
+                    Element element = asString ? new AsString(i) : new ByIndex(i);
+                    elements.add(element);
+                    stringBuilder.append('{');
+                    stringBuilder.append(element);
+                    stringBuilder.append('}');
+                }
+                Template template = new Template(stringBuilder.toString(), elements.build());
+                return ExpressionUtils.template(Object.class, template, args.subList(fromIndex, toIndex));
             } else {
-                Expression<?> expr1 = asExpression(arg1);
-                Expression<?> expr2 = asExpression(arg2);
+                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);
+                    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,
-                                    operation.getArg(0), Expressions.constant(num2));
                         }
                     }
-                }
 
-                return ExpressionUtils.operation(expr1.getType(), operator, expr1, expr2);
+                    return ExpressionUtils.operation(expr1.getType(), operator, expr1, expr2);
+                }
             }
         }

TemplateFactory

@@ -33,8 +33,8 @@
  */
 public class TemplateFactory {
 
-    private static final Map<String, Operator> OPERATORS = ImmutableMap.<String, Operator>of(
-            "+", Ops.ADD, "-", Ops.SUB, "*", Ops.MULT, "/", Ops.DIV);
+    static final Map<String, Operator> OPERATORS = ImmutableMap.<String, Operator>of(
+            "+", Ops.ADD, "-", Ops.SUB, "*", Ops.MULT, "/", Ops.DIV, "..", Template.Operation.RANGE);
 
     public static final TemplateFactory DEFAULT = new TemplateFactory('\\');
 
@@ -42,8 +42,8 @@
 
     private static final Pattern elementPattern = Pattern.compile("\\{"
             + "(%?%?)"
-            + "(\\d+)"
-            + "(?:([+-/*])(?:(\\d+)|'(-?\\d+(?:\\.\\d+)?)'))?"
+            + "([-]?\\d+)"
+            + "(?:([+-/*]|\\.\\.)(?:([-]?\\d+)|'(-?\\d+(?:\\.\\d+)?)'))?"
             + "([slu%]?%?)"
             + "\\}");
@jwgmeligmeyling
Copy link
Member

If still interested, please provide a pull request together with a couple of test cases and an example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants