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 2010/09/24 21:32:23 UTC

svn commit: r1001040 - in /cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src: main/java/org/apache/cayenne/access/jdbc/ main/java/org/apache/cayenne/access/trans/ main/java/org/apache/cayenne/ejbql/ main/java/org/apache/cayenne/ejbql/parser/ ...

Author: aadamchik
Date: Fri Sep 24 19:32:22 2010
New Revision: 1001040

URL: http://svn.apache.org/viewvc?rev=1001040&view=rev
Log:
CAY-1213  Cayenne should support enum types in qualifier statements/expression handling

* CAY-1213-ejbql.patch by Andrei Veprev

Added:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLConstant.java
Modified:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLConditionTranslator.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QualifierTranslator.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLBaseVisitor.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpressionVisitor.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/SimpleNode.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/util/Util.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/query/ConstQueryTest.java

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLConditionTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLConditionTranslator.java?rev=1001040&r1=1001039&r2=1001040&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLConditionTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLConditionTranslator.java Fri Sep 24 19:32:22 2010
@@ -32,6 +32,7 @@ import org.apache.cayenne.ejbql.EJBQLBas
 import org.apache.cayenne.ejbql.EJBQLException;
 import org.apache.cayenne.ejbql.EJBQLExpression;
 import org.apache.cayenne.ejbql.parser.AggregateConditionNode;
+import org.apache.cayenne.ejbql.parser.EJBQLConstant;
 import org.apache.cayenne.ejbql.parser.EJBQLDecimalLiteral;
 import org.apache.cayenne.ejbql.parser.EJBQLEquals;
 import org.apache.cayenne.ejbql.parser.EJBQLIdentificationVariable;
@@ -685,6 +686,18 @@ public class EJBQLConditionTranslator ex
     }
 
     @Override
