You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2013/08/02 15:56:48 UTC
svn commit: r1509717 - in
/cayenne/main/trunk/framework/cayenne-core-unpublished/src:
main/java/org/apache/cayenne/exp/parser/ test/java/org/apache/cayenne/exp/
Author: aadamchik
Date: Fri Aug 2 13:56:48 2013
New Revision: 1509717
URL: http://svn.apache.org/r1509717
Log:
CAY-1860 In-memory matching of DataObjects against ObjectId or int
experimenting with static evaluator for LHS expression
Added:
cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/Evaluator.java
Modified:
cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTEqual.java
cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java
cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTNotIn.java
cayenne/main/trunk/framework/cayenne-core-unpublished/src/test/java/org/apache/cayenne/exp/ExpressionTest.java
Modified: cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTEqual.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTEqual.java?rev=1509717&r1=1509716&r2=1509717&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTEqual.java (original)
+++ cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTEqual.java Fri Aug 2 13:56:48 2013
@@ -19,11 +19,8 @@
package org.apache.cayenne.exp.parser;
-import java.math.BigDecimal;
import java.util.List;
-import org.apache.cayenne.DataObject;
-import org.apache.cayenne.PersistenceState;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ValueInjector;
import org.apache.commons.logging.Log;
@@ -35,8 +32,9 @@ import org.apache.commons.logging.LogFac
* @since 1.1
*/
public class ASTEqual extends ConditionNode implements ValueInjector {
- private static final Log logObj = LogFactory.getLog(ASTEqual.class);
+ private static final Log logObj = LogFactory.getLog(ASTEqual.class);
+
/**
* Constructor used by expression parser. Do not invoke directly.
*/
@@ -70,26 +68,26 @@ public class ASTEqual extends ConditionN
return evaluateImpl(o1, o2);
}
-
+
/**
- * Compares two objects, if one of them is array, 'in' operation is performed
+ * Compares two objects, if one of them is array, 'in' operation is
+ * performed
*/
static boolean evaluateImpl(Object o1, Object o2) {
// TODO: maybe we need a comparison "strategy" here, instead of
// a switch of all possible cases? ... there were other requests for
// more relaxed type-unsafe comparison (e.g. numbers to strings)
-
+
if (o1 == null && o2 == null) {
return true;
- }
- else if (o1 != null) {
- /**
- * Per CAY-419 we perform 'in' comparison if one object is a list,
- * and other is not
- */
+ } else if (o1 != null) {
+
+ // Per CAY-419 we perform 'in' comparison if one object is a list,
+ // and other is not
+
if (o1 instanceof List && !(o2 instanceof List)) {
for (Object element : ((List<?>) o1)) {
- if (element != null && evaluateAtomic(element, o2)) {
+ if (element != null && Evaluator.evaluator(element).eq(element, o2)) {
return true;
}
}
@@ -97,45 +95,16 @@ public class ASTEqual extends ConditionN
}
if (o2 instanceof List && !(o1 instanceof List)) {
for (Object element : ((List<?>) o2)) {
- if (element != null && evaluateAtomic(element, o1)) {
+ if (element != null && Evaluator.evaluator(element).eq(element, o1)) {
return true;
}
}
return false;
}
-
- return evaluateAtomic(o1, o2);
- }
- return false;
- }
-
- /**
- * Compares two objects. They must not be null
- */
- static boolean evaluateAtomic(Object o1, Object o2) {
- // BigDecimals must be compared using compareTo (
- // see CAY-280 and BigDecimal.equals JavaDoc)
- if (o1 instanceof BigDecimal) {
- if (o2 instanceof BigDecimal) {
- return ((BigDecimal) o1).compareTo((BigDecimal) o2) == 0;
- }
- return false;
+ return Evaluator.evaluator(o1).eq(o1, o2);
}
-
- if (o1 instanceof DataObject) {
- if (o2 instanceof DataObject && ((DataObject)o1).getObjectId().equals(((DataObject)o2).getObjectId())) {
- if (((DataObject)o1).getObjectContext().equals(((DataObject)o2).getObjectContext())) {
- return o1.equals(o2);
- } else if (((DataObject)o1).getPersistenceState() == ((DataObject)o2).getPersistenceState()) {
- DataObject o1_context = (((DataObject)o2).getObjectContext()).localObject(((DataObject)o1));
- return ((DataObject)o2).equals(o1_context);
- }
- }
- return false;
- }
-
- return o1.equals(o2);
+ return false;
}
/**
@@ -150,11 +119,11 @@ public class ASTEqual extends ConditionN
protected String getExpressionOperator(int index) {
return "=";
}
-
+
@Override
protected String getEJBQLExpressionOperator(int index) {
if (jjtGetChild(1) instanceof ASTScalar && ((ASTScalar) jjtGetChild(1)).getValue() == null) {
- //for ejbql, we need "is null" instead of "= null"
+ // for ejbql, we need "is null" instead of "= null"
return "is";
}
return getExpressionOperator(index);
@@ -166,27 +135,25 @@ public class ASTEqual extends ConditionN
}
public void injectValue(Object o) {
- //try to inject value, if one of the operands is scalar, and other is a path
-
+ // try to inject value, if one of the operands is scalar, and other is a
+ // path
+
Node[] args = new Node[] { jjtGetChild(0), jjtGetChild(1) };
-
+
int scalarIndex = -1;
if (args[0] instanceof ASTScalar) {
scalarIndex = 0;
- }
- else if (args[1] instanceof ASTScalar) {
+ } else if (args[1] instanceof ASTScalar) {
scalarIndex = 1;
}
-
+
if (scalarIndex != -1 && args[1 - scalarIndex] instanceof ASTObjPath) {
- //inject
+ // inject
ASTObjPath path = (ASTObjPath) args[1 - scalarIndex];
try {
path.injectValue(o, evaluateChild(scalarIndex, o));
- }
- catch (Exception ex) {
- logObj.warn("Failed to inject value " +
- " on path " + path.getPath() + " to " + o, ex);
+ } catch (Exception ex) {
+ logObj.warn("Failed to inject value " + " on path " + path.getPath() + " to " + o, ex);
}
}
}
Modified: cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java?rev=1509717&r1=1509716&r2=1509717&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java (original)
+++ cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTIn.java Fri Aug 2 13:56:48 2013
@@ -64,22 +64,21 @@ public class ASTIn extends ConditionNode
if (objects == null) {
return Boolean.FALSE;
}
-
+
int size = objects.length;
for (int i = 0; i < size; i++) {
if (objects[i] != null) {
if (o1 instanceof Collection) {
- /* handle the case where we have a collection of objects */
- for (Object obj : (Collection) o1) {
- if (ASTEqual.evaluateAtomic(obj, objects[i])) {
+ // handle the case where we have a collection of objects
+ for (Object obj : (Collection) o1) {
+ if (Evaluator.evaluator(obj).eq(obj, objects[i])) {
return Boolean.TRUE;
- }
- }
- }
- else {
- if (ASTEqual.evaluateAtomic(o1, objects[i])) {
+ }
+ }
+ } else {
+ if (Evaluator.evaluator(o1).eq(o1, objects[i])) {
return Boolean.TRUE;
- }
+ }
}
}
}
@@ -108,7 +107,7 @@ public class ASTIn extends ConditionNode
@Override
protected Object transformExpression(Transformer transformer) {
Object transformed = super.transformExpression(transformer);
-
+
// transform empty ASTIn to ASTFalse
if (transformed instanceof ASTIn) {
ASTIn exp = (ASTIn) transformed;
Modified: cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTNotIn.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTNotIn.java?rev=1509717&r1=1509716&r2=1509717&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTNotIn.java (original)
+++ cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/ASTNotIn.java Fri Aug 2 13:56:48 2013
@@ -62,7 +62,7 @@ public class ASTNotIn extends ConditionN
int size = objects.length;
for (int i = 0; i < size; i++) {
- if (objects[i] != null && ASTEqual.evaluateAtomic(o1, objects[i])) {
+ if (objects[i] != null && Evaluator.evaluator(o1).eq(o1, objects[i])) {
return Boolean.FALSE;
}
}
Added: cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/Evaluator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/Evaluator.java?rev=1509717&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/Evaluator.java (added)
+++ cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/exp/parser/Evaluator.java Fri Aug 2 13:56:48 2013
@@ -0,0 +1,209 @@
+/*****************************************************************
+ * 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.math.BigDecimal;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.cayenne.Cayenne;
+import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.Persistent;
+import org.apache.cayenne.util.ConversionUtil;
+
+/**
+ * Performs argument conversions for a calling binary expression, so that the
+ * expression could eval the arguments of the same type.
+ *
+ * @since 3.2
+ */
+abstract class Evaluator {
+
+ private static final ConcurrentMap<Class<?>, Evaluator> evaluators;
+ private static final Evaluator DEFAULT_EVALUATOR;
+ private static final Evaluator PERSISTENT_EVALUATOR;
+ private static final Evaluator BIG_DECIMAL_EVALUATOR;
+ private static final Evaluator COMPAREABLE_EVALUATOR;
+
+ static class NullEvaluator extends Evaluator {
+ Evaluator delegate;
+
+ NullEvaluator(Evaluator delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ int compare(Object lhs, Object rhs) {
+ if (lhs == null && rhs == null) {
+ return 0;
+ } else if (lhs == null) {
+ return -1;
+ } else if (rhs == null) {
+ return 1;
+ } else {
+ return delegate.compare(lhs, rhs);
+ }
+ }
+
+ @Override
+ boolean eq(Object lhs, Object rhs) {
+ if (lhs == null) {
+ return rhs == null;
+ }
+
+ return (rhs != null) ? delegate.eq(lhs, rhs) : false;
+ }
+ }
+
+ static {
+ evaluators = new ConcurrentHashMap<Class<?>, Evaluator>();
+ DEFAULT_EVALUATOR = new NullEvaluator(new Evaluator() {
+ @Override
+ boolean eq(Object lhs, Object rhs) {
+ return lhs.equals(rhs);
+ }
+
+ @Override
+ int compare(Object lhs, Object rhs) {
+ throw new UnsupportedOperationException("Unsupported");
+ }
+ });
+
+ PERSISTENT_EVALUATOR = new NullEvaluator(new Evaluator() {
+
+ @Override
+ int compare(Object lhs, Object rhs) {
+ throw new UnsupportedOperationException("Unsupported");
+ }
+
+ @Override
+ boolean eq(Object lhs, Object rhs) {
+
+ Persistent lhsPersistent = (Persistent) lhs;
+
+ if (rhs instanceof Persistent) {
+ return lhsPersistent.getObjectId().equals(((Persistent) rhs).getObjectId());
+ }
+
+ if (rhs instanceof ObjectId) {
+ return lhsPersistent.getObjectId().equals((ObjectId) rhs);
+ }
+
+ // comparing ObjectId with a single value ...
+ if (lhsPersistent.getObjectId().getIdSnapshot().size() != 1) {
+ return false;
+ }
+
+ if (rhs instanceof Number) {
+ return Cayenne.longPKForObject(lhsPersistent) == ((Number) rhs).longValue();
+ }
+
+ return Cayenne.pkForObject(lhsPersistent).equals(rhs);
+ }
+ });
+
+ BIG_DECIMAL_EVALUATOR = new NullEvaluator(new Evaluator() {
+
+ @Override
+ int compare(Object lhs, Object rhs) {
+ return ((BigDecimal) lhs).compareTo(ConversionUtil.toBigDecimal(rhs));
+ }
+
+ @Override
+ boolean eq(Object lhs, Object rhs) {
+
+ // BigDecimals must be compared using compareTo (
+ // see CAY-280 and BigDecimal.equals JavaDoc)
+
+ return compare(lhs, rhs) == 0;
+ }
+ });
+
+ COMPAREABLE_EVALUATOR = new NullEvaluator(new Evaluator() {
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
+ int compare(Object lhs, Object rhs) {
+ return ((Comparable) lhs).compareTo(ConversionUtil.toComparable(rhs));
+ }
+
+ @Override
+ boolean eq(Object lhs, Object rhs) {
+ return lhs.equals(rhs);
+ }
+ });
+ }
+
+ static <T> Evaluator evaluator(Object lhs) {
+
+ if(lhs == null) {
+ return DEFAULT_EVALUATOR;
+ }
+
+ Class<?> lhsType = lhs.getClass();
+
+ Evaluator e = evaluators.get(lhsType);
+
+ if (e == null) {
+ Evaluator created = compileEvaluator(lhsType);
+ Evaluator existing = evaluators.putIfAbsent(lhsType, created);
+ e = existing != null ? existing : created;
+ }
+
+ return e;
+ }
+
+ private static Evaluator compileEvaluator(Class<?> lhsType) {
+
+ Evaluator ev = findInHierarchy(lhsType);
+ if (ev != null) {
+ return ev;
+ }
+
+ // check known interfaces
+ if (Persistent.class.isAssignableFrom(lhsType)) {
+ return PERSISTENT_EVALUATOR;
+ }
+
+ if (BigDecimal.class.isAssignableFrom(lhsType)) {
+ return BIG_DECIMAL_EVALUATOR;
+ }
+
+ if (Comparable.class.isAssignableFrom(lhsType)) {
+ return COMPAREABLE_EVALUATOR;
+ }
+
+ // nothing we recognize... return default
+ return DEFAULT_EVALUATOR;
+ }
+
+ private static Evaluator findInHierarchy(Class<?> lhsType) {
+
+ if (Object.class.equals(lhsType)) {
+ return null;
+ }
+
+ Evaluator ev = evaluators.get(lhsType);
+ return (ev != null) ? ev : findInHierarchy(lhsType.getSuperclass());
+ }
+
+ abstract boolean eq(Object lhs, Object rhs);
+
+ abstract int compare(Object lhs, Object rhs);
+}
Modified: cayenne/main/trunk/framework/cayenne-core-unpublished/src/test/java/org/apache/cayenne/exp/ExpressionTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-core-unpublished/src/test/java/org/apache/cayenne/exp/ExpressionTest.java?rev=1509717&r1=1509716&r2=1509717&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-core-unpublished/src/test/java/org/apache/cayenne/exp/ExpressionTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-core-unpublished/src/test/java/org/apache/cayenne/exp/ExpressionTest.java Fri Aug 2 13:56:48 2013
@@ -253,15 +253,14 @@ public class ExpressionTest extends Serv
assertTrue(e.match(objects.get(0)));
// we change one object - so the objects are different now
- // (PersistenceState
- // different)
+ // (PersistenceState different)
a1.setArtistName("newName");
SelectQuery q2 = new SelectQuery(Painting.class);
Expression ex2 = ExpressionFactory.matchExp(Painting.TO_ARTIST_PROPERTY, a1);
q2.setQualifier(ex2);
- assertFalse(ex2.match(objects.get(0)));
+ assertTrue(ex2.match(objects.get(0)));
Artist a2 = context.newObject(Artist.class);
a1.setArtistName("Equals");