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/01/05 12:33:23 UTC
[2/3] cayenne git commit: CAY-2191 and CAY-2188 - expressions in
Property class - explicit type in Property class and factory methods - string
and math functions from JPA standard with support for specific DBs - factory
methods for new function call
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
index a340b50..fe963b1 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
@@ -18,7 +18,6 @@
****************************************************************/
package org.apache.cayenne.exp;
-import org.apache.cayenne.exp.parser.ASTObjPath;
import org.apache.cayenne.query.Ordering;
import org.apache.cayenne.query.PrefetchTreeNode;
import org.apache.cayenne.query.SortOrder;
@@ -33,16 +32,19 @@ import java.util.List;
* A property in a DataObject.
* </p>
* <p>
- * <p>
- * Used to construct Expressions quickly and with type-safety, and to construct
- * Orderings
+ * Used to construct Expressions quickly and with type-safety, and to construct Orderings
* </p>
* <p>
- * <p>
* Instances of this class are immutable
+ * Construct via factory methods Property.create(..)
* </p>
*
* @param <E> The type this property returns.
+ *
+ * @see Property#create(String, Class)
+ * @see Property#create(Expression, Class)
+ * @see Property#create(String, Expression, Class)
+ *
* @since 4.0
*/
public class Property<E> {
@@ -53,10 +55,67 @@ public class Property<E> {
private final String name;
/**
+ * Expression provider for the property
+ * @since 4.0
+ */
+ private final ExpressionProvider expressionProvider;
+
+ /**
+ * Explicit type of the property
+ * @since 4.0
+ */
+ private final Class<? super E> type;
+
+ /**
* Constructs a new property with the given name.
+ *
+ * @param name name of the property (usually it's obj path)
+ *
+ * @see Property#create(String, Class)
+ * @deprecated use factory method Property.create("propertyName", PropertyType.class)
+ */
+ public Property(final String name) {
+ this(name, null);
+ }
+
+ /**
+ * Constructs a new property with the given name and type.
+ *
+ * @param name of the property (usually it's obj path)
+ * @param type of the property
+ *
+ * @see Property#create(String, Class)
+ */
+ protected Property(final String name, final Class<? super E> type) {
+ this.name = name;
+ expressionProvider = new ExpressionProvider() {
+ @Override
+ public Expression get() {
+ return ExpressionFactory.pathExp(name);
+ }
+ };
+ this.type = type;
+ }
+
+ /**
+ * Constructs a new property with the given name and expression
+ *
+ * @param name of the property (will be used as alias for the expression)
+ * @param expression expression for property
+ * @param type of the property
+ * @since 4.0
+ *
+ * @see Property#create(String, Expression, Class)
*/
- public Property(String name) {
+ protected Property(final String name, final Expression expression, final Class<? super E> type) {
this.name = name;
+ expressionProvider = new ExpressionProvider() {
+ @Override
+ public Expression get() {
+ return expression.deepCopy();
+ }
+ };
+ this.type = type;
}
/**
@@ -66,6 +125,13 @@ public class Property<E> {
return name;
}
+ /**
+ * @since 4.0
+ */
+ public Expression getExpression() {
+ return expressionProvider.get();
+ }
+
@Override
public int hashCode() {
return getName().hashCode();
@@ -82,7 +148,7 @@ public class Property<E> {
* @return a newly created Property object.
*/
public Property<Object> dot(String property) {
- return new Property<Object>(getName() + "." + property);
+ return create(getName() + "." + property, null);
}
/**
@@ -91,7 +157,7 @@ public class Property<E> {
* @return a newly created Property object.
*/
public <T> Property<T> dot(Property<T> property) {
- return new Property<T>(getName() + "." + property.getName());
+ return create(getName() + "." + property.getName(), property.getType());
}
/**
@@ -100,7 +166,7 @@ public class Property<E> {
* as "outer" attributes make no sense.
*/
public Property<E> outer() {
- return isOuter() ? this : new Property<E>(name + "+");
+ return isOuter() ? this : create(name + "+", type);
}
private boolean isOuter() {
@@ -109,46 +175,48 @@ public class Property<E> {
/**
* Converts this property to a path expression.
+ * This method is equivalent of getExpression() which is preferred as more generic.
*
* @return a newly created expression.
+ * @see Property#getExpression()
*/
public Expression path() {
- return ExpressionFactory.pathExp(getName());
+ return getExpression();
}
/**
* @return An expression representing null.
*/
public Expression isNull() {
- return ExpressionFactory.matchExp(getName(), null);
+ return ExpressionFactory.matchExp(getExpression(), null);
}
/**
* @return An expression representing a non-null value.
*/
public Expression isNotNull() {
- return ExpressionFactory.matchExp(getName(), null).notExp();
+ return ExpressionFactory.matchExp(getExpression(), null).notExp();
}
/**
* @return An expression representing equality to TRUE.
*/
public Expression isTrue() {
- return ExpressionFactory.matchExp(getName(), Boolean.TRUE);
+ return ExpressionFactory.matchExp(getExpression(), Boolean.TRUE);
}
/**
* @return An expression representing equality to FALSE.
*/
public Expression isFalse() {
- return ExpressionFactory.matchExp(getName(), Boolean.FALSE);
+ return ExpressionFactory.matchExp(getExpression(), Boolean.FALSE);
}
/**
* @return An expression representing equality to a value.
*/
public Expression eq(E value) {
- return ExpressionFactory.matchExp(getName(), value);
+ return ExpressionFactory.matchExp(getExpression(), value);
}
/**
@@ -156,14 +224,14 @@ public class Property<E> {
* (columns).
*/
public Expression eq(Property<?> value) {
- return ExpressionFactory.matchExp(getName(), new ASTObjPath(value.getName()));
+ return ExpressionFactory.matchExp(getExpression(), value.getExpression());
}
/**
* @return An expression representing inequality to a value.
*/
public Expression ne(E value) {
- return ExpressionFactory.noMatchExp(getName(), value);
+ return ExpressionFactory.noMatchExp(getExpression(), value);
}
/**
@@ -171,7 +239,7 @@ public class Property<E> {
* (columns).
*/
public Expression ne(Property<?> value) {
- return ExpressionFactory.noMatchExp(getName(), new ASTObjPath(value.getName()));
+ return ExpressionFactory.noMatchExp(getExpression(), value.getExpression());
}
/**
@@ -183,7 +251,7 @@ public class Property<E> {
* @return An expression for a Database "LIKE" query.
*/
public Expression like(String pattern) {
- return ExpressionFactory.likeExp(getName(), pattern);
+ return ExpressionFactory.likeExp(getExpression(), pattern);
}
/**
@@ -194,28 +262,28 @@ public class Property<E> {
* @return An expression for a Database "LIKE" query.
*/
public Expression like(String pattern, char escapeChar) {
- return ExpressionFactory.likeExp(getName(), pattern, escapeChar);
+ return ExpressionFactory.likeExp(getExpression(), pattern, escapeChar);
}
/**
* @return An expression for a case insensitive "LIKE" query.
*/
public Expression likeIgnoreCase(String pattern) {
- return ExpressionFactory.likeIgnoreCaseExp(getName(), pattern);
+ return ExpressionFactory.likeIgnoreCaseExp(getExpression(), pattern);
}
/**
* @return An expression for a Database "NOT LIKE" query.
*/
public Expression nlike(String value) {
- return ExpressionFactory.notLikeExp(getName(), value);
+ return ExpressionFactory.notLikeExp(getExpression(), value);
}
/**
* @return An expression for a case insensitive "NOT LIKE" query.
*/
public Expression nlikeIgnoreCase(String value) {
- return ExpressionFactory.notLikeIgnoreCaseExp(getName(), value);
+ return ExpressionFactory.notLikeIgnoreCaseExp(getExpression(), value);
}
/**
@@ -228,7 +296,7 @@ public class Property<E> {
* @return a newly created expression.
*/
public Expression contains(String substring) {
- return ExpressionFactory.containsExp(getName(), substring);
+ return ExpressionFactory.containsExp(getExpression(), substring);
}
/**
@@ -241,7 +309,7 @@ public class Property<E> {
* @return a newly created expression.
*/
public Expression startsWith(String value) {
- return ExpressionFactory.startsWithExp(getName(), value);
+ return ExpressionFactory.startsWithExp(getExpression(), value);
}
/**
@@ -254,7 +322,7 @@ public class Property<E> {
* @return a newly created expression.
*/
public Expression endsWith(String value) {
- return ExpressionFactory.endsWithExp(getName(), value);
+ return ExpressionFactory.endsWithExp(getExpression(), value);
}
/**
@@ -262,7 +330,7 @@ public class Property<E> {
* comparison.
*/
public Expression containsIgnoreCase(String value) {
- return ExpressionFactory.containsIgnoreCaseExp(getName(), value);
+ return ExpressionFactory.containsIgnoreCaseExp(getExpression(), value);
}
/**
@@ -270,7 +338,7 @@ public class Property<E> {
* comparison.
*/
public Expression startsWithIgnoreCase(String value) {
- return ExpressionFactory.startsWithIgnoreCaseExp(getName(), value);
+ return ExpressionFactory.startsWithIgnoreCaseExp(getExpression(), value);
}
/**
@@ -278,7 +346,7 @@ public class Property<E> {
* comparison.
*/
public Expression endsWithIgnoreCase(String value) {
- return ExpressionFactory.endsWithIgnoreCaseExp(getName(), value);
+ return ExpressionFactory.endsWithIgnoreCaseExp(getExpression(), value);
}
/**
@@ -288,7 +356,7 @@ public class Property<E> {
* bound inclusive
*/
public Expression between(E lower, E upper) {
- return ExpressionFactory.betweenExp(getName(), lower, upper);
+ return ExpressionFactory.betweenExp(getExpression(), lower, upper);
}
/**
@@ -305,7 +373,7 @@ public class Property<E> {
System.arraycopy(moreValues, 0, values, 1, moreValuesLength);
}
- return ExpressionFactory.inExp(getName(), values);
+ return ExpressionFactory.inExp(getExpression(), values);
}
/**
@@ -323,14 +391,14 @@ public class Property<E> {
System.arraycopy(moreValues, 0, values, 1, moreValuesLength);
}
- return ExpressionFactory.notInExp(getName(), values);
+ return ExpressionFactory.notInExp(getExpression(), values);
}
/**
* @return An expression for finding objects with values in the given set.
*/
public Expression in(Collection<E> values) {
- return ExpressionFactory.inExp(getName(), values);
+ return ExpressionFactory.inExp(getExpression(), values);
}
/**
@@ -338,14 +406,14 @@ public class Property<E> {
* set.
*/
public Expression nin(Collection<E> values) {
- return ExpressionFactory.notInExp(getName(), values);
+ return ExpressionFactory.notInExp(getExpression(), values);
}
/**
* @return A greater than Expression.
*/
public Expression gt(E value) {
- return ExpressionFactory.greaterExp(getName(), value);
+ return ExpressionFactory.greaterExp(getExpression(), value);
}
/**
@@ -353,14 +421,14 @@ public class Property<E> {
* (columns).
*/
public Expression gt(Property<?> value) {
- return ExpressionFactory.greaterExp(getName(), new ASTObjPath(value.getName()));
+ return ExpressionFactory.greaterExp(getExpression(), value.getExpression());
}
/**
* @return A greater than or equal to Expression.
*/
public Expression gte(E value) {
- return ExpressionFactory.greaterOrEqualExp(getName(), value);
+ return ExpressionFactory.greaterOrEqualExp(getExpression(), value);
}
/**
@@ -368,14 +436,14 @@ public class Property<E> {
* attributes (columns).
*/
public Expression gte(Property<?> value) {
- return ExpressionFactory.greaterOrEqualExp(getName(), new ASTObjPath(value.getName()));
+ return ExpressionFactory.greaterOrEqualExp(getExpression(), value.getExpression());
}
/**
* @return A less than Expression.
*/
public Expression lt(E value) {
- return ExpressionFactory.lessExp(getName(), value);
+ return ExpressionFactory.lessExp(getExpression(), value);
}
/**
@@ -383,14 +451,14 @@ public class Property<E> {
* (columns).
*/
public Expression lt(Property<?> value) {
- return ExpressionFactory.lessExp(getName(), new ASTObjPath(value.getName()));
+ return ExpressionFactory.lessExp(getExpression(), value.getExpression());
}
/**
* @return A less than or equal to Expression.
*/
public Expression lte(E value) {
- return ExpressionFactory.lessOrEqualExp(getName(), value);
+ return ExpressionFactory.lessOrEqualExp(getExpression(), value);
}
/**
@@ -398,7 +466,7 @@ public class Property<E> {
* attributes (columns).
*/
public Expression lte(Property<?> value) {
- return ExpressionFactory.lessOrEqualExp(getName(), new ASTObjPath(value.getName()));
+ return ExpressionFactory.lessOrEqualExp(getExpression(), value.getExpression());
}
/**
@@ -471,7 +539,7 @@ public class Property<E> {
* prefetch semantics.
*/
public PrefetchTreeNode joint() {
- return PrefetchTreeNode.withPath(name, PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+ return PrefetchTreeNode.withPath(getName(), PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
}
/**
@@ -480,7 +548,7 @@ public class Property<E> {
* "disjoint" prefetch semantics.
*/
public PrefetchTreeNode disjoint() {
- return PrefetchTreeNode.withPath(name, PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
+ return PrefetchTreeNode.withPath(getName(), PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
}
/**
@@ -489,7 +557,7 @@ public class Property<E> {
* "disjoint by id" prefetch semantics.
*/
public PrefetchTreeNode disjointById() {
- return PrefetchTreeNode.withPath(name, PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
+ return PrefetchTreeNode.withPath(getName(), PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
}
/**
@@ -534,4 +602,27 @@ public class Property<E> {
}
}
+ public Property<E> alias(String alias) {
+ return new Property<>(alias, this.getExpression(), this.getType());
+ }
+
+ public Class<? super E> getType() {
+ return type;
+ }
+
+ public static <T> Property<T> create(String name, Class<? super T> type) {
+ return new Property<>(name, type);
+ }
+
+ public static <T> Property<T> create(Expression expression, Class<? super T> type) {
+ return new Property<>(expression.expName().toLowerCase(), expression, type);
+ }
+
+ public static <T> Property<T> create(String name, Expression expression, Class<? super T> type) {
+ return new Property<>(name, expression, type);
+ }
+
+ private interface ExpressionProvider {
+ Expression get();
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAbs.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAbs.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAbs.java
new file mode 100644
index 0000000..47b06dc
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAbs.java
@@ -0,0 +1,48 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.util.ConversionUtil;
+
+/**
+ * @since 4.0
+ */
+public class ASTAbs extends ASTFunctionCall {
+
+ ASTAbs(int id) {
+ super(id, "ABS");
+ }
+
+ public ASTAbs(Expression expression) {
+ super("ABS", expression);
+ }
+
+ @Override
+ protected Object evaluateNode(Object o) throws Exception {
+ double n = ConversionUtil.toDouble(evaluateChild(0, o), 0.0);
+ return Math.abs(n);
+ }
+
+ @Override
+ public Expression shallowCopy() {
+ return new ASTAbs(id);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTBetween.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTBetween.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTBetween.java
index 539991c..38d2cba 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTBetween.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTBetween.java
@@ -38,7 +38,7 @@ public class ASTBetween extends ConditionNode {
super(ExpressionParserTreeConstants.JJTBETWEEN);
}
- public ASTBetween(ASTPath path, Object value1, Object value2) {
+ public ASTBetween(SimpleNode path, Object value1, Object value2) {
super(ExpressionParserTreeConstants.JJTBETWEEN);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(value1), 1);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTConcat.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTConcat.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTConcat.java
new file mode 100644
index 0000000..efb11ad
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTConcat.java
@@ -0,0 +1,51 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.util.ConversionUtil;
+
+/**
+ * @since 4.0
+ */
+public class ASTConcat extends ASTFunctionCall {
+
+ ASTConcat(int id) {
+ super(id, "CONCAT");
+ }
+
+ public ASTConcat(Expression... expressions) {
+ super("CONCAT", expressions);
+ }
+
+ @Override
+ protected Object evaluateNode(Object o) throws Exception {
+ StringBuilder sb = new StringBuilder();
+ for(int i=0; i<getOperandCount(); i++) {
+ sb.append(ConversionUtil.toString(evaluateChild(i, o)));
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public Expression shallowCopy() {
+ return new ASTConcat(id);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTEqual.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTEqual.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTEqual.java
index 579545d..e2194d0 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTEqual.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTEqual.java
@@ -51,7 +51,7 @@ public class ASTEqual extends ConditionNode implements ValueInjector {
/**
* Creates "Equal To" expression.
*/
- public ASTEqual(ASTPath path, Object value) {
+ public ASTEqual(SimpleNode path, Object value) {
super(ExpressionParserTreeConstants.JJTEQUAL);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(value), 1);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTFunctionCall.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTFunctionCall.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTFunctionCall.java
new file mode 100644
index 0000000..9c3aa07
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTFunctionCall.java
@@ -0,0 +1,64 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import org.apache.cayenne.exp.Expression;
+
+/**
+ * @since 4.0
+ */
+public abstract class ASTFunctionCall extends SimpleNode {
+
+ private String functionName;
+
+ ASTFunctionCall(int id, String functionName) {
+ super(id);
+ this.functionName = functionName;
+ }
+
+ public ASTFunctionCall(String functionName, Object... nodes) {
+ this(0, functionName);
+ this.functionName = functionName;
+ int len = nodes.length;
+ for (int i = 0; i < len; i++) {
+ jjtAddChild(wrapChild(nodes[i]), i);
+ }
+
+ connectChildren();
+ }
+
+ @Override
+ public int getType() {
+ return Expression.FUNCTION_CALL;
+ }
+
+ public String getFunctionName() {
+ return functionName;
+ }
+
+ /**
+ * TODO what should this method return?
+ */
+ @Override
+ protected String getExpressionOperator(int index) {
+ return functionName;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreater.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreater.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreater.java
index f990e24..c7eb1f1 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreater.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreater.java
@@ -39,7 +39,7 @@ public class ASTGreater extends ConditionNode {
super(ExpressionParserTreeConstants.JJTGREATER);
}
- public ASTGreater(ASTPath path, Object value) {
+ public ASTGreater(SimpleNode path, Object value) {
super(ExpressionParserTreeConstants.JJTGREATER);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(value), 1);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreaterOrEqual.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreaterOrEqual.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreaterOrEqual.java
index 22d9e97..a970c12 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreaterOrEqual.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTGreaterOrEqual.java
@@ -41,7 +41,7 @@ public class ASTGreaterOrEqual extends ConditionNode {
super(ExpressionParserTreeConstants.JJTGREATEROREQUAL);
}
- public ASTGreaterOrEqual(ASTPath path, Object value) {
+ public ASTGreaterOrEqual(SimpleNode path, Object value) {
super(ExpressionParserTreeConstants.JJTGREATEROREQUAL);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(value), 1);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java
index 10d7dc7..44ced07 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java
@@ -43,7 +43,7 @@ public class ASTIn extends ConditionNode {
super(ExpressionParserTreeConstants.JJTIN);
}
- public ASTIn(ASTPath path, ASTList list) {
+ public ASTIn(SimpleNode path, ASTList list) {
super(ExpressionParserTreeConstants.JJTIN);
jjtAddChild(path, 0);
jjtAddChild(list, 1);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLength.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLength.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLength.java
new file mode 100644
index 0000000..5d1eea8
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLength.java
@@ -0,0 +1,51 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.util.ConversionUtil;
+
+/**
+ * @since 4.0
+ */
+public class ASTLength extends ASTFunctionCall {
+
+ ASTLength(int id) {
+ super(id, "LENGTH");
+ }
+
+ public ASTLength(Expression expression) {
+ super("LENGTH", expression);
+ }
+
+ @Override
+ protected Object evaluateNode(Object o) throws Exception {
+ String s1 = ConversionUtil.toString(evaluateChild(0, o));
+ if (s1 == null) {
+ return null;
+ }
+ return s1.length();
+ }
+
+ @Override
+ public Expression shallowCopy() {
+ return new ASTLength(id);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLess.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLess.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLess.java
index 5a44f62..5880f93 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLess.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLess.java
@@ -41,7 +41,7 @@ public class ASTLess extends ConditionNode {
super(ExpressionParserTreeConstants.JJTLESS);
}
- public ASTLess(ASTPath path, Object value) {
+ public ASTLess(SimpleNode path, Object value) {
super(ExpressionParserTreeConstants.JJTLESS);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(value), 1);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLessOrEqual.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLessOrEqual.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLessOrEqual.java
index 9065631..d686797 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLessOrEqual.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLessOrEqual.java
@@ -41,7 +41,7 @@ public class ASTLessOrEqual extends ConditionNode {
super(ExpressionParserTreeConstants.JJTLESSOREQUAL);
}
- public ASTLessOrEqual(ASTPath path, Object value) {
+ public ASTLessOrEqual(SimpleNode path, Object value) {
super(ExpressionParserTreeConstants.JJTLESSOREQUAL);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(value), 1);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLike.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLike.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLike.java
index 0799ccf..31afa92 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLike.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLike.java
@@ -41,14 +41,14 @@ public class ASTLike extends PatternMatchNode {
super(ExpressionParserTreeConstants.JJTLIKE, false);
}
- public ASTLike(ASTPath path, Object pattern) {
+ public ASTLike(SimpleNode path, Object pattern) {
super(ExpressionParserTreeConstants.JJTLIKE, false);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(pattern), 1);
connectChildren();
}
- public ASTLike(ASTPath path, Object pattern, char escapeChar) {
+ public ASTLike(SimpleNode path, Object pattern, char escapeChar) {
super(ExpressionParserTreeConstants.JJTLIKE, false, escapeChar);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(pattern), 1);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLikeIgnoreCase.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLikeIgnoreCase.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLikeIgnoreCase.java
index 44cd9f2..c8be6a0 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLikeIgnoreCase.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLikeIgnoreCase.java
@@ -42,14 +42,14 @@ public class ASTLikeIgnoreCase extends IgnoreCaseNode {
super(ExpressionParserTreeConstants.JJTLIKEIGNORECASE, true);
}
- public ASTLikeIgnoreCase(ASTPath path, Object pattern) {
+ public ASTLikeIgnoreCase(SimpleNode path, Object pattern) {
super(ExpressionParserTreeConstants.JJTLIKEIGNORECASE, true);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(pattern), 1);
connectChildren();
}
- public ASTLikeIgnoreCase(ASTPath path, Object pattern, char escapeChar) {
+ public ASTLikeIgnoreCase(SimpleNode path, Object pattern, char escapeChar) {
super(ExpressionParserTreeConstants.JJTLIKEIGNORECASE, true, escapeChar);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(pattern), 1);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLocate.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLocate.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLocate.java
new file mode 100644
index 0000000..1c537e5
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLocate.java
@@ -0,0 +1,64 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.util.ConversionUtil;
+
+/**
+ * @since 4.0
+ */
+public class ASTLocate extends ASTFunctionCall {
+
+ ASTLocate(int id) {
+ super(id, "LOCATE");
+ }
+
+ public ASTLocate(Expression substring, Expression path) {
+ super("LOCATE", substring, path);
+ }
+
+ public ASTLocate(Expression substring, Expression path, Expression offset) {
+ super("LOCATE", substring, path, offset);
+ }
+
+ @Override
+ protected Object evaluateNode(Object o) throws Exception {
+ int len = jjtGetNumChildren();
+ if (len < 2) {
+ return 0L;
+ }
+
+ String substr = ConversionUtil.toString(evaluateChild(0, o));
+ String str = ConversionUtil.toString(evaluateChild(1, o));
+ int offset = 0;
+ if(len > 2) {
+ offset = ConversionUtil.toInt(evaluateChild(2, o), 0);
+ }
+
+ // +1 to comply with SQL
+ return str.indexOf(substr, offset) + 1;
+ }
+
+ @Override
+ public Expression shallowCopy() {
+ return new ASTLocate(id);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLower.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLower.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLower.java
new file mode 100644
index 0000000..b63319a
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLower.java
@@ -0,0 +1,52 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.util.ConversionUtil;
+
+/**
+ * @since 4.0
+ */
+public class ASTLower extends ASTFunctionCall {
+
+
+ ASTLower(int id) {
+ super(id, "LOWER");
+ }
+
+ public ASTLower(Expression expression) {
+ super("LOWER", expression);
+ }
+
+ @Override
+ protected Object evaluateNode(Object o) throws Exception {
+ String s1 = ConversionUtil.toString(evaluateChild(0, o));
+ if (s1 == null) {
+ return null;
+ }
+ return s1.toLowerCase();
+ }
+
+ @Override
+ public Expression shallowCopy() {
+ return new ASTLower(id);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMod.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMod.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMod.java
new file mode 100644
index 0000000..f951cb7
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMod.java
@@ -0,0 +1,52 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.util.ConversionUtil;
+
+/**
+ * @since 4.0
+ */
+public class ASTMod extends ASTFunctionCall {
+
+ ASTMod(int id) {
+ super(id, "MOD");
+ }
+
+ public ASTMod(Expression expression, Expression divisor) {
+ super("MOD", expression, divisor);
+ }
+
+ @Override
+ protected Object evaluateNode(Object o) throws Exception {
+ double x = ConversionUtil.toDouble(evaluateChild(0, o), 0.0);
+ double y = ConversionUtil.toDouble(evaluateChild(1, o), 0.0);
+ if(y == 0.0) {
+ return 0.0;
+ }
+ return x % y;
+ }
+
+ @Override
+ public Expression shallowCopy() {
+ return new ASTMod(id);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotBetween.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotBetween.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotBetween.java
index 9b0b013..000e5ee 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotBetween.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotBetween.java
@@ -34,7 +34,7 @@ public class ASTNotBetween extends ConditionNode {
super(ExpressionParserTreeConstants.JJTNOTBETWEEN);
}
- public ASTNotBetween(ASTPath path, Object value1, Object value2) {
+ public ASTNotBetween(SimpleNode path, Object value1, Object value2) {
super(ExpressionParserTreeConstants.JJTNOTBETWEEN);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(value1), 1);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotEqual.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotEqual.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotEqual.java
index 8a83a86..1ec687b 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotEqual.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotEqual.java
@@ -38,7 +38,7 @@ public class ASTNotEqual extends ConditionNode {
/**
* Creates "Not Equal To" expression.
*/
- public ASTNotEqual(ASTPath path, Object value) {
+ public ASTNotEqual(SimpleNode path, Object value) {
super(ExpressionParserTreeConstants.JJTNOTEQUAL);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(value), 1);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotIn.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotIn.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotIn.java
index 373280b..ea82c38 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotIn.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotIn.java
@@ -36,7 +36,7 @@ public class ASTNotIn extends ConditionNode {
super(ExpressionParserTreeConstants.JJTNOTIN);
}
- public ASTNotIn(ASTPath path, ASTList list) {
+ public ASTNotIn(SimpleNode path, ASTList list) {
super(ExpressionParserTreeConstants.JJTNOTIN);
jjtAddChild(path, 0);
jjtAddChild(list, 1);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotLike.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotLike.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotLike.java
index 2278143..da0fd76 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotLike.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotLike.java
@@ -37,14 +37,14 @@ public class ASTNotLike extends PatternMatchNode {
super(ExpressionParserTreeConstants.JJTNOTLIKE, false);
}
- public ASTNotLike(ASTPath path, Object value) {
+ public ASTNotLike(SimpleNode path, Object value) {
super(ExpressionParserTreeConstants.JJTNOTLIKE, false);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(value), 1);
connectChildren();
}
- public ASTNotLike(ASTPath path, Object value, char escapeChar) {
+ public ASTNotLike(SimpleNode path, Object value, char escapeChar) {
super(ExpressionParserTreeConstants.JJTNOTLIKE, false, escapeChar);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(value), 1);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotLikeIgnoreCase.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotLikeIgnoreCase.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotLikeIgnoreCase.java
index 8317775..f6f6d11 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotLikeIgnoreCase.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTNotLikeIgnoreCase.java
@@ -38,14 +38,14 @@ public class ASTNotLikeIgnoreCase extends IgnoreCaseNode {
super(ExpressionParserTreeConstants.JJTNOTLIKEIGNORECASE, true);
}
- public ASTNotLikeIgnoreCase(ASTPath path, Object value) {
+ public ASTNotLikeIgnoreCase(SimpleNode path, Object value) {
super(ExpressionParserTreeConstants.JJTNOTLIKEIGNORECASE, true);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(value), 1);
connectChildren();
}
- public ASTNotLikeIgnoreCase(ASTPath path, Object value, char escapeChar) {
+ public ASTNotLikeIgnoreCase(SimpleNode path, Object value, char escapeChar) {
super(ExpressionParserTreeConstants.JJTNOTLIKEIGNORECASE, true, escapeChar);
jjtAddChild(path, 0);
jjtAddChild(new ASTScalar(value), 1);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSqrt.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSqrt.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSqrt.java
new file mode 100644
index 0000000..d311444
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSqrt.java
@@ -0,0 +1,48 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.util.ConversionUtil;
+
+/**
+ * @since 4.0
+ */
+public class ASTSqrt extends ASTFunctionCall {
+
+ ASTSqrt(int id) {
+ super(id, "SQRT");
+ }
+
+ public ASTSqrt(Expression expression) {
+ super("SQRT", expression);
+ }
+
+ @Override
+ protected Object evaluateNode(Object o) throws Exception {
+ double n = ConversionUtil.toDouble(evaluateChild(0, o), 0.0);
+ return Math.sqrt(n);
+ }
+
+ @Override
+ public Expression shallowCopy() {
+ return new ASTSqrt(id);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSubstring.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSubstring.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSubstring.java
new file mode 100644
index 0000000..c6ae864
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSubstring.java
@@ -0,0 +1,64 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.util.ConversionUtil;
+
+/**
+ * @since 4.0
+ */
+public class ASTSubstring extends ASTFunctionCall {
+
+
+ ASTSubstring(int id) {
+ super(id, "SUBSTRING");
+ }
+
+ public ASTSubstring(Expression path, Expression length, Expression offset) {
+ super("SUBSTRING", path, length, offset);
+ }
+
+ @Override
+ protected Object evaluateNode(Object o) throws Exception {
+ int len = jjtGetNumChildren();
+ if (len != 3) {
+ return null;
+ }
+
+ String s1 = ConversionUtil.toString(evaluateChild(0, o));
+ if (s1 == null) {
+ return null;
+ }
+
+ int offset = ConversionUtil.toInt(evaluateChild(1, o), 0);
+ int length = ConversionUtil.toInt(evaluateChild(2, o), 0);
+ if(length == 0) {
+ return null;
+ }
+
+ return s1.substring(offset, offset + length);
+ }
+
+ @Override
+ public Expression shallowCopy() {
+ return new ASTSubstring(id);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTTrim.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTTrim.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTTrim.java
new file mode 100644
index 0000000..2a0077a
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTTrim.java
@@ -0,0 +1,62 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.util.ConversionUtil;
+
+/**
+ * @since 4.0
+ */
+public class ASTTrim extends ASTFunctionCall {
+
+ ASTTrim(int id) {
+ super(id, "TRIM");
+ }
+
+ public ASTTrim(Expression path) {
+ super("TRIM", path);
+ }
+
+ @Override
+ protected String getExpressionOperator(int index) {
+ return null;
+ }
+
+ @Override
+ protected Object evaluateNode(Object o) throws Exception {
+ String s1 = ConversionUtil.toString(evaluateChild(0, o));
+ if (s1 == null) {
+ return null;
+ }
+
+ return s1.trim();
+ }
+
+ @Override
+ public int getType() {
+ return Expression.FUNCTION_CALL;
+ }
+
+ @Override
+ public Expression shallowCopy() {
+ return new ASTTrim(id);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTUpper.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTUpper.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTUpper.java
new file mode 100644
index 0000000..fad6026
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTUpper.java
@@ -0,0 +1,51 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.util.ConversionUtil;
+
+/**
+ * @since 4.0
+ */
+public class ASTUpper extends ASTFunctionCall {
+
+ ASTUpper(int id) {
+ super(id, "UPPER");
+ }
+
+ public ASTUpper(Expression expression) {
+ super("UPPER", expression);
+ }
+
+ @Override
+ protected Object evaluateNode(Object o) throws Exception {
+ String s1 = ConversionUtil.toString(evaluateChild(0, o));
+ if (s1 == null) {
+ return null;
+ }
+ return s1.toUpperCase();
+ }
+
+ @Override
+ public Expression shallowCopy() {
+ return new ASTUpper(id);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/SimpleNode.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/SimpleNode.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/SimpleNode.java
index 458afb4..cbe9e16 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/SimpleNode.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/SimpleNode.java
@@ -326,10 +326,8 @@ public abstract class SimpleNode extends Expression implements Node {
}
protected Node wrapChild(Object child) {
- // when child is null, there's no way of telling whether this is a
- // scalar or
- // not... fuzzy... maybe we should stop using this method - it is too
- // generic
+ // when child is null, there's no way of telling whether this is a scalar or not... fuzzy...
+ // maybe we should stop using this method - it is too generic
return (child instanceof Node || child == null) ? (Node) child : new ASTScalar(child);
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/util/ConversionUtil.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/util/ConversionUtil.java b/cayenne-server/src/main/java/org/apache/cayenne/util/ConversionUtil.java
index 81729de..69f00aa 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/util/ConversionUtil.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/util/ConversionUtil.java
@@ -30,20 +30,17 @@ import org.apache.cayenne.exp.ExpressionException;
*
* @since 1.1
*/
-public class ConversionUtil {
+public final class ConversionUtil {
public static int toInt(Object object, int defaultValue) {
if (object == null) {
return defaultValue;
- }
- else if (object instanceof Number) {
+ } else if (object instanceof Number) {
return ((Number) object).intValue();
- }
- else if (object instanceof String) {
+ } else if (object instanceof String) {
try {
return Integer.parseInt((String) object);
- }
- catch (NumberFormatException ex) {
+ } catch (NumberFormatException ex) {
return defaultValue;
}
}
@@ -57,15 +54,31 @@ public class ConversionUtil {
public static long toLong(Object object, long defaultValue) {
if (object == null) {
return defaultValue;
- }
- else if (object instanceof Number) {
+ } else if (object instanceof Number) {
return ((Number) object).longValue();
- }
- else if (object instanceof String) {
+ } else if (object instanceof String) {
try {
return Long.parseLong((String) object);
+ } catch (NumberFormatException ex) {
+ return defaultValue;
}
- catch (NumberFormatException ex) {
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * @since 4.0
+ */
+ public static double toDouble(Object object, double defaultValue) {
+ if (object == null) {
+ return defaultValue;
+ } else if (object instanceof Number) {
+ return ((Number) object).doubleValue();
+ } else if (object instanceof String) {
+ try {
+ return Double.parseDouble((String) object);
+ } catch (NumberFormatException ex) {
return defaultValue;
}
}
@@ -89,14 +102,11 @@ public class ConversionUtil {
if (object == null) {
return null;
- }
- else if (object instanceof BigDecimal) {
+ } else if (object instanceof BigDecimal) {
return (BigDecimal) object;
- }
- else if (object instanceof BigInteger) {
+ } else if (object instanceof BigInteger) {
return new BigDecimal((BigInteger) object);
- }
- else if (object instanceof Number) {
+ } else if (object instanceof Number) {
return new BigDecimal(((Number) object).doubleValue());
}
@@ -109,20 +119,15 @@ public class ConversionUtil {
public static Comparable toComparable(Object object) {
if (object == null) {
return null;
- }
- else if (object instanceof Comparable) {
+ } else if (object instanceof Comparable) {
return (Comparable) object;
- }
- else if (object instanceof StringBuilder) {
+ } else if (object instanceof StringBuilder) {
return object.toString();
- }
- else if (object instanceof StringBuffer) {
+ } else if (object instanceof StringBuffer) {
return object.toString();
- }
- else if (object instanceof char[]) {
+ } else if (object instanceof char[]) {
return new String((char[]) object);
- }
- else {
+ } else {
throw new ClassCastException(
"Invalid Comparable class:" + object.getClass().getName());
}
@@ -134,17 +139,13 @@ public class ConversionUtil {
public static String toString(Object object) {
if (object == null) {
return null;
- }
- else if (object instanceof String) {
+ } else if (object instanceof String) {
return (String) object;
- }
- else if (object instanceof StringBuffer) {
+ } else if (object instanceof StringBuffer) {
return object.toString();
- }
- else if (object instanceof char[]) {
+ } else if (object instanceof char[]) {
return new String((char[]) object);
- }
- else {
+ } else {
throw new ClassCastException(
"Invalid class for String conversion:" + object.getClass().getName());
}
@@ -156,11 +157,9 @@ public class ConversionUtil {
public static Object toUpperCase(Object object) {
if ((object instanceof String) || (object instanceof StringBuffer)) {
return object.toString().toUpperCase();
- }
- else if (object instanceof char[]) {
+ } else if (object instanceof char[]) {
return new String((char[]) object).toUpperCase();
- }
- else {
+ } else {
return object;
}
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorIT.java
index 5671830..9db81a1 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorIT.java
@@ -29,8 +29,11 @@ import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.di.Inject;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.exp.FunctionExpressionFactory;
+import org.apache.cayenne.exp.Property;
import org.apache.cayenne.query.MockQuery;
import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.testdo.testmap.Artist;
import org.apache.cayenne.testdo.testmap.Exhibit;
import org.apache.cayenne.testdo.testmap.Gallery;
import org.apache.cayenne.testdo.testmap.Painting;
@@ -132,6 +135,23 @@ public class QualifierTranslatorIT extends ServerCase {
doExpressionTest(Exhibit.class, e2, "(ta.GALLERY_ID = ?) OR (ta.GALLERY_ID = ?)");
}
+ @Test
+ public void testTrim() throws Exception {
+ Expression exp = FunctionExpressionFactory.trimExp(Artist.ARTIST_NAME.path());
+ Property<String> property = Property.create("trimmedName", exp, String.class);
+
+ doExpressionTest(Artist.class, property.like("P%"), "TRIM(ta.ARTIST_NAME) LIKE ?");
+ }
+
+ @Test
+ public void testConcat() throws Exception {
+ Expression exp = FunctionExpressionFactory.concatExp("artistName", "dateOfBirth");
+
+ Property<String> property = Property.create("concatNameAndDate", exp, String.class);
+
+ doExpressionTest(Artist.class, property.like("P%"), "CONCAT(ta.ARTIST_NAME, ta.DATE_OF_BIRTH) LIKE ?");
+ }
+
private void doExpressionTest(Class<?> queryType, String qualifier, String expectedSQL) throws Exception {
doExpressionTest(queryType, ExpressionFactory.exp(qualifier), expectedSQL);
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/test/java/org/apache/cayenne/exp/FunctionExpressionFactoryTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/FunctionExpressionFactoryTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/FunctionExpressionFactoryTest.java
new file mode 100644
index 0000000..ab3443e
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/FunctionExpressionFactoryTest.java
@@ -0,0 +1,195 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp;
+
+import org.apache.cayenne.exp.parser.ASTAbs;
+import org.apache.cayenne.exp.parser.ASTConcat;
+import org.apache.cayenne.exp.parser.ASTLength;
+import org.apache.cayenne.exp.parser.ASTLocate;
+import org.apache.cayenne.exp.parser.ASTLower;
+import org.apache.cayenne.exp.parser.ASTMod;
+import org.apache.cayenne.exp.parser.ASTScalar;
+import org.apache.cayenne.exp.parser.ASTSqrt;
+import org.apache.cayenne.exp.parser.ASTSubstring;
+import org.apache.cayenne.exp.parser.ASTTrim;
+import org.apache.cayenne.exp.parser.ASTUpper;
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 4.0
+ */
+public class FunctionExpressionFactoryTest {
+
+ @Test
+ public void substringExp() throws Exception {
+ Expression exp1 = FunctionExpressionFactory.substringExp(Artist.ARTIST_NAME.path(), 10, 15);
+ Expression exp2 = FunctionExpressionFactory.substringExp(Artist.ARTIST_NAME.getName(), 10, 15);
+ Expression exp3 = FunctionExpressionFactory.substringExp(Artist.ARTIST_NAME.path(), new ASTScalar(10), new ASTScalar(15));
+
+ assertTrue(exp1 instanceof ASTSubstring);
+
+ assertEquals(3, exp1.getOperandCount());
+ assertEquals(Artist.ARTIST_NAME.path(), exp1.getOperand(0));
+ assertEquals(10, exp1.getOperand(1));
+ assertEquals(15, exp1.getOperand(2));
+
+ assertEquals(exp1, exp2);
+ assertEquals(exp2, exp3);
+ }
+
+ @Test
+ public void trimExp() throws Exception {
+ Expression exp1 = FunctionExpressionFactory.trimExp(Artist.ARTIST_NAME.path());
+ Expression exp2 = FunctionExpressionFactory.trimExp(Artist.ARTIST_NAME.getName());
+
+ assertTrue(exp1 instanceof ASTTrim);
+
+ assertEquals(1, exp1.getOperandCount());
+ assertEquals(Artist.ARTIST_NAME.path(), exp1.getOperand(0));
+
+ assertEquals(exp1, exp2);
+ }
+
+ @Test
+ public void lowerExp() throws Exception {
+ Expression exp1 = FunctionExpressionFactory.lowerExp(Artist.ARTIST_NAME.path());
+ Expression exp2 = FunctionExpressionFactory.lowerExp(Artist.ARTIST_NAME.getName());
+
+ assertTrue(exp1 instanceof ASTLower);
+
+ assertEquals(1, exp1.getOperandCount());
+ assertEquals(Artist.ARTIST_NAME.path(), exp1.getOperand(0));
+
+ assertEquals(exp1, exp2);
+ }
+
+ @Test
+ public void upperExp() throws Exception {
+ Expression exp1 = FunctionExpressionFactory.upperExp(Artist.ARTIST_NAME.path());
+ Expression exp2 = FunctionExpressionFactory.upperExp(Artist.ARTIST_NAME.getName());
+
+ assertTrue(exp1 instanceof ASTUpper);
+
+ assertEquals(1, exp1.getOperandCount());
+ assertEquals(Artist.ARTIST_NAME.path(), exp1.getOperand(0));
+
+ assertEquals(exp1, exp2);
+ }
+
+ @Test
+ public void lengthExp() throws Exception {
+ Expression exp1 = FunctionExpressionFactory.lengthExp(Artist.ARTIST_NAME.path());
+ Expression exp2 = FunctionExpressionFactory.lengthExp(Artist.ARTIST_NAME.getName());
+
+ assertTrue(exp1 instanceof ASTLength);
+
+ assertEquals(1, exp1.getOperandCount());
+ assertEquals(Artist.ARTIST_NAME.path(), exp1.getOperand(0));
+
+ assertEquals(exp1, exp2);
+ }
+
+
+ @Test
+ public void locateExp() throws Exception {
+ Expression exp1 = FunctionExpressionFactory.locateExp("abc", Artist.ARTIST_NAME.path());
+ Expression exp2 = FunctionExpressionFactory.locateExp("abc", Artist.ARTIST_NAME.getName());
+ Expression exp3 = FunctionExpressionFactory.locateExp(new ASTScalar("abc"), Artist.ARTIST_NAME.path());
+
+ assertTrue(exp1 instanceof ASTLocate);
+
+ assertEquals(2, exp1.getOperandCount());
+ assertEquals("abc", exp1.getOperand(0));
+ assertEquals(Artist.ARTIST_NAME.path(), exp1.getOperand(1));
+
+ assertEquals(exp1, exp2);
+ assertEquals(exp2, exp3);
+ }
+
+
+ @Test
+ public void absExp() throws Exception {
+ Expression exp1 = FunctionExpressionFactory.absExp(Artist.ARTIST_NAME.path());
+ Expression exp2 = FunctionExpressionFactory.absExp(Artist.ARTIST_NAME.getName());
+
+ assertTrue(exp1 instanceof ASTAbs);
+
+ assertEquals(1, exp1.getOperandCount());
+ assertEquals(Artist.ARTIST_NAME.path(), exp1.getOperand(0));
+
+ assertEquals(exp1, exp2);
+ }
+
+ @Test
+ public void sqrtExp() throws Exception {
+ Expression exp1 = FunctionExpressionFactory.sqrtExp(Artist.ARTIST_NAME.path());
+ Expression exp2 = FunctionExpressionFactory.sqrtExp(Artist.ARTIST_NAME.getName());
+
+ assertTrue(exp1 instanceof ASTSqrt);
+
+ assertEquals(1, exp1.getOperandCount());
+ assertEquals(Artist.ARTIST_NAME.path(), exp1.getOperand(0));
+
+ assertEquals(exp1, exp2);
+ }
+
+
+ @Test
+ public void modExp() throws Exception {
+ Expression exp1 = FunctionExpressionFactory.modExp(Artist.ARTIST_NAME.path(), 10);
+ Expression exp2 = FunctionExpressionFactory.modExp(Artist.ARTIST_NAME.getName(), 10);
+ Expression exp3 = FunctionExpressionFactory.modExp(Artist.ARTIST_NAME.path(), new ASTScalar(10));
+
+ assertTrue(exp1 instanceof ASTMod);
+
+ assertEquals(2, exp1.getOperandCount());
+ assertEquals(Artist.ARTIST_NAME.path(), exp1.getOperand(0));
+ assertEquals(10, exp1.getOperand(1));
+
+ assertEquals(exp1, exp2);
+ assertEquals(exp2, exp3);
+ }
+
+
+ @Test
+ public void concatExp() throws Exception {
+ Expression exp1 = FunctionExpressionFactory.concatExp(Artist.ARTIST_NAME.path(), new ASTScalar("abc"), Artist.DATE_OF_BIRTH.path());
+ assertTrue(exp1 instanceof ASTConcat);
+ assertEquals(3, exp1.getOperandCount());
+
+ assertEquals(Artist.ARTIST_NAME.path(), exp1.getOperand(0));
+ assertEquals("abc", exp1.getOperand(1));
+ assertEquals(Artist.DATE_OF_BIRTH.path(), exp1.getOperand(2));
+
+
+ Expression exp2 = FunctionExpressionFactory.concatExp(Artist.ARTIST_NAME.getName(), Artist.DATE_OF_BIRTH.getName(), Artist.PAINTING_ARRAY.getName());
+ assertTrue(exp2 instanceof ASTConcat);
+ assertEquals(3, exp2.getOperandCount());
+
+ assertEquals(Artist.ARTIST_NAME.path(), exp2.getOperand(0));
+ assertEquals(Artist.DATE_OF_BIRTH.path(), exp2.getOperand(1));
+ assertEquals(Artist.PAINTING_ARRAY.path(), exp2.getOperand(2));
+ }
+
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java
index 5984a94..757c4d6 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.List;
+import org.apache.cayenne.exp.parser.ASTObjPath;
import org.apache.cayenne.exp.parser.PatternMatchNode;
import org.apache.cayenne.reflect.TstJavaBean;
import org.junit.Test;
@@ -223,4 +224,12 @@ public class PropertyTest {
assertEquals("prop like \"%a#_!bc%\"", e.toString());
assertEquals('#', ((PatternMatchNode) e).getEscapeChar());
}
+
+ @Test
+ public void testExpressionConstructor() {
+ Property<Integer> p = Property.create("testPath", new ASTObjPath("test.path"), Integer.class);
+ assertEquals("testPath", p.getName());
+ Expression ex = p.getExpression();
+ assertEquals("test.path", ex.toString());
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTAbsTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTAbsTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTAbsTest.java
new file mode 100644
index 0000000..a8ec060
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTAbsTest.java
@@ -0,0 +1,45 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import org.apache.cayenne.testdo.table_primitives.TablePrimitives;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 4.0
+ */
+public class ASTAbsTest {
+
+ @Test
+ public void evaluateNode() throws Exception {
+ ASTObjPath path = new ASTObjPath("intColumn");
+ ASTAbs abs = new ASTAbs(path);
+
+ TablePrimitives a = new TablePrimitives();
+ a.setIntColumn(-10);
+
+ Object res = abs.evaluateNode(a);
+ assertTrue(res instanceof Double);
+ assertEquals(10.0, res);
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTConcatTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTConcatTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTConcatTest.java
new file mode 100644
index 0000000..0374aaa
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTConcatTest.java
@@ -0,0 +1,50 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @since 4.0
+ */
+public class ASTConcatTest {
+
+ @Test
+ public void testEvaluateConcat() throws Exception {
+
+ ASTObjPath path = new ASTObjPath("artistName");
+ ASTScalar scalar = new ASTScalar(" ");
+ ASTScalar scalar1 = new ASTScalar("test");
+
+ ASTConcat concat = new ASTConcat(path, scalar, scalar1);
+
+ Artist a = new Artist();
+ a.setArtistName("name");
+
+ Object res = concat.evaluateNode(a);
+ assertTrue(res instanceof String);
+ assertEquals("name test", res);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTFunctionCallMathIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTFunctionCallMathIT.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTFunctionCallMathIT.java
new file mode 100644
index 0000000..192a1ad
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTFunctionCallMathIT.java
@@ -0,0 +1,83 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.exp.Property;
+import org.apache.cayenne.query.ObjectSelect;
+import org.apache.cayenne.testdo.table_primitives.TablePrimitives;
+import org.apache.cayenne.unit.di.server.CayenneProjects;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertEquals;
+
+/**
+ * @since 4.0
+ */
+@UseServerRuntime(CayenneProjects.TABLE_PRIMITIVES_PROJECT)
+public class ASTFunctionCallMathIT extends ServerCase {
+
+ @Inject
+ private ObjectContext context;
+
+ private TablePrimitives createPrimitives(int value) {
+ TablePrimitives primitives = context.newObject(TablePrimitives.class);
+ primitives.setIntColumn(value);
+ context.commitChanges();
+ return primitives;
+ }
+
+ @Test
+ public void testASTAbs() throws Exception {
+ TablePrimitives p1 = createPrimitives(-10);
+
+ ASTAbs exp = new ASTAbs(TablePrimitives.INT_COLUMN.path());
+ Property<Integer> intColumn = Property.create("intColumn", exp, Integer.class);
+
+ TablePrimitives p2 = ObjectSelect.query(TablePrimitives.class).where(intColumn.eq(10)).selectOne(context);
+ assertEquals(p1, p2);
+ }
+
+ @Test
+ public void testASTSqrt() throws Exception {
+ TablePrimitives p1 = createPrimitives(9);
+
+ ASTSqrt exp = new ASTSqrt(TablePrimitives.INT_COLUMN.path());
+ Property<Integer> intColumn = Property.create("intColumn", exp, Integer.class);
+
+ TablePrimitives p2 = ObjectSelect.query(TablePrimitives.class).where(intColumn.eq(3)).selectOne(context);
+ assertEquals(p1, p2);
+ }
+
+ @Test
+ public void testASTMod() throws Exception {
+ TablePrimitives p1 = createPrimitives(10);
+
+ ASTMod exp = new ASTMod(TablePrimitives.INT_COLUMN.path(), new ASTScalar((Integer)3));
+ Property<Integer> intColumn = Property.create("intColumn", exp, Integer.class);
+
+ TablePrimitives p2 = ObjectSelect.query(TablePrimitives.class).where(intColumn.eq(1)).selectOne(context);
+ assertEquals(p1, p2);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTFunctionCallStringIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTFunctionCallStringIT.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTFunctionCallStringIT.java
new file mode 100644
index 0000000..cbb7514
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTFunctionCallStringIT.java
@@ -0,0 +1,155 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cayenne.exp.parser;
+
+import java.util.Date;
+
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.exp.Property;
+import org.apache.cayenne.query.ObjectSelect;
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.apache.cayenne.unit.di.server.CayenneProjects;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertEquals;
+
+/**
+ * @since 4.0
+ */
+@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
+public class ASTFunctionCallStringIT extends ServerCase {
+
+ @Inject
+ private ObjectContext context;
+
+ private Artist createArtist(String name) throws Exception {
+ Artist a1 = context.newObject(Artist.class);
+ a1.setArtistName(name);
+ a1.setDateOfBirth(new Date());
+ context.commitChanges();
+ return a1;
+ }
+
+ @Test
+ public void testASTTrimInWhere() throws Exception {
+ Artist a1 = createArtist(" name ");
+
+ ASTTrim exp = new ASTTrim(Artist.ARTIST_NAME.path());
+ Property<String> trimmedName = Property.create("trimmedName", exp, String.class);
+
+ Artist a2 = ObjectSelect.query(Artist.class).where(trimmedName.eq("name")).selectOne(context);
+ assertEquals(a1, a2);
+ }
+
+ @Test
+ public void testASTUpperInWhere() throws Exception {
+ Artist a1 = createArtist("name");
+
+ ASTUpper exp = new ASTUpper(Artist.ARTIST_NAME.path());
+ Property<String> upperName = Property.create("upperName", exp, String.class);
+
+ Artist a2 = ObjectSelect.query(Artist.class).where(upperName.eq("NAME")).selectOne(context);
+ assertEquals(a1, a2);
+ }
+
+ @Test
+ public void testASTLowerInWhere() throws Exception {
+ Artist a1 = createArtist("NAME");
+
+ ASTLower exp = new ASTLower(Artist.ARTIST_NAME.path());
+ Property<String> lowerName = Property.create("lowerName", exp, String.class);
+
+ Artist a2 = ObjectSelect.query(Artist.class).where(lowerName.eq("name")).selectOne(context);
+ assertEquals(a1, a2);
+ }
+
+ @Test
+ public void testASTSubstringInWhere() throws Exception {
+ Artist a1 = createArtist("1234567890xyz");
+
+ ASTSubstring exp = new ASTSubstring(Artist.ARTIST_NAME.path(), new ASTScalar((Integer)2), new ASTScalar((Integer)8));
+ Property<String> substrName = Property.create("substrName", exp, String.class);
+
+ Artist a2 = ObjectSelect.query(Artist.class).where(substrName.eq("23456789")).selectOne(context);
+ assertEquals(a1, a2);
+ }
+
+ @Test
+ public void testASTConcat() throws Exception {
+ Artist a1 = createArtist("Pablo");
+
+ ASTScalar scalar1 = new ASTScalar(" ");
+ ASTScalar scalar2 = new ASTScalar("Picasso");
+
+ ASTConcat exp = new ASTConcat(Artist.ARTIST_NAME.path(), scalar1, scalar2);
+ Property<String> concatName = Property.create("concatName", exp, String.class);
+
+ Artist a2 = ObjectSelect.query(Artist.class).where(concatName.eq("Pablo Picasso")).selectOne(context);
+ assertEquals(a1, a2);
+ }
+
+ @Test
+ public void testASTLength() throws Exception {
+ Artist a1 = createArtist("123456");
+
+ ASTLength exp = new ASTLength(Artist.ARTIST_NAME.path());
+ Property<Integer> nameLength = Property.create("nameLength", exp, Integer.class);
+
+ Artist a2 = ObjectSelect.query(Artist.class).where(nameLength.gt(5)).selectOne(context);
+ assertEquals(a1, a2);
+
+ Artist a3 = ObjectSelect.query(Artist.class).where(nameLength.lt(5)).selectOne(context);
+ assertEquals(null, a3);
+ }
+
+ @Test
+ public void testASTLocate() throws Exception {
+ Artist a1 = createArtist("1267834567890abc");
+
+ ASTScalar substr = new ASTScalar("678");
+// ASTScalar offset = new ASTScalar((Integer)5); // not all DBs support offset parameter, so skip it
+ ASTLocate exp = new ASTLocate(substr, Artist.ARTIST_NAME.path());
+ Property<Integer> nameLoc = Property.create("nameLoc", exp, Integer.class);
+
+ Artist a2 = ObjectSelect.query(Artist.class).where(nameLoc.eq(3)).selectOne(context);
+ assertEquals(a1, a2);
+ }
+
+ @Test
+ public void testCombinedFunction() throws Exception {
+ Artist a1 = createArtist("absdefghij klmnopq"); // substring with length 10 from 3 is "sdefghij "
+
+ ASTSubstring substring = new ASTSubstring(
+ Artist.ARTIST_NAME.path(),
+ new ASTScalar((Integer)3),
+ new ASTScalar((Integer)10));
+ ASTTrim trim = new ASTTrim(substring);
+ ASTUpper upper = new ASTUpper(trim);
+ ASTConcat concat = new ASTConcat(upper, new ASTScalar(" "), new ASTScalar("test"));
+
+ Property<String> name = Property.create("substrName", concat, String.class);
+ Artist a2 = ObjectSelect.query(Artist.class).where(name.eq("SDEFGHIJ test")).selectOne(context);
+ assertEquals(a1, a2);
+ }
+
+}