You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2017/08/16 15:37:03 UTC
[11/13] cayenne git commit: CAY-2345 Own template renderer as a
replacement for Velocity - additional test and fixes
CAY-2345 Own template renderer as a replacement for Velocity
- additional test and fixes
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/38ca7283
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/38ca7283
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/38ca7283
Branch: refs/heads/master
Commit: 38ca7283955f4d7c756c16c4290f09bd19bf904b
Parents: f734851
Author: Nikita Timofeev <st...@gmail.com>
Authored: Wed Aug 9 18:29:40 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Wed Aug 16 18:30:09 2017 +0300
----------------------------------------------------------------------
.../org/apache/cayenne/template/Context.java | 20 +-
.../template/directive/BindNotEqual.java | 2 +-
.../cayenne/template/parser/ASTMethod.java | 33 +--
.../cayenne/template/parser/ASTVariable.java | 9 +-
.../CayenneSQLTemplateProcessorTest.java | 204 +++++++++++++++++++
.../template/parser/SQLTemplateParserTest.java | 120 ++++++++++-
6 files changed, 364 insertions(+), 24 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38ca7283/cayenne-server/src/main/java/org/apache/cayenne/template/Context.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/Context.java b/cayenne-server/src/main/java/org/apache/cayenne/template/Context.java
index bb528d2..c15371c 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/template/Context.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/template/Context.java
@@ -57,14 +57,14 @@ public class Context {
int counter;
public Context() {
- directives.put( "bind", Bind.INSTANCE);
- directives.put( "bindEqual", BindEqual.INSTANCE);
- directives.put( "bindNotEqual", BindNotEqual.INSTANCE);
- directives.put( "bindObjectEqual", BindObjectEqual.INSTANCE);
- directives.put( "bindObjectNotEqual", BindObjectNotEqual.INSTANCE);
- directives.put( "result", Result.INSTANCE);
-
- objects.put("helper", new SQLTemplateRenderingUtils());
+ addDirective( "result", Result.INSTANCE);
+ addDirective( "bind", Bind.INSTANCE);
+ addDirective( "bindEqual", BindEqual.INSTANCE);
+ addDirective( "bindNotEqual", BindNotEqual.INSTANCE);
+ addDirective( "bindObjectEqual", BindObjectEqual.INSTANCE);
+ addDirective( "bindObjectNotEqual", BindObjectNotEqual.INSTANCE);
+
+ addParameter("helper", new SQLTemplateRenderingUtils());
}
public Context(boolean positionalMode) {
@@ -92,6 +92,10 @@ public class Context {
return builder.toString();
}
+ public boolean haveObject(String name) {
+ return objects.containsKey(name);
+ }
+
public Object getObject(String name) {
Object object = objects.get(name);
if(object != null) {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38ca7283/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindNotEqual.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindNotEqual.java b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindNotEqual.java
index e58200f..dda2900 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindNotEqual.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindNotEqual.java
@@ -27,7 +27,7 @@ import org.apache.cayenne.template.Context;
*/
public class BindNotEqual extends Bind {
- public static final BindEqual INSTANCE = new BindEqual();
+ public static final BindNotEqual INSTANCE = new BindNotEqual();
@Override
protected void processBinding(Context context, ParameterBinding binding) {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38ca7283/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTMethod.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTMethod.java b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTMethod.java
index c4b96a0..869d5f8 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTMethod.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTMethod.java
@@ -20,7 +20,6 @@
package org.apache.cayenne.template.parser;
import java.lang.reflect.Method;
-import java.util.Objects;
import org.apache.cayenne.reflect.PropertyUtils;
import org.apache.cayenne.template.Context;
@@ -58,17 +57,27 @@ public class ASTMethod extends IdentifierNode {
Object[] arguments = new Object[jjtGetNumChildren()];
for(Class<?> parameterType : m.getParameterTypes()) {
ASTExpression child = (ASTExpression)jjtGetChild(i);
- if(parameterType.isAssignableFrom(String.class)) {
- arguments[i] = child.evaluateAsString(context);
- } else if(parameterType.isAssignableFrom(Double.class)) {
- arguments[i] = child.evaluateAsDouble(context);
- } else if(parameterType.isAssignableFrom(Long.class)) {
- arguments[i] = child.evaluateAsLong(context);
- } else if(parameterType.isAssignableFrom(Integer.class)) {
- arguments[i] = (int)child.evaluateAsLong(context);
- } else if(parameterType.isAssignableFrom(Boolean.class)) {
- arguments[i] = child.evaluateAsBoolean(context);
- } else {
+ try {
+ if (parameterType.isAssignableFrom(Object.class)) {
+ arguments[i] = child.evaluateAsObject(context);
+ } else if (parameterType.isAssignableFrom(String.class)) {
+ arguments[i] = child.evaluateAsString(context);
+ } else if (parameterType.isAssignableFrom(Boolean.class) || parameterType.isAssignableFrom(boolean.class)) {
+ arguments[i] = child.evaluateAsBoolean(context);
+ } else if (parameterType.isAssignableFrom(Double.class) || parameterType.isAssignableFrom(double.class)) {
+ arguments[i] = child.evaluateAsDouble(context);
+ } else if (parameterType.isAssignableFrom(Float.class) || parameterType.isAssignableFrom(float.class)) {
+ arguments[i] = (float) child.evaluateAsDouble(context);
+ } else if (parameterType.isAssignableFrom(Long.class) || parameterType.isAssignableFrom(long.class)) {
+ arguments[i] = child.evaluateAsLong(context);
+ } else if (parameterType.isAssignableFrom(Integer.class) || parameterType.isAssignableFrom(int.class)) {
+ arguments[i] = (int) child.evaluateAsLong(context);
+ } else if (parameterType.isAssignableFrom(Object[].class)) {
+ arguments[i] = child.evaluateAsObject(context);
+ } else {
+ continue methodsLoop;
+ }
+ } catch (UnsupportedOperationException ignored) {
continue methodsLoop;
}
i++;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38ca7283/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTVariable.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTVariable.java b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTVariable.java
index 3d2f60b..f7f992a 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTVariable.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTVariable.java
@@ -35,7 +35,14 @@ public class ASTVariable extends IdentifierNode implements ExpressionNode {
@Override
public String evaluateAsString(Context context) {
Object object = evaluateAsObject(context);
- return object == null ? "" : object.toString();
+ if(object == null) {
+ if(!context.haveObject(getIdentifier())) {
+ return '$' + getIdentifier();
+ }
+ return "";
+ }
+
+ return object.toString();
}
@Override
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38ca7283/cayenne-server/src/test/java/org/apache/cayenne/template/CayenneSQLTemplateProcessorTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/template/CayenneSQLTemplateProcessorTest.java b/cayenne-server/src/test/java/org/apache/cayenne/template/CayenneSQLTemplateProcessorTest.java
index cae3078..de148e0 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/template/CayenneSQLTemplateProcessorTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/template/CayenneSQLTemplateProcessorTest.java
@@ -19,6 +19,20 @@
package org.apache.cayenne.template;
+import java.sql.Types;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.DataObject;
+import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.access.jdbc.SQLStatement;
+import org.apache.cayenne.access.translator.ParameterBinding;
+import org.junit.Before;
+import org.junit.Test;
+
import static org.junit.Assert.*;
/**
@@ -26,6 +40,196 @@ import static org.junit.Assert.*;
*/
public class CayenneSQLTemplateProcessorTest {
+ private CayenneSQLTemplateProcessor processor;
+
+ @Before
+ public void before() {
+ processor = new CayenneSQLTemplateProcessor();
+ }
+
+ @Test
+ public void testProcessTemplateUnchanged1() throws Exception {
+ String sqlTemplate = "SELECT * FROM ME";
+
+ SQLStatement compiled = processor.processTemplate(sqlTemplate, Collections.<String, Object> emptyMap());
+
+ assertEquals(sqlTemplate, compiled.getSql());
+ assertEquals(0, compiled.getBindings().length);
+ }
+
+ @Test
+ public void testProcessTemplateUnchanged2() throws Exception {
+ String sqlTemplate = "SELECT a.b as XYZ FROM $SYSTEM_TABLE";
+
+ SQLStatement compiled = processor.processTemplate(sqlTemplate, Collections.<String, Object> emptyMap());
+
+ assertEquals(sqlTemplate, compiled.getSql());
+ assertEquals(0, compiled.getBindings().length);
+ }
+
+ @Test
+ public void testProcessTemplateSimpleDynamicContent() throws Exception {
+ String sqlTemplate = "SELECT * FROM ME WHERE $a";
+
+ Map<String, Object> map = Collections.<String, Object> singletonMap("a", "VALUE_OF_A");
+ SQLStatement compiled = processor.processTemplate(sqlTemplate, map);
+
+ assertEquals("SELECT * FROM ME WHERE VALUE_OF_A", compiled.getSql());
+
+ // bindings are not populated, since no "bind" macro is used.
+ assertEquals(0, compiled.getBindings().length);
+ }
+
+ @Test
+ public void testProcessTemplateBind() throws Exception {
+ String sqlTemplate = "SELECT * FROM ME WHERE "
+ + "COLUMN1 = #bind($a 'VARCHAR') AND COLUMN2 = #bind($b 'INTEGER')";
+ Map<String, Object> map = Collections.<String, Object> singletonMap("a", "VALUE_OF_A");
+ SQLStatement compiled = processor.processTemplate(sqlTemplate, map);
+
+ assertEquals("SELECT * FROM ME WHERE COLUMN1 = ? AND COLUMN2 = ?", compiled.getSql());
+ assertEquals(2, compiled.getBindings().length);
+ assertBindingValue("VALUE_OF_A", compiled.getBindings()[0]);
+ assertBindingValue(null, compiled.getBindings()[1]);
+ }
+
+ @Test
+ public void testProcessTemplateBindGuessVarchar() throws Exception {
+ String sqlTemplate = "SELECT * FROM ME WHERE COLUMN1 = #bind($a)";
+ Map<String, Object> map = Collections.<String, Object> singletonMap("a", "VALUE_OF_A");
+
+ SQLStatement compiled = processor.processTemplate(sqlTemplate, map);
+
+ assertEquals(1, compiled.getBindings().length);
+ assertBindingType(Types.VARCHAR, compiled.getBindings()[0]);
+ }
+
+ @Test
+ public void testProcessTemplateBindGuessInteger() throws Exception {
+ String sqlTemplate = "SELECT * FROM ME WHERE COLUMN1 = #bind($a)";
+ Map<String, Object> map = Collections.<String, Object> singletonMap("a", 4);
+
+ SQLStatement compiled = processor.processTemplate(sqlTemplate, map);
+
+ assertEquals(1, compiled.getBindings().length);
+ assertBindingType(Types.INTEGER, compiled.getBindings()[0]);
+ }
+
+ @Test
+ public void testProcessTemplateBindEqual() throws Exception {
+ String sqlTemplate = "SELECT * FROM ME WHERE COLUMN #bindEqual($a 'VARCHAR')";
+
+ SQLStatement compiled = processor.processTemplate(sqlTemplate, Collections.<String, Object> emptyMap());
+
+ assertEquals("SELECT * FROM ME WHERE COLUMN IS NULL", compiled.getSql());
+ assertEquals(0, compiled.getBindings().length);
+
+ Map<String, Object> map = Collections.<String, Object> singletonMap("a", "VALUE_OF_A");
+
+ compiled = processor.processTemplate(sqlTemplate, map);
+
+ assertEquals("SELECT * FROM ME WHERE COLUMN = ?", compiled.getSql());
+ assertEquals(1, compiled.getBindings().length);
+ assertBindingValue("VALUE_OF_A", compiled.getBindings()[0]);
+ }
+
+ @Test
+ public void testProcessTemplateBindNotEqual() throws Exception {
+ String sqlTemplate = "SELECT * FROM ME WHERE COLUMN #bindNotEqual($a 'VARCHAR')";
+
+ SQLStatement compiled = processor.processTemplate(sqlTemplate, Collections.<String, Object> emptyMap());
+
+ assertEquals("SELECT * FROM ME WHERE COLUMN IS NOT NULL", compiled.getSql());
+ assertEquals(0, compiled.getBindings().length);
+
+ Map<String, Object> map = Collections.<String, Object> singletonMap("a", "VALUE_OF_A");
+
+ compiled = processor.processTemplate(sqlTemplate, map);
+
+ assertEquals("SELECT * FROM ME WHERE COLUMN <> ?", compiled.getSql());
+ assertEquals(1, compiled.getBindings().length);
+ assertBindingValue("VALUE_OF_A", compiled.getBindings()[0]);
+ }
+
+ @Test
+ public void testProcessTemplateID() throws Exception {
+ String sqlTemplate = "SELECT * FROM ME WHERE COLUMN1 = #bind($helper.cayenneExp($a, 'db:ID_COLUMN'))";
+
+ DataObject dataObject = new CayenneDataObject();
+ dataObject.setObjectId(new ObjectId("T", "ID_COLUMN", 5));
+
+ Map<String, Object> map = Collections.<String, Object> singletonMap("a", dataObject);
+
+ SQLStatement compiled = processor.processTemplate(sqlTemplate, map);
+
+ assertEquals("SELECT * FROM ME WHERE COLUMN1 = ?", compiled.getSql());
+ assertEquals(1, compiled.getBindings().length);
+ assertBindingValue(5, compiled.getBindings()[0]);
+ }
+
+ @Test
+ public void testProcessTemplateNotEqualID() throws Exception {
+ String sqlTemplate = "SELECT * FROM ME WHERE "
+ + "COLUMN1 #bindNotEqual($helper.cayenneExp($a, 'db:ID_COLUMN1')) "
+ + "AND COLUMN2 #bindNotEqual($helper.cayenneExp($a, 'db:ID_COLUMN2'))";
+
+ Map<String, Object> idMap = new HashMap<>();
+ idMap.put("ID_COLUMN1", 3);
+ idMap.put("ID_COLUMN2", "aaa");
+ ObjectId id = new ObjectId("T", idMap);
+ DataObject dataObject = new CayenneDataObject();
+ dataObject.setObjectId(id);
+
+ Map<String, Object> map = Collections.<String, Object> singletonMap("a", dataObject);
+
+ SQLStatement compiled = processor.processTemplate(sqlTemplate, map);
+
+ assertEquals("SELECT * FROM ME WHERE COLUMN1 <> ? AND COLUMN2 <> ?", compiled.getSql());
+ assertEquals(2, compiled.getBindings().length);
+ assertBindingValue(3, compiled.getBindings()[0]);
+ assertBindingValue("aaa", compiled.getBindings()[1]);
+ }
+
+ @Test
+ public void testProcessTemplateConditions() throws Exception {
+ String sqlTemplate = "SELECT * FROM ME #if($a) WHERE COLUMN1 > #bind($a)#end";
+
+ Map<String, Object> map = Collections.<String, Object> singletonMap("a", "VALUE_OF_A");
+
+ SQLStatement compiled = processor.processTemplate(sqlTemplate, map);
+
+ assertEquals("SELECT * FROM ME WHERE COLUMN1 > ?", compiled.getSql());
+ assertEquals(1, compiled.getBindings().length);
+ assertBindingValue("VALUE_OF_A", compiled.getBindings()[0]);
+
+ compiled = processor.processTemplate(sqlTemplate, Collections.<String, Object> emptyMap());
+
+ assertEquals("SELECT * FROM ME ", compiled.getSql());
+ assertEquals(0, compiled.getBindings().length);
+ }
+
+ @Test
+ public void testProcessTemplateBindCollection() throws Exception {
+ String sqlTemplate = "SELECT * FROM ME WHERE COLUMN IN (#bind($list 'VARCHAR'))";
+
+ Map<String, Object> map = Collections.<String, Object> singletonMap("list", Arrays.asList("a", "b", "c"));
+ SQLStatement compiled = processor.processTemplate(sqlTemplate, map);
+
+ assertEquals("SELECT * FROM ME WHERE COLUMN IN (?,?,?)", compiled.getSql());
+ assertEquals(3, compiled.getBindings().length);
+ assertBindingValue("a", compiled.getBindings()[0]);
+ assertBindingValue("b", compiled.getBindings()[1]);
+ assertBindingValue("c", compiled.getBindings()[2]);
+ }
+
+ private void assertBindingValue(Object expectedValue, Object binding) {
+ assertTrue("Not a binding!", binding instanceof ParameterBinding);
+ assertEquals(expectedValue, ((ParameterBinding) binding).getValue());
+ }
+ private void assertBindingType(Integer expectedType, Object binding) {
+ assertTrue("Not a binding!", binding instanceof ParameterBinding);
+ assertEquals(expectedType, ((ParameterBinding) binding).getJdbcType());
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38ca7283/cayenne-server/src/test/java/org/apache/cayenne/template/parser/SQLTemplateParserTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/template/parser/SQLTemplateParserTest.java b/cayenne-server/src/test/java/org/apache/cayenne/template/parser/SQLTemplateParserTest.java
index 6d26b20..22d91a0 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/template/parser/SQLTemplateParserTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/template/parser/SQLTemplateParserTest.java
@@ -20,9 +20,9 @@
package org.apache.cayenne.template.parser;
import java.io.ByteArrayInputStream;
-import java.io.StringReader;
import org.apache.cayenne.template.Context;
+import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;
@@ -33,7 +33,7 @@ import static org.junit.Assert.*;
public class SQLTemplateParserTest {
@Test
- public void testSimpleParse() throws Exception {
+ public void testUnchangedParse() throws Exception {
Context context = new Context();
String template = "SELECT * FROM a";
@@ -145,6 +145,87 @@ public class SQLTemplateParserTest {
}
@Test
+ public void testHelperObject() throws Exception {
+ String tpl = "($helper.cayenneExp($a, 'field'))";
+ Context context = new Context();
+ context.addParameter("a", new TestBean(5));
+
+ String sql = parseString(tpl, context);
+ assertEquals("(5)", sql);
+ }
+
+ @Test
+ public void testMethodCallArray() throws Exception {
+ String tpl = "$a.arrayMethod(['1' '2' '3'])";
+ Context context = new Context();
+ context.addParameter("a", new TestBean(5));
+
+ String sql = parseString(tpl, context);
+ assertEquals("array_3", sql);
+ }
+
+ @Test
+ public void testMethodCallInt() throws Exception {
+ String tpl = "$a.intMethod(42)";
+ Context context = new Context();
+ context.addParameter("a", new TestBean(5));
+
+ String sql = parseString(tpl, context);
+ assertEquals("int_42", sql);
+ }
+
+ @Test
+ public void testMethodCallString() throws Exception {
+ String tpl = "$a.stringMethod(\"abc\")";
+ Context context = new Context();
+ context.addParameter("a", new TestBean(5));
+
+ String sql = parseString(tpl, context);
+ assertEquals("string_abc", sql);
+ }
+
+ @Test
+ public void testMethodCallFloat() throws Exception {
+ String tpl = "$a.floatMethod(3.14)";
+ Context context = new Context();
+ context.addParameter("a", new TestBean(5));
+
+ String sql = parseString(tpl, context);
+ assertEquals("float_3.14", sql);
+ }
+
+ @Test
+ @Ignore("Method overload not properly supported, this test can return m2_true")
+ public void testMethodCallSelectByArgType1() throws Exception {
+ String tpl = "$a.method(123)";
+ Context context = new Context();
+ context.addParameter("a", new TestBean(5));
+
+ String sql = parseString(tpl, context);
+ assertEquals("m1_123", sql);
+ }
+
+ @Test
+ public void testMethodCallSelectByArgType2() throws Exception {
+ String tpl = "$a.method(true)";
+ Context context = new Context();
+ context.addParameter("a", new TestBean(5));
+
+ String sql = parseString(tpl, context);
+ assertEquals("m2_true", sql);
+ }
+
+ @Test
+ public void testPropertyAccess() throws Exception {
+ String tpl = "$a.field()";
+ Context context = new Context();
+ context.addParameter("a", new TestBean(5));
+
+ String sql = parseString(tpl, context);
+ assertEquals("5", sql);
+ }
+
+ @Test
public void testNestedBrackets() throws Exception {
String tpl = "(#bind('A' 'b'))";
String sql = parseString(tpl, new Context());
@@ -180,4 +261,39 @@ public class SQLTemplateParserTest {
return context.buildTemplate();
}
+ static public class TestBean {
+ private int field;
+ TestBean(int field) {
+ this.field = field;
+ }
+
+ public int getField() {
+ return field;
+ }
+
+ public String arrayMethod(Object[] array) {
+ return "array_" + array.length;
+ }
+
+ public String stringMethod(String string) {
+ return "string_" + string;
+ }
+
+ public String intMethod(int i) {
+ return "int_" + i;
+ }
+
+ public String floatMethod(float f) {
+ return "float_" + f;
+ }
+
+ public String method(int i) {
+ return "m1_" + i;
+ }
+
+ public String method(boolean b) {
+ return "m2_" + b;
+ }
+ }
+
}
\ No newline at end of file