+    public boolean visitConst(EJBQLExpression expression) {
+        Object constValue = ((EJBQLConstant) expression).getValue();
+        String constBoundName = context.bindParameter(constValue);
+        context.append(" #bind($").append(constBoundName).append(")");
+
+        if (constValue instanceof Enum<?>) {
+            processEnumParameter(expression, constBoundName, (Enum<?>) constValue);
+        }
+        return true;
+    }
+
+    @Override
     public boolean visitIntegerLiteral(EJBQLIntegerLiteral expression) {
         if (expression.getText() == null) {
             context.append("null");
@@ -810,6 +823,60 @@ public class EJBQLConditionTranslator ex
         return false;
     }
 
+    private AttributeProperty getParentExpressionLastPathChildAttribute(
+            EJBQLExpression expression) {
+        Node parent = ((SimpleNode) expression).jjtGetParent();
+        EJBQLPathAnaliserTranslator translator = new EJBQLPathAnaliserTranslator(context);
+        parent.visit(translator);
+        translator.visitPath(parent, parent.getChildrenCount());
+
+        String id = translator.idPath;
+        if (id != null) {
+            ClassDescriptor descriptor = context.getEntityDescriptor(id);
+            if (descriptor == null) {
+                throw new EJBQLException("Unmapped id variable: " + id);
+            }
+            String pathChunk = translator.lastPathComponent;
+            Property property = descriptor.getProperty(pathChunk);
+            if (property instanceof AttributeProperty) {
+                return (AttributeProperty) property;
+            }
+        }
+        return null;
+    }
+
+    private Integer getParentExpressionSqlType(EJBQLExpression expression) {
+        AttributeProperty lastPathAttribute = getParentExpressionLastPathChildAttribute(expression);
+        return lastPathAttribute == null ? null : lastPathAttribute
+                .getAttribute()
+                .getDbAttribute()
+                .getType();
+    }
+
+    private String getParentExpressionSqlTypeName(EJBQLExpression expression) {
+        AttributeProperty lastPathAttribute = getParentExpressionLastPathChildAttribute(expression);
+        if (lastPathAttribute != null) {
+            String atrType = lastPathAttribute.getAttribute().getType();
+            return TypesMapping.getSqlNameByType(TypesMapping.getSqlTypeByJava(atrType));
+        }
+        return null;
+    }
+
+    private void processEnumParameter(
+            EJBQLExpression expression,
+            String boundName,
+            Enum<?> value) {
+        context.pushMarker("@processEnumParameter", true);
+        Integer sqlType = getParentExpressionSqlType(expression);
+        if (sqlType != null && TypesMapping.isNumeric(sqlType)) {
+            context.rebindParameter(boundName, value.ordinal());
+        }
+        else {
+            context.rebindParameter(boundName, value.name());
+        }
+        context.popMarker();
+    }
+
     private void processParameter(String boundName, EJBQLExpression expression) {
         Object object = context.getBoundParameter(boundName);
 
@@ -838,38 +905,13 @@ public class EJBQLConditionTranslator ex
 
         if (object != null) {
             context.append(" #bind($").append(boundName).append(")");
+            if (object instanceof Enum<?>) {
+                processEnumParameter(expression, boundName, (Enum<?>) object);
+            }
         }
         else {
-
-            String type = null;
-            Node parent = ((SimpleNode) expression).jjtGetParent();
-
             context.pushMarker("@processParameter", true);
-
-            EJBQLPathAnaliserTranslator translator = new EJBQLPathAnaliserTranslator(
-                    context);
-            parent.visit(translator);
-            translator.visitPath(parent, parent.getChildrenCount());
-
-            String id = translator.idPath;
-            if (id != null) {
-
-                ClassDescriptor descriptor = context.getEntityDescriptor(id);
-                if (descriptor == null) {
-                    throw new EJBQLException("Unmapped id variable: " + id);
-                }
-                String pathChunk = translator.lastPathComponent;
-
-                Property property = descriptor.getProperty(pathChunk);
-                if (property instanceof AttributeProperty) {
-                    String atrType = ((AttributeProperty) property)
-                            .getAttribute()
-                            .getType();
-
-                    type = TypesMapping.getSqlNameByType(TypesMapping
-                            .getSqlTypeByJava(atrType));
-                }
-            }
+            String type = getParentExpressionSqlTypeName(expression);
             context.popMarker();
 
             if (type == null) {

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QualifierTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QualifierTranslator.java?rev=1001040&r1=1001039&r2=1001040&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QualifierTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QualifierTranslator.java Fri Sep 24 19:32:22 2010
@@ -376,34 +376,16 @@ public class QualifierTranslator extends
     }
 
     private boolean tryAppendConst(String path, Expression parentNode) throws IOException {
-
-        if (path.length() < 3) {
-            return false;
-        }
-
-        int lastDot = path.lastIndexOf('.');
-        if (lastDot <= 0 || lastDot == path.length() - 1) {
-            return false;
-        }
-
-        String constName = path.substring(lastDot + 1);
-        String className = path.substring(0, lastDot);
-        
         Object constValue;
         try {
-            Class<?> klass = Util.getJavaClass(className);
-            Field constField = klass.getField(constName);
-            constValue = constField.get(null);
-        }
-        catch (ClassNotFoundException e) {
-            return false;
-        }
-        catch (NoSuchFieldException e) {
-            return false;
+            constValue = Util.getClassFieldValue(path);
         }
         catch (IllegalAccessException e) {
             throw new ExpressionException("Can't access const field", e);
         }
+        if (constValue == null) {
+            return false;
+        }
         appendLiteral(constValue, paramsDbType(parentNode), parentNode);
         return true;
     }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLBaseVisitor.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLBaseVisitor.java?rev=1001040&r1=1001039&r2=1001040&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLBaseVisitor.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLBaseVisitor.java Fri Sep 24 19:32:22 2010
@@ -299,6 +299,10 @@ public class EJBQLBaseVisitor implements
         return continueFlag;
     }
 
+    public boolean visitConst(EJBQLExpression expression) {
+        return continueFlag;
+    }
+
     public boolean visitPatternValue(EJBQLExpression expression) {
         return continueFlag;
     }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpressionVisitor.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpressionVisitor.java?rev=1001040&r1=1001039&r2=1001040&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpressionVisitor.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpressionVisitor.java Fri Sep 24 19:32:22 2010
@@ -275,7 +275,12 @@ public interface EJBQLExpressionVisitor 
      *            visited.
      */
     boolean visitPath(EJBQLExpression expression, int finishedChildIndex);
-    
+
+    /**
+     * @since 3.1
+     */
+    boolean visitConst(EJBQLExpression expression);
+
     boolean visitDbPath(EJBQLExpression expression, int finishedChildIndex);
 
     boolean visitPatternValue(EJBQLExpression expression);

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java?rev=1001040&r1=1001039&r2=1001040&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java Fri Sep 24 19:32:22 2010
@@ -47,6 +47,7 @@ import org.apache.cayenne.reflect.Proper
 import org.apache.cayenne.reflect.PropertyVisitor;
 import org.apache.cayenne.reflect.ToManyProperty;
 import org.apache.cayenne.reflect.ToOneProperty;
+import org.apache.cayenne.util.Util;
 
 /**
  * Produces an {@link EJBQLCompiledExpression} out of an EJBQL expression tree.
@@ -66,6 +67,7 @@ class Compiler {
     private Collection<EJBQLPath> paths;
     private EJBQLExpressionVisitor fromItemVisitor;
     private EJBQLExpressionVisitor joinVisitor;
+    private EJBQLExpressionVisitor constTransformVisitor;
     private EJBQLExpressionVisitor pathVisitor;
     private EJBQLExpressionVisitor rootDescriptorVisitor;
     private List<Object> resultComponents;
@@ -80,6 +82,7 @@ class Compiler {
         this.fromItemVisitor = new FromItemVisitor();
         this.joinVisitor = new JoinVisitor();
         this.pathVisitor = new PathVisitor();
+        this.constTransformVisitor = new ConstTransformVisitor();
     }
 
     CompiledExpression compile(String source, EJBQLExpression parsed) {
@@ -441,6 +444,7 @@ class Compiler {
 
         @Override
         public boolean visitWhere(EJBQLExpression expression) {
+            expression.visit(constTransformVisitor);
             expression.visit(pathVisitor);
 
             // continue with children as there may be subselects with their own id
@@ -583,6 +587,37 @@ class Compiler {
         }
     }
 
+    class ConstTransformVisitor extends EJBQLBaseVisitor {
+
+        @Override
+        public boolean visitPath(EJBQLExpression expression, int finishedChildIndex) {
+            EJBQLPath pathExpression = (EJBQLPath) expression;
+            String path = pathExpression.getAbsolutePath();
+            Object constValue;
+            try {
+                constValue = Util.getClassFieldValue(path);
+            }
+            catch (IllegalAccessException e) {
+                throw new EJBQLException("Can't access const field", e);
+            }
+            if (constValue != null) {
+                transformNode(pathExpression, path, constValue);
+            }
+            return false;
+        }
+
+        private void transformNode(
+                EJBQLPath pathExpression,
+                String path,
+                Object constValue) {
+            EJBQLConstant constantExpression = new EJBQLConstant(
+                    pathExpression,
+                    path,
+                    constValue);
+            pathExpression.getParent().replaceChild(pathExpression, constantExpression);
+        }
+    }
+
     class PathVisitor extends EJBQLBaseVisitor {
 
         @Override

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLConstant.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLConstant.java?rev=1001040&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLConstant.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLConstant.java Fri Sep 24 19:32:22 2010
@@ -0,0 +1,47 @@
+/*****************************************************************
+ *   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.ejbql.parser;
+
+import org.apache.cayenne.ejbql.EJBQLExpressionVisitor;
+
+public class EJBQLConstant extends SimpleNode {
+
+    private String path;
+    private Object value;
+
+    public EJBQLConstant(EJBQLPath pathExpression, String path, Object value) {
+        super(pathExpression.id);
+        this.parent = pathExpression.parent;
+        this.path = path;
+        this.value = value;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    @Override
+    protected boolean visitNode(EJBQLExpressionVisitor visitor) {
+        return visitor.visitConst(this);
+    }
+}

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/SimpleNode.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/SimpleNode.java?rev=1001040&r1=1001039&r2=1001040&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/SimpleNode.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/SimpleNode.java Fri Sep 24 19:32:22 2010
@@ -92,6 +92,18 @@ public abstract class SimpleNode impleme
         return jjtGetNumChildren();
     }
 
+    public void replaceChild(SimpleNode oldChild, SimpleNode newChild) {
+        for (int i = 0; i < children.length; i++) {
+            if (children[i] == oldChild) {
+                children[i] = newChild;
+            }
+        }
+    }
+
+    public SimpleNode getParent() {
+        return parent;
+    }
+
     public String getName() {
         String className = getClass().getName();
         int i = className.lastIndexOf("EJBQL");

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/util/Util.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/util/Util.java?rev=1001040&r1=1001039&r2=1001040&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/util/Util.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/util/Util.java Fri Sep 24 19:32:22 2010
@@ -34,6 +34,7 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.OutputStream;
 import java.io.Serializable;
+import java.lang.reflect.Field;
 import java.lang.reflect.Member;
 import java.lang.reflect.Modifier;
 import java.net.URI;
@@ -739,6 +740,38 @@ public class Util {
         }
     }
 
+    /**
+     * Returns constant field value by fully qualified path.
+     * 
+     * @return Field value or {@code null} if class or field specified in path was not
+     *         found.
+     * @since 3.1
+     */
+    public static Object getClassFieldValue(String path) throws IllegalAccessException {
+        if (path.length() < 3) {
+            return null;
+        }
+
+        int lastDot = path.lastIndexOf('.');
+        if (lastDot <= 0 || lastDot == path.length() - 1) {
+            return null;
+        }
+
+        String constName = path.substring(lastDot + 1);
+        String className = path.substring(0, lastDot);
+        try {
+            Class<?> klass = getJavaClass(className);
+            Field constField = klass.getField(constName);
+            return constField.get(null);
+        }
+        catch (ClassNotFoundException e) {
+            return null;
+        }
+        catch (NoSuchFieldException e) {
+            return null;
+        }
+    }
+
     static void setReverse(
             final Persistent sourceObject,
             String propertyName,

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/query/ConstQueryTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/query/ConstQueryTest.java?rev=1001040&r1=1001039&r2=1001040&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/query/ConstQueryTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/query/ConstQueryTest.java Fri Sep 24 19:32:22 2010
@@ -103,4 +103,25 @@ public class ConstQueryTest extends Serv
         assertEquals(1, users.size());
         assertEquals("org.apache.cayenne.testdo.consttest.Const1Type.ADMIN", ((Const1Entity) users.get(0)).getName());
     }
+    
+    public void testSelectByEnumValueWithEJBQL() throws Exception {
+        createConst1EntityDataSet();
+
+        EJBQLQuery query = new EJBQLQuery(
+                "SELECT e FROM Const1Entity e WHERE e.type = :t");
+        query.setParameter("t", Const1Type.ORDINARY);
+        List entities = context.performQuery(query);
+        assertEquals(1, entities.size());
+        assertEquals("entity1", ((Const1Entity) entities.get(0)).getName());
+    }
+
+    public void testSelectByEnumValueSpecifiedAsConstantWithEJBQL() throws Exception {
+        createConst1EntityDataSet();
+
+        EJBQLQuery query = new EJBQLQuery(
+                "SELECT e FROM Const1Entity e WHERE e.type = org.apache.cayenne.testdo.consttest.Const1Type.ORDINARY");
+        List entities = context.performQuery(query);
+        assertEquals(1, entities.size());
+        assertEquals("entity1", ((Const1Entity) entities.get(0)).getName());
+    }
 }