openjpa-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ppod...@apache.org
Subject svn commit: r801045 [1/2] - in /openjpa/trunk: openjpa-kernel/src/main/java/org/apache/openjpa/datacache/ openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ openjpa-kernel/src/test/java/...
Date Wed, 05 Aug 2009 02:07:55 GMT
Author: ppoddar
Date: Wed Aug  5 02:07:54 2009
New Revision: 801045

URL: http://svn.apache.org/viewvc?rev=801045&view=rev
Log:
OPENJPA-1225: Shape of Result for Criteria query. Draft.

Added:
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultShape.java   (with props)
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultShapePacker.java   (with props)
    openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/util/TestResultShape.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Bar.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Bar_.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Foo.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/FooBar.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Foo_.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CompoundSelections.java   (with props)
Removed:
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/NewInstanceSelection.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/TupleSelection.java
Modified:
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExpressionStoreQuery.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultPacker.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreQuery.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/CriteriaTest.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/TestMultiselect.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaBuilder.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaExpressionBuilder.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaQueryImpl.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/ExpressionImpl.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/Expressions.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/OrderImpl.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/SelectionImpl.java

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java?rev=801045&r1=801044&r2=801045&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java Wed Aug  5 02:07:54 2009
@@ -37,6 +37,7 @@
 import org.apache.openjpa.kernel.LockLevels;
 import org.apache.openjpa.kernel.OpenJPAStateManager;
 import org.apache.openjpa.kernel.QueryContext;
+import org.apache.openjpa.kernel.ResultShape;
 import org.apache.openjpa.kernel.StoreContext;
 import org.apache.openjpa.kernel.StoreQuery;
 import org.apache.openjpa.kernel.exps.AggregateListener;
@@ -438,6 +439,10 @@
             return _ex.getResultClass(unwrap(q));
         }
 
+        public ResultShape<?> getResultShape(StoreQuery q) {
+            return _ex.getResultShape(q);
+        }
+        
         public String[] getProjectionAliases(StoreQuery q) {
             return _ex.getProjectionAliases(unwrap(q));
         }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java?rev=801045&r1=801044&r2=801045&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java Wed Aug  5 02:07:54 2009
@@ -137,6 +137,10 @@
             return null;
         }
         
+        public ResultShape<?> getResultShape(StoreQuery q) {
+            return null;
+        }
+        
         public void getRange(StoreQuery q, Object[] params, Range range) {
         }
 

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExpressionStoreQuery.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExpressionStoreQuery.java?rev=801045&r1=801044&r2=801045&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExpressionStoreQuery.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExpressionStoreQuery.java Wed Aug  5 02:07:54 2009
@@ -353,6 +353,10 @@
         public final Class getResultClass(StoreQuery q) {
             return assertQueryExpression().resultClass;
         }
+        
+        public final ResultShape<?> getResultShape(StoreQuery q) {
+            return assertQueryExpression().shape;
+        }
 
         public final boolean[] getAscending(StoreQuery q) {
             return assertQueryExpression().ascending;

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java?rev=801045&r1=801044&r2=801045&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java Wed Aug  5 02:07:54 2009
@@ -1267,10 +1267,14 @@
             // result class but no result; means candidate is being set
             // into some result class
             _packer = new ResultPacker(_class, getAlias(), resultClass);
-        } else if (resultClass != null) {
-            // projection
+        } else if (resultClass != null) { // projection
+            ResultShape shape = ex.getResultShape(q);
             Class[] types = ex.getProjectionTypes(q);
-            _packer = new ResultPacker(types, aliases, resultClass);
+            if (shape == null) {
+                _packer = new ResultPacker(types, aliases, resultClass);
+            } else {
+                _packer = new ResultShapePacker(types, aliases, resultClass, shape);
+            }
         }
         return _packer;
     }
@@ -1934,6 +1938,10 @@
             return _executors[0].getResultClass(q);
         }
 
+        public ResultShape<?> getResultShape(StoreQuery q) {
+            return _executors[0].getResultShape(q);
+        }
+        
         public Class[] getProjectionTypes(StoreQuery q) {
             return _executors[0].getProjectionTypes(q);
         }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultPacker.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultPacker.java?rev=801045&r1=801044&r2=801045&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultPacker.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultPacker.java Wed Aug  5 02:07:54 2009
@@ -84,6 +84,17 @@
     private final Member[] _sets;
     private final Method _put;
     private final Constructor _constructor;
+    
+    /**
+     * Protected constructor to bypass this implementation but allow extension.
+     */
+    protected ResultPacker() {
+        _resultClass = null;
+        _aliases = null;
+        _sets = null;
+        _put = null;
+        _constructor = null;
+    }
 
     /**
      * Constructor for result class without a projection.
@@ -358,7 +369,7 @@
      * Return the put method if one exists.
      */
     private static Method findPut(Method[] methods) {
-        Class[] params;
+        Class<?>[] params;
         for (int i = 0; i < methods.length; i++) {
             if (!methods[i].getName().equals("put"))
                 continue;

Added: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultShape.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultShape.java?rev=801045&view=auto
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultShape.java (added)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultShape.java Wed Aug  5 02:07:54 2009
@@ -0,0 +1,521 @@
+package org.apache.openjpa.kernel;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.openjpa.enhance.Reflection;
+import org.apache.openjpa.kernel.Filters;
+
+/**
+ * Describes the shape of a query result.
+ * <br>
+ * A shape is described as a Java class by the generic type argument T. A shape may contain zero or more shapes. 
+ * A shape is categorized as follows: 
+ * <LI>A <em>primitive</em> shape can not have child shapes e.g. Foo or float. 
+ * <LI>A <em>compound</em> shape has zero or more child shapes e.g. Foo{} or Foo{String, int} or 
+ * Foo{String,Bar{Double},int}. 
+ * <LI>A <em>nesting</em> shape has one or more compound child shape(s).
+ * For example,  Foo{String,Bar{Double},int}. On the other hand, Foo{String, int} is a compound shape but is not
+ * nesting because all its child shapes are primitive.
+ * <br>
+ * A primitive category shape is declared during construction and immutable.
+ * The category of a non-primitive shape is mutable. 
+ * <br>
+ * Notice that all nested shapes are compound shapes but not all compound shapes are nesting.
+ * <br>
+ * A compound shape can <em>add</em> other primitive shapes or <em>nest</em> other shapes to any arbitrary depth.
+ * However, a shape does not allow recursive nesting of shapes.
+ * <br> 
+ * <B>Usage</B>: 
+ * The purpose of a shape is to populate an instance of T from an array of input values where each
+ * array element is further specified with a type and an alias. FillStrategy determines how a shape 
+ * populates an instance of T by consuming the input array element values.
+ * The input data is presented as an Object[] with a parallel array of types because the primitive
+ * types (short, float etc.) are not preserved in the input array. For certain FillStrategy such as
+ * MAP or BEAN, the alias of the input array element are used to identify the Map key or setter
+ * methods respectively. 
+ * 
+ * @author Pinaki Poddar
+ * 
+ * @since 2.0.0
+ *
+ */
+@SuppressWarnings("serial")
+public class ResultShape<T> implements Serializable {
+    // Determines how to populate an instance from the input array values and aliases
+    public static enum FillStrategy {
+        ARRAY,       // copy input array elements to this shape's array element values
+        ASSIGN,      // assign the single input array element to this shape's value 
+        BEAN,        // invoke bean-style setter methods of alias names with array element values as arguments
+        CONSTRUCTOR, // invoke constructor with array element values as arguments
+        MAP          // invoke put(Object, Object) method with alias as key and array elements as values 
+    }
+    
+    private final Class<T> cls;        // the type of value this shape represents or populates
+    private final boolean isPrimitive; // flags this shape as primitive
+    private boolean isNesting;         // flags this shape as nesting
+    private String alias;
+    
+    private final FillStrategy strategy;   // the strategy to populate this shape
+    private final List<ResultShape<?>> children; // children of this shape. null for primitives
+    private Set<ResultShape<?>> parents;   // the shapes that have nested this shape
+    
+    private Method putMethod;            // Population method for MAP FillStrategy
+    private Constructor<? extends T> constructor;  // Constructor for CONSTRUCTOR FillStrategy
+    private Method[] setters;            // Setter methods for BEAN FillStrategy
+
+    /**
+     * Construct a non-primitive shape with ASSIGN or ARRAY fill strategy. 
+     */
+    public ResultShape(Class<T> cls) {
+        this(cls, false);
+    }
+    
+    /**
+     * Construct a primitive or non-primitive shape with ASSIGN or ARRAY fill strategy. 
+     * If the shape is declared as primitive then the given class can not be an array.
+     */
+    public ResultShape(Class<T> cls, boolean primitive) {
+        this(cls, cls.isArray() ? FillStrategy.ARRAY : FillStrategy.ASSIGN, primitive);
+        if (cls.isArray() && primitive)
+            throw new IllegalArgumentException(cls.getSimpleName() + " can not be primitive shape");
+    }
+    
+    /**
+     * 
+     * Construct a non-primitive shape with the given fill strategy. 
+     */
+    public ResultShape(Class<T> cls, FillStrategy strategy) {
+        this(cls, strategy, false);
+    }
+    
+    /**
+     * Construct a shape with the given fill strategy. 
+     * 
+     */
+    public ResultShape(Class<T> cls, FillStrategy strategy, boolean primitive) {
+        if (cls == null) throw new NullPointerException();
+        this.cls = cls;
+        this.strategy = strategy;
+        isPrimitive = primitive;
+        children = isPrimitive ? null : new ArrayList<ResultShape<?>>();
+    }
+    
+    /**
+     * Construct a shape with the MAP fill strategy to invoke the given method. 
+     * 
+     */
+    public ResultShape(Class<T> cls, Method putMethod) {
+        if (cls == null) throw new NullPointerException();
+        this.cls = cls;
+        this.strategy = FillStrategy.MAP;
+        isPrimitive = true;
+        children = new ArrayList<ResultShape<?>>();
+    }
+
+    /**
+     * Construct a shape with the CONSTRUCTOR fill strategy to invoke the given constructor. 
+     * 
+     */
+    public ResultShape(Class<T> cls, Constructor<? extends T> cons) {
+        if (cls == null) throw new NullPointerException();
+        this.cls = cls;
+        this.strategy = FillStrategy.CONSTRUCTOR;
+        this.constructor = cons;
+        isPrimitive = false;
+        children = new ArrayList<ResultShape<?>>();
+    }
+    
+    /**
+     * Gets the type of instance populated by this shape.
+     */
+    public Class<T> getType() {
+        return cls;
+    }
+    
+    public FillStrategy getStrategy() {
+        return strategy;
+    }
+    
+    public ResultShape<T> setAlias(String alias) {
+        this.alias = alias;
+        return this;
+    }
+    
+    public String getAlias() {
+        return alias;
+    }
+    
+    /**
+     * Gets the list of classes to compose this shape and all its children.
+     * For example, a shape Foo{String,Bar{int, Date}, Double} will return
+     * {String, int, Date, Double}
+     */
+    public List<Class<?>> getCompositeTypes() {
+        List<Class<?>> result = new ArrayList<Class<?>>();
+        if (isPrimitive() || children.isEmpty()) {
+            result.add(cls);
+        } else {
+            for (ResultShape<?> child : children) {
+                result.addAll(child.getCompositeTypes());
+            }
+        }
+        return result;
+    }
+    
+    /**
+     * Gets the list of classes to compose this shape only i.e. without expanding the children's shape.
+     * For example, a shape Foo{String,Bar{int, Date}, Double} will return {String, Bar, Double}
+     */
+    public List<Class<?>> getTypes() {
+        List<Class<?>> result = new ArrayList<Class<?>>();
+        if (children.isEmpty()) {
+            result.add(cls);
+        } else {
+            for (ResultShape<?> child : children) {
+                result.add(child.getType());
+            }
+        }
+        return result;
+    }
+    
+    /**
+     * Creates a new shape of type X with the given class arguments and nests
+     * the new shape within this shape. 
+     * 
+     * @return newly created nested shape
+     */
+    public <X> ResultShape<X> nest(Class<X> cls, FillStrategy strategy, Class<?>... classes) {
+        assertNotPrimitive();
+        ResultShape<X> child = new ResultShape<X>(cls, strategy, true);
+        this.nest(child.add(classes));
+        return child;
+    }
+
+    /**
+     * Nest the given shape. 
+     * 
+     * @param shape The given shape can not be a parent of this shape
+     * to prohibit recursive nesting.
+     * 
+     * @return this shape itself
+     */
+    public ResultShape<T> nest(ResultShape<?> shape) {
+        assertNotPrimitive();
+        if (shape.isParent(this))
+            throw new IllegalArgumentException(this + " can not nest recursive " + shape);
+        children.add(shape);
+        shape.addParent(this);
+        isNesting |= !shape.isPrimitive(); 
+        return this;
+    }
+    
+    /**
+     * Adds the given shape as one of the parents of this shape.
+     * 
+     */
+    private void addParent(ResultShape<?> p) {
+        if (parents == null)
+            parents = new HashSet<ResultShape<?>>();
+        parents.add(p);
+    }
+    
+    /**
+     * Adds the given classes as child shapes of this shape.
+     * The child shapes are primitive shapes.
+     */
+    public ResultShape<T> add(Class<?>... classes) {
+        assertNotPrimitive();
+        for (Class<?> c : classes) {
+            children.add(new ResultShape(c, true));
+        }
+        return this;
+    }
+    
+    /**
+     * Gets all the child shapes.
+     */
+    public List<ResultShape<?>> getChildren() {
+        return Collections.unmodifiableList(children);
+    }
+    
+    /**
+     * Affirms if this shape can have child shapes.
+     */
+    public boolean isCompound() {
+        return !isPrimitive;
+    }
+    
+    /**
+     * Affirms if this shape can not have any child shape.
+     * A primitive shape uses ASSIGN strategy.
+     */
+    public boolean isPrimitive() {
+        return isPrimitive;
+    }
+    
+    /**
+     * Affirms if at least one child shape of this shape is a compound shape.
+     */
+    public boolean isNesting() {
+        return isNesting;
+    }
+    
+    /**
+     * Affirms if this shape is nested within other shapes.
+     */
+    public boolean isNested() {
+        return parents != null;
+    }
+    
+    /**
+     * Affirms if the given shape is a parent (or grandparent) of this shape.
+     */
+    public boolean isParent(ResultShape<?> p) {
+        if (p.getParents().contains(this))
+            return true;
+        if (children != null) {
+            for (ResultShape<?> child : children) {
+                if (child.isParent(p))
+                    return true;
+            }
+        }
+        return false;
+    }
+    
+    void assertNotPrimitive() {
+        if (isPrimitive)
+            throw new UnsupportedOperationException("Can not add/nest shape to primitive shape " + this);
+    }
+    
+    /**
+     * Gets the immediate parents of this shape.
+     */
+    public Set<ResultShape<?>> getParents() {
+        return parents == null ? Collections.EMPTY_SET : Collections.unmodifiableSet(parents);
+    }
+    
+    /**
+     * Total number of arguments required to populate the shape and all its child shapes.
+     */
+    public int argLength() {
+        if (isPrimitive() || children.isEmpty())
+            return 1;
+        int l = 0;
+        for (ResultShape<?> child : children) {
+            l += child.argLength();
+        }
+        return l;
+    }
+    
+    /**
+     * Number of arguments to populate this shape only.
+     */
+    public int length() {
+        if (isPrimitive() || children.isEmpty())
+            return 1;
+        return children.size();
+    }
+    
+    // ======================================================================================
+    // Data Population Routines
+    // ======================================================================================
+
+    /**
+     * Fill this shape and its children with the given array element values.
+     * The parallel arrays contain the actual values, the types of these values and aliases.
+     * The type and alias information are used for packing Map or invoking constructor. 
+     * The type can be different from what can be determined from array elements because
+     * of boxing of primitive types.
+     * The actual constructor argument types are sourced from types[] array.
+     */
+    public T pack(Object[] values, Class<?>[] types, String[] aliases) {
+        if (values.length < argLength()) // input can be longer than required
+            throw new IndexOutOfBoundsException(values.length + " values are less than " + 
+                    argLength() + " argumenets required to pack " + this); 
+        Object result = null;
+        Object[] args = new Object[length()];
+        Class<?>[] argTypes = new Class[length()];
+        String[] argAliases = new String[length()];
+        if (isPrimitive() || children.isEmpty()) {
+            args[0] = values[0];
+            argTypes[0] = types[0];
+            argAliases[0] = aliases[0];
+        } else { // pack each children
+            int start = 0;
+            int i = 0;
+            for (ResultShape<?> rs : children) {
+                int finish = start + rs.argLength();
+                args[i] = rs.pack(chop(values, start, finish), chop(types, start, finish), 
+                        chop(aliases, start, finish));
+                argTypes[i] = rs.getType();
+                argAliases[0] = rs.getAlias();
+                start = finish;
+                i++;
+            }
+        }
+        initializeStrategyElements(cls, args, argTypes, aliases);
+        switch (strategy) {
+        case ARRAY:
+            result = newArray(cls.getComponentType(), args);
+            break;
+        case ASSIGN:
+            result = args[0];
+            break;
+        case CONSTRUCTOR:
+            result = newInstance(constructor, args, aliases);
+            break;
+        case MAP:
+            result = newMap(putMethod, args, aliases);
+            break;
+        case BEAN:
+            result = newBean(setters, args, aliases);
+            break;
+        default:
+            result = values;
+        }
+        return (T)result;
+    }
+
+    /**
+     * Chop an array from start to finish.
+     */
+    <X> X[] chop(X[] values, int start, int finish) {
+        X[] result = (X[])Array.newInstance(values.getClass().getComponentType(), finish-start);
+        System.arraycopy(values, start, result, 0, finish-start);
+        return result;
+    }
+    
+    <X> X[] newArray(Class<X> cls, Object[] values) {
+        X[] result = (X[])Array.newInstance(cls, values.length);
+        System.arraycopy(values, 0, result, 0, values.length);
+        return result;
+    }
+    
+    /**
+     * Construct and populate an instance by the given constructor and arguments.
+     */
+    Object newInstance(Constructor<?> cons, Object[] args, String[] aliases) {
+        try {
+            Class[] types = cons.getParameterTypes();
+            for (int i = 0; i < args.length; i++) {
+                args[i] = Filters.convert(args[i], types[i]);
+            }
+            return cons.newInstance(args);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    /**
+     * Construct and populate an instance by invoking the put method 
+     * with each alias as key and element of the given args[] as value.
+     */
+    Object newMap(Method put, Object[] args, String[] aliases) {
+        try {
+            Object map = put.getDeclaringClass().newInstance();
+            for (int i = 0; i < args.length; i++)
+                putMethod.invoke(map, aliases[i], args[i]);
+            return map;
+        } catch (InvocationTargetException t) {
+            throw new RuntimeException(t.getTargetException());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    /**
+     * Create and populate a bean with the given setter methods with each array
+     * element value as argument.
+     */
+    Object newBean(Method[] setters, Object[] args, String[] aliases) {
+        try {
+            Object bean = cls.newInstance();
+            for (int i = 0; i < args.length; i++)
+                setters[i].invoke(bean, args[i]);
+            return bean;
+        } catch (InvocationTargetException t) {
+            throw new RuntimeException(t.getTargetException());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    /**
+     * Initializes the mechanism to populate. Population mechanism can be a constructor,
+     * a put method for Map or bean-style setter methods.
+     */
+    void initializeStrategyElements(Class<T> cls, Object[] args, Class<?>[] types, String[] aliases) {
+        switch (strategy) {
+        case MAP:
+            if (putMethod == null)
+                putMethod = findMapPopulationMethod();
+            break;
+        case CONSTRUCTOR:
+            if (constructor == null)
+                constructor = findConstructor(cls, args, types);
+            break;
+        case BEAN:
+            if (setters == null) {
+                setters = new Method[args.length];
+                for (int i = 0; i < args.length; i++) {
+                    setters[i] = Reflection.findSetter(cls, aliases[i], true);
+                }
+            }
+            break;  
+        default:
+        }
+    }
+    
+    /**
+     * Find a method named <code>put(Object, Object)</code> or <code>put(String, Object)</code>.
+     */
+    Method findMapPopulationMethod() {
+        try {
+            return cls.getMethod("put", Object.class, Object.class);
+        } catch (Exception e1) {
+            try {
+                return cls.getMethod("put", String.class, Object.class);
+            } catch (Exception e2) {
+                throw new RuntimeException(e2);
+            }
+        }
+    }
+    
+    /**
+     * Finds a constructor of the given class with given argument types.
+     */
+    <X> Constructor<X> findConstructor(Class<X> cls, Object[] args, Class<?>[] types) {
+        try {
+            return cls.getConstructor(types);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    /**
+     * Gets a human-readable representation of this shape.
+     * 
+     */
+    public String toString() {
+        StringBuilder buf = new StringBuilder(cls.getSimpleName());
+        if (isPrimitive() || children.isEmpty())
+            return buf.toString();
+        int i = 0;
+        for (ResultShape<?> child : children) {
+            buf.append(i++ == 0 ? "{" : ", ");
+            buf.append(child);
+        }
+        if (!children.isEmpty())
+            buf.append("}");
+        return buf.toString();
+    }
+}

Propchange: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultShape.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultShapePacker.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultShapePacker.java?rev=801045&view=auto
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultShapePacker.java (added)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultShapePacker.java Wed Aug  5 02:07:54 2009
@@ -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.openjpa.kernel;
+
+
+/**
+ * Packs result by delegation to a ResultShape.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class ResultShapePacker extends ResultPacker {
+    private final ResultShape<?> _shape;
+    private final Class<?>[] _types;
+    private final String[] _aliases;
+    public ResultShapePacker(Class<?>[] types, String[] aliases, Class resultClass, ResultShape<?> shape) {
+        super(); // bypass superclass implementation
+        _shape = shape;
+        _types = types;
+        _aliases = aliases;
+    }
+    
+    @Override
+    public Object pack(Object o) {
+        return pack(new Object[]{o});
+    }
+    
+    @Override
+    public Object pack(Object[] values) {
+        if (_shape == null)
+            return super.pack(values);
+        return _shape.pack(values, _types, _aliases);
+    }
+
+}

Propchange: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultShapePacker.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreQuery.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreQuery.java?rev=801045&r1=801044&r2=801045&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreQuery.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreQuery.java Wed Aug  5 02:07:54 2009
@@ -330,6 +330,8 @@
          * Returns the result class, if any.
          */
         public Class getResultClass(StoreQuery q);
+        
+        public ResultShape<?> getResultShape(StoreQuery q);
 
         /**
          * Return a map of {@link FieldMetaData} to update

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java?rev=801045&r1=801044&r2=801045&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java Wed Aug  5 02:07:54 2009
@@ -26,6 +26,7 @@
 
 import org.apache.commons.collections.map.LinkedMap;
 import org.apache.openjpa.kernel.QueryOperations;
+import org.apache.openjpa.kernel.ResultShape;
 import org.apache.openjpa.kernel.StoreQuery;
 import org.apache.openjpa.kernel.exps.Context;
 import org.apache.openjpa.meta.ClassMetaData;
@@ -73,6 +74,7 @@
     private Boolean _aggregate = null;
     private Stack<Context> _contexts = null;
     public Object state;
+    public ResultShape<?> shape;
     
     /**
      * Set reference to the JPQL query contexts.

Added: openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/util/TestResultShape.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/util/TestResultShape.java?rev=801045&view=auto
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/util/TestResultShape.java (added)
+++ openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/util/TestResultShape.java Wed Aug  5 02:07:54 2009
@@ -0,0 +1,216 @@
+package org.apache.openjpa.util;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.openjpa.kernel.ResultShape;
+
+import junit.framework.TestCase;
+import static org.apache.openjpa.kernel.ResultShape.FillStrategy.*;
+
+public class TestResultShape extends TestCase {
+
+    public void testPrimitiveShapeIsImmutable() {
+        ResultShape<Object> shape = new ResultShape<Object>(Object.class, true);
+        assertCategory(shape, true, false, false);
+        assertEquals(ASSIGN, shape.getStrategy());
+        
+        try {
+            shape.add(int.class);
+            fail(shape + " should not allow adding other shapes");
+        } catch (UnsupportedOperationException e) {
+        }
+        try {
+            shape.nest(Object[].class, ARRAY, int.class, double.class);
+            fail(shape + " should not allow nesting other shapes");
+        } catch (UnsupportedOperationException e) {
+        }
+    }
+    
+    public void testArrayIsMutable() {
+        ResultShape<Object[]> shape = new ResultShape<Object[]>(Object[].class);
+        assertCategory(shape, false, true, false);
+        assertEquals(ARRAY, shape.getStrategy());
+        
+        shape.add(int.class, double.class); // will add primitive shapes
+        assertCategory(shape, false, true, false);
+        
+        ResultShape<Object> primitiveShape = new ResultShape<Object>(Object.class, true);
+        shape.nest(primitiveShape);
+        assertCategory(shape, false, true, false);
+        
+        ResultShape<Object[]> nonPrimitiveShape = new ResultShape<Object[]>(Object[].class);
+        nonPrimitiveShape.add(int.class, double.class);
+        assertCategory(nonPrimitiveShape, false, true, false);
+        shape.nest(nonPrimitiveShape);
+        assertCategory(shape, false, true, true);
+    }
+
+    public void testMethodImpliesMapStrategy() {
+        ResultShape<Map> mapShape = new ResultShape<Map>(Map.class, 
+                method(Map.class, "put", Object.class, Object.class));
+        assertCategory(mapShape, true, false, false);
+        assertEquals(MAP, mapShape.getStrategy());
+    }
+
+    public void testShapeWithConstrcutorStrategy() {
+        ResultShape<List> listShape = new ResultShape<List>(List.class, constructor(ArrayList.class, int.class));
+        assertCategory(listShape, false, true, false);
+        assertEquals(CONSTRUCTOR, listShape.getStrategy());
+    }
+
+    public void testGetCompositeTypes() {
+        ResultShape<Object[]> root = new ResultShape<Object[]>(Object[].class);
+        ResultShape<Bar> bar1 = new ResultShape<Bar>(Bar.class, CONSTRUCTOR, false);
+        bar1.add(int.class);
+        ResultShape<Foo> fooBarConstructor = new ResultShape<Foo>(Foo.class, 
+                constructor(Foo.class, short.class, Bar.class));
+        fooBarConstructor.add(short.class);
+        fooBarConstructor.nest(bar1);
+        root.add(Foo.class, Object.class);
+        root.nest(fooBarConstructor);
+        ResultShape<Bar> bar2 = new ResultShape<Bar>(Bar.class, CONSTRUCTOR, false);
+        root.nest(bar2);
+        assertEquals("Object[]{Foo, Object, Foo{short, Bar{int}}, Bar}", root.toString());
+        assertEquals(Arrays.asList(Foo.class, Object.class, short.class, int.class, Bar.class), 
+                root.getCompositeTypes());
+        assertEquals(Arrays.asList(Foo.class, Object.class, Foo.class, Bar.class), root.getTypes());
+        assertEquals(5, root.argLength());
+        assertEquals(4, root.length());
+    }
+
+    public void testRecursiveNestingIsNotAllowed() {
+        ResultShape<Object[]> root = new ResultShape<Object[]>(Object[].class);
+        ResultShape<Bar> bar1 = new ResultShape<Bar>(Bar.class, CONSTRUCTOR, false);
+        bar1.add(int.class);
+        ResultShape<Foo> fooBarConstructor = new ResultShape<Foo>(Foo.class, 
+                constructor(Foo.class, short.class, Bar.class));
+        fooBarConstructor.add(short.class);
+        fooBarConstructor.nest(bar1);
+        root.add(Foo.class, Object.class);
+        root.nest(fooBarConstructor);
+        ResultShape<Bar> bar2 = new ResultShape<Bar>(Bar.class, CONSTRUCTOR, false);
+        root.nest(bar2);
+        
+        try {
+            bar1.nest(fooBarConstructor);
+            fail("Expecetd recursive nesting error in nest " + fooBarConstructor + " in " + bar1);
+        } catch (IllegalArgumentException e) {
+            
+        }
+    }
+
+
+    public void testFill() {
+        //Fill this shape: Foo{short, Bar{String, Double}};
+        ResultShape<Foo> foo = new ResultShape<Foo>(Foo.class, CONSTRUCTOR, false);
+        ResultShape<Bar> bar = new ResultShape<Bar>(Bar.class, CONSTRUCTOR, false);
+        bar.add(String.class, Double.class);
+        foo.add(short.class);
+        foo.nest(bar);
+        assertEquals("Foo{short, Bar{String, Double}}", foo.toString());
+        
+        //from this array: 200s, "bar1", 12.3d)
+        Object[] values = {(short)200, "bar1", 12.3d};
+        Class[]  types  = {short.class, String.class, Double.class};
+        String[]  aliases  = {"foo-short", "foo-bar-string", "foo-bar-Double"};
+        Foo result = foo.pack(values, types, aliases);
+        assertEquals(200, result.shrt);
+        assertEquals("bar1", result.b.string);
+        assertEquals(12.3, result.b.Dbl);
+    }
+    public void testFill2() {
+        //Fill this shape: Object[]{Foo, Object, Foo{short, Bar{String, Double}}, Bar{double}};
+        ResultShape<Object[]> root = new ResultShape<Object[]>(Object[].class);
+        ResultShape<Bar> bar1 = new ResultShape<Bar>(Bar.class, CONSTRUCTOR, false);
+        bar1.add(String.class, Double.class);
+        ResultShape<Foo> fooBarConstructor = new ResultShape<Foo>(Foo.class, CONSTRUCTOR, false);
+        fooBarConstructor.add(short.class);
+        fooBarConstructor.nest(bar1);
+        ResultShape<Bar> bar2 = new ResultShape<Bar>(Bar.class, CONSTRUCTOR, false);
+        bar2.add(double.class);
+        root.add(Foo.class, Object.class);
+        root.nest(fooBarConstructor);
+        root.nest(bar2);
+        assertEquals("Object[]{Foo, Object, Foo{short, Bar{String, Double}}, Bar{double}}", root.toString());
+        
+        //from this array: new Foo(), new Object(), 200s, "bar1", 12.3d, 45.6d)
+        Object[] values = {new Foo(), new Object(), 200, "bar1", 12.3d, 45.6d};
+        Class[]  types  = {Foo.class, Object.class, short.class, String.class, Double.class, double.class};
+        String[]  aliases  = {"Foo", "Object", "foo-short", "foo-bar-string", "foo-bar-Double", "bar-double"};
+        Object[] result = root.pack(values, types, aliases);
+        
+        assertEquals(4, result.length);
+        assertEquals(Foo.class, result[0].getClass());
+        assertEquals(Object.class, result[1].getClass());
+        assertEquals(Foo.class, result[2].getClass());
+        assertEquals(Bar.class, result[3].getClass());
+        assertEquals(200, ((Foo)result[2]).shrt);
+        assertEquals("bar1", ((Foo)result[2]).b.string);
+        assertEquals(12.3, ((Foo)result[2]).b.Dbl);
+        assertEquals(45.6, ((Bar)result[3]).dbl);
+    }
+
+    void assertCategory(ResultShape<?> s, boolean primitive, boolean compound, boolean nesting) {
+        if (primitive)
+            assertTrue(s + " is not primitive", s.isPrimitive());
+        else
+            assertFalse(s + " is primitive", s.isPrimitive());
+        if (compound)
+            assertTrue(s + " is not compound", s.isCompound());
+        else 
+            assertFalse(s + " is compound", s.isCompound());
+        if (nesting)
+            assertTrue(s + " is not nesting", s.isNesting());
+        else 
+            assertFalse(s + " is nesting", s.isNesting());
+    }
+    
+    void arrayEquals(Object[] a, Object[] b) {
+        assertEquals(a.length, b.length);
+        for (int i = 0; i < a.length; i++) {
+            assertEquals(i+"-th element not equal", a[i], b[i]);
+        }
+    }
+    
+    <T> Constructor<T> constructor(Class<T> t, Class<?>...args) {
+        try {
+            return t.getConstructor(args);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+    <T> Method method(Class<T> t, String name, Class<?>...args) {
+        try {
+            return t.getMethod(name, args);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    public static class Foo {
+        private String string;
+        private int i;
+        private short shrt;
+        private Bar b;
+        public Foo() {}
+        public Foo(String s, int i) {this.string = s; this.i = i;}
+        public Foo(short s, Bar b){this.shrt = s; this.b = b;}
+        public String toString() {
+            return "Foo(string='"+string+"' i="+i+" short="+shrt+" bar="+b+"";}
+    }
+    
+    public static class Bar {
+        private String string;
+        private Double Dbl;
+        private double dbl;
+        public Bar() {}
+        public Bar(double d) {this.dbl = d;}
+        public Bar(String s, Double i) {this.string = s; this.Dbl = i;}
+        public String toString() {return "Bar(string='"+string+"' Dbl="+Dbl+" dbl="+dbl+"";}
+    }
+}

Propchange: openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/util/TestResultShape.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/CriteriaTest.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/CriteriaTest.java?rev=801045&r1=801044&r2=801045&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/CriteriaTest.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/CriteriaTest.java Wed Aug  5 02:07:54 2009
@@ -21,6 +21,8 @@
 import javax.persistence.EntityManager;
 
 import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
+import org.apache.openjpa.persistence.criteria.results.Bar;
+import org.apache.openjpa.persistence.criteria.results.Foo;
 
 /**
  * Generic utility to run Criteria tests.
@@ -41,7 +43,7 @@
             Dependent.class, D.class, Employee.class, Exempt.class, FemaleUser.class, FrequentFlierPlan.class,
             Item.class, LineItem.class, Magazine.class, MaleUser.class, Manager.class, Movie.class, Order.class,
             Person.class, Phone.class, Photo.class, Product.class, Publisher.class, Request.class, Semester.class,
-            Student.class, TransactionHistory.class, Transaction.class, VideoStore.class, };
+            Student.class, TransactionHistory.class, Transaction.class, VideoStore.class, Foo.class, Bar.class};
 
     protected Class<?>[] getDomainClasses() {
         return CLASSES;

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Bar.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Bar.java?rev=801045&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Bar.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Bar.java Wed Aug  5 02:07:54 2009
@@ -0,0 +1,72 @@
+/*
+ * 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.openjpa.persistence.criteria.results;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+/**
+ * A simple class for testing select projections.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+@Entity
+public class Bar {
+    @Id
+    private long bid;
+    private short bshort;
+    private String bstring;
+    private int bint;
+    
+    protected Bar() {
+        
+    }
+    
+    public Bar(long bid, String bstring) {
+        super();
+        this.bid = bid;
+        this.bstring = bstring;
+    }
+    public long getBid() {
+        return bid;
+    }
+    public void setBid(long bid) {
+        this.bid = bid;
+    }
+    public short getBshort() {
+        return bshort;
+    }
+    public void setBshort(short bshort) {
+        this.bshort = bshort;
+    }
+    public String getBstring() {
+        return bstring;
+    }
+    public void setBstring(String bstring) {
+        this.bstring = bstring;
+    }
+    public int getBint() {
+        return bint;
+    }
+    public void setBint(int bint) {
+        this.bint = bint;
+    }
+    
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Bar.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Bar_.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Bar_.java?rev=801045&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Bar_.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Bar_.java Wed Aug  5 02:07:54 2009
@@ -0,0 +1,37 @@
+/*
+ * 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.openjpa.persistence.criteria.results;
+
+import javax.persistence.metamodel.SingularAttribute;
+import javax.persistence.metamodel.StaticMetamodel;
+
+/**
+ * Hand-written (i.e. non-canonical in techie terms).
+ *  
+ * @author Pinaki Poddar
+ *
+ */
+@StaticMetamodel(Bar.class)
+public class Bar_ {
+    public static volatile SingularAttribute<Bar, Long> bid;
+    public static volatile SingularAttribute<Bar, Integer> bint;
+    public static volatile SingularAttribute<Bar, Short> bshort;
+    public static volatile SingularAttribute<Bar, String> bstring;
+
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Bar_.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Foo.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Foo.java?rev=801045&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Foo.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Foo.java Wed Aug  5 02:07:54 2009
@@ -0,0 +1,105 @@
+/*
+ * 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.openjpa.persistence.criteria.results;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToOne;
+
+/**
+ * A simple class for testing select projections.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+
+@Entity
+public class Foo {
+    @Id
+    private long fid;
+    private String fstring;
+    private long flong;
+    private int fint;
+    @OneToOne
+    private Bar bar;
+    
+    protected Foo() {
+        
+    }
+    
+    public Foo(long fid, long flong, int fint) {
+        super();
+        this.fid = fid;
+        this.flong = flong;
+        this.fint = fint;
+    }
+
+    public Foo(long fid, String fstring) {
+        super();
+        this.fid = fid;
+        this.fstring = fstring;
+    }
+
+    public Foo(long fid, int fint) {
+        super();
+        this.fid = fid;
+        this.fint = fint;
+    }
+    
+    public long getFid() {
+        return fid;
+    }
+
+    public void setFid(long fid) {
+        this.fid = fid;
+    }
+
+    public String getFstring() {
+        return fstring;
+    }
+
+    public void setFstring(String fstring) {
+        this.fstring = fstring;
+    }
+
+    public long getFlong() {
+        return flong;
+    }
+
+    public void setFlong(long flong) {
+        this.flong = flong;
+    }
+
+    public int getFint() {
+        return fint;
+    }
+
+    public void setFint(int fint) {
+        this.fint = fint;
+    }
+
+    public Bar getBar() {
+        return bar;
+    }
+
+    public void setBar(Bar bar) {
+        this.bar = bar;
+    }
+
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Foo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/FooBar.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/FooBar.java?rev=801045&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/FooBar.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/FooBar.java Wed Aug  5 02:07:54 2009
@@ -0,0 +1,41 @@
+/*
+ * 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.openjpa.persistence.criteria.results;
+
+/**
+ * A non-entity class for testing constructor expressions in query.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class FooBar {
+    private final long fid;
+    private final long bid;
+    public FooBar(long fid, long bid) {
+        super();
+        this.fid = fid;
+        this.bid = bid;
+    }
+    public long getFid() {
+        return fid;
+    }
+    public long getBid() {
+        return bid;
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/FooBar.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Foo_.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Foo_.java?rev=801045&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Foo_.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Foo_.java Wed Aug  5 02:07:54 2009
@@ -0,0 +1,37 @@
+/*
+ * 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.openjpa.persistence.criteria.results;
+
+import javax.persistence.metamodel.SingularAttribute;
+import javax.persistence.metamodel.StaticMetamodel;
+
+/**
+ * Hand-written (i.e. non-canonical in techie terms).
+ *  
+ * @author Pinaki Poddar
+ *
+ */
+@StaticMetamodel(Foo.class)
+public class Foo_ {
+    public static volatile SingularAttribute<Foo, Long> fid;
+    public static volatile SingularAttribute<Foo, String> fstring;
+    public static volatile SingularAttribute<Foo, Integer> fint;
+    public static volatile SingularAttribute<Foo, Bar> bar;
+
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/Foo_.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/TestMultiselect.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/TestMultiselect.java?rev=801045&r1=801044&r2=801045&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/TestMultiselect.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/TestMultiselect.java Wed Aug  5 02:07:54 2009
@@ -21,16 +21,16 @@
 import java.lang.reflect.Array;
 import java.util.List;
 
-import javax.persistence.PersistenceException;
 import javax.persistence.Tuple;
+import javax.persistence.criteria.CompoundSelection;
 import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Join;
 import javax.persistence.criteria.Root;
 
 import org.apache.openjpa.persistence.criteria.CriteriaTest;
 import org.apache.openjpa.persistence.criteria.Person;
 import org.apache.openjpa.persistence.criteria.Person_;
 import org.apache.openjpa.persistence.test.AllowFailure;
-import org.apache.openjpa.util.UserException;
 
 
 /**
@@ -40,7 +40,6 @@
  *
  */
 
-@AllowFailure(value=false, message="Tests:16 Errors:1 Failure:1")
 public class TestMultiselect extends CriteriaTest {
     private static boolean initialized = false;
     
@@ -48,15 +47,30 @@
     public void setUp() {
         super.setUp();
         if (!initialized) {
+            clearData();
             createData();
             initialized = true;
         }
     }
     
+    void clearData() {
+        em.getTransaction().begin();
+        em.createQuery("DELETE FROM Foo f").executeUpdate();
+        em.createQuery("DELETE FROM Bar b").executeUpdate();
+        em.createQuery("DELETE FROM Person p").executeUpdate();
+        em.getTransaction().commit();
+    }
+    
     void createData() {
         em.getTransaction().begin();
         Person p = new Person("Test Result Shape");
         em.persist(p);
+        
+        Foo foo = new Foo(100L, "Test Foo");
+        Bar bar = new Bar(200L, "Test Bar");
+        foo.setBar(bar);
+        em.persist(foo);
+        em.persist(bar);
         em.getTransaction().commit();
     }
     
@@ -75,7 +89,7 @@
         Root<Person> p = q.from(Person.class); 
         q.multiselect(p.get(Person_.name), p.get(Person_.id));
         
-        assertResult(q, Tuple.class);
+        assertResult(q, Tuple.class, String.class, Integer.class);
     }
     
     public void testTupleQueryExplicit() {
@@ -83,7 +97,7 @@
         Root<Person> p = q.from(Person.class); 
         q.multiselect(p.get(Person_.name), p.get(Person_.id));
         
-        assertResult(q, Tuple.class);
+        assertResult(q, Tuple.class, String.class, Integer.class);
     }
   
     //=======================================================================
@@ -106,7 +120,7 @@
     
     public void testUserResultQueryWithImplicitProjection() {
         CriteriaQuery<Person> q = cb.createQuery(Person.class);
-        Root<Person> p = q.from(Person.class); 
+        q.from(Person.class); 
         
         assertResult(q, Person.class);
     }
@@ -119,6 +133,32 @@
         fail("Person has no constrcutor with (name,id)", q);
     }
     
+    public void testMultipleConstructorWithAliasRepeatedAndInOrderingClause() {
+        // SELECT NEW(p.name), p.id, NEW(p.name), p.name FROM Person p ORDER BY p.name
+        CriteriaQuery<Tuple> q = cb.createTupleQuery();
+        Root<Person> p = q.from(Person.class); 
+        q.multiselect(
+            cb.construct(Person.class, p.get(Person_.name)),
+            p.get(Person_.id),
+            cb.construct(Person.class, p.get(Person_.name)),
+            p.get(Person_.name))
+            .orderBy(cb.asc(p.get(Person_.name)));
+        
+        List<Tuple> tuples = em.createQuery(q).getResultList();
+        assertTrue(!tuples.isEmpty());
+        for (Tuple row : tuples) {
+            assertEquals(4, row.getElements().size());
+            
+            assertEquals(Person.class,  row.get(0).getClass());
+            assertEquals(Integer.class, row.get(1).getClass());
+            assertEquals(Person.class,  row.get(2).getClass());
+            assertEquals(String.class,  row.get(3).getClass());
+            
+            assertEquals(((Person)row.get(0)).getName(), ((Person)row.get(2)).getName());
+            assertEquals(((Person)row.get(0)).getName(), row.get(3));
+        }
+    }
+    
     // ======================================================================
     /**
      * If the type of the criteria query is CriteriaQuery<X[]> for
@@ -239,7 +279,40 @@
      * An element of the list passed to the multiselect method 
      * must not be a tuple- or array-valued compound selection item. 
      */
+    public void testTupleCanNotBeNested() {
+        CriteriaQuery<Tuple> q = cb.createTupleQuery();
+        Root<Person> p = q.from(Person.class);
+        
+        CompoundSelection<Tuple> tuple1 = cb.tuple(p.get(Person_.name), p.get(Person_.id));
+        CompoundSelection<Tuple> tuple2 = cb.tuple(p.get(Person_.id), p.get(Person_.name));
+        
+        try {
+            cb.tuple(tuple1, tuple2);
+            fail("Expected exception while nesting tuples");
+        } catch (IllegalArgumentException e) {
+            
+        }
+    }
     
+    public void testMultiConstructor() {
+        // SELECT NEW Foo(f.fid,f.fint), b, NEW FooBar(f.fid, b.bid) from Foo f JOIN f.bar b WHERE f.b=b
+        CriteriaQuery<Object[]> q = cb.createQuery(Object[].class);
+        Root<Foo> f = q.from(Foo.class);
+        Join<Foo, Bar> b = f.join(Foo_.bar);
+        q.multiselect(cb.construct(Foo.class, f.get(Foo_.fid), f.get(Foo_.fstring)), 
+                      b, 
+                      cb.construct(FooBar.class, f.get(Foo_.fid), b.get(Bar_.bid)));
+        q.where(cb.equal(f.get(Foo_.fid), 100L));
+        
+        List<Object[]> result = em.createQuery(q).getResultList();
+        assertFalse(result.isEmpty());
+        for (Object[] row : result) {
+            assertEquals(3, row.length);
+            assertTrue("0-th element " + row[0].getClass() + " is not Foo", row[0] instanceof Foo);
+            assertTrue("1-st element " + row[1].getClass() + " is not Bar", row[1] instanceof Bar);
+            assertTrue("2-nd element " + row[2].getClass() + " is not FooBar", row[2] instanceof FooBar);
+        }
+    }
     
 // =============== assertions by result types ========================
     
@@ -262,14 +335,23 @@
                        " does not match actual result " + toClass(element), arrayElementClasses[i].isInstance(element));
                 }
             }
+            if (resultClass == Tuple.class && arrayElementClasses != null) {
+                Tuple tuple = (Tuple)row;
+                for (int i = 0; i < arrayElementClasses.length; i++) {
+                    Object element = tuple.get(i);
+                    assertTrue(i + "-th tuple element " + toString(arrayElementClasses[i]) + 
+                       " does not match actual result " + toClass(element), arrayElementClasses[i].isInstance(element));
+                }
+            }
         }
     }
     
+    
     void fail(String msg, CriteriaQuery<?> q) {
         try {
             em.createQuery(q).getResultList();
             fail("Expected to fail " + msg);
-        } catch (UserException e) {
+        } catch (Exception e) {
             // this is an expected exception
         }
     }

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CompoundSelections.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CompoundSelections.java?rev=801045&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CompoundSelections.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CompoundSelections.java Wed Aug  5 02:07:54 2009
@@ -0,0 +1,66 @@
+package org.apache.openjpa.persistence.criteria;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.persistence.criteria.CompoundSelection;
+import javax.persistence.criteria.Selection;
+
+public class CompoundSelections {
+    private abstract static class CompoundSelectionImpl<X> extends SelectionImpl<X> implements CompoundSelection<X> {
+        private final List<Selection<?>> _args;
+        public CompoundSelectionImpl(Class<X> cls, Selection<?>...args) {
+            super(cls);
+            assertNoCompoundSelection(args);
+            _args = args == null ? (List<Selection<?>>)Collections.EMPTY_LIST : Arrays.asList(args);
+        }
+        
+        public final boolean isCompoundSelection() {
+            return true;
+        }
+        
+        /**
+         * Return selection items composing a compound selection
+         * @return list of selection items
+         * @throws IllegalStateException if selection is not a compound
+         *           selection
+         */
+        public final List<Selection<?>> getCompoundSelectionItems() {
+            return Collections.unmodifiableList(_args);
+        }
+        
+        void assertNoCompoundSelection(Selection<?>...args) {
+            if (args == null)
+                return;
+            for (Selection<?> s : args) {
+                if (s.isCompoundSelection() && !(s.getClass() == NewInstance.class)) {
+                    throw new IllegalArgumentException("compound selection " + s + " can not be nested in " + this);
+                }
+            }
+        }
+
+    }
+    
+    public static class Array<X> extends CompoundSelectionImpl<X> {
+        public Array(Class<X> cls, Selection<?>... terms) {
+            super(cls, terms);
+            if (!cls.isArray()) {
+                throw new IllegalArgumentException(cls + " is not an array. " + this + " needs an array");
+            }
+        }
+    }
+    
+    public static class NewInstance<X> extends CompoundSelectionImpl<X> {
+        public NewInstance(Class<X> cls, Selection<?>... selections) {
+            super(cls, selections);
+        }
+    }
+    
+    public static class Tuple<T extends javax.persistence.Tuple> extends CompoundSelectionImpl<T> {
+        public Tuple(final Class<T> cls, final Selection<?>[] selections) {
+            super(cls, selections);
+        }
+    }
+
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CompoundSelections.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaBuilder.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaBuilder.java?rev=801045&r1=801044&r2=801045&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaBuilder.java (original)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaBuilder.java Wed Aug  5 02:07:54 2009
@@ -24,6 +24,7 @@
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 
@@ -47,6 +48,7 @@
 import org.apache.openjpa.kernel.exps.QueryExpressions;
 import org.apache.openjpa.kernel.exps.Value;
 import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.persistence.TupleImpl;
 import org.apache.openjpa.persistence.meta.MetamodelImpl;
 
 /**
@@ -331,7 +333,7 @@
     }
 
     public <K, M extends Map<K, ?>> Expression<Set<K>> keys(M map) {
-        throw new AbstractMethodError();
+        return new Expressions.Constant<Set<K>>(map == null ? Collections.EMPTY_SET : map.keySet());
     }
 
     public Predicate le(Expression<? extends Number> x, Expression<? extends Number> y) {
@@ -554,7 +556,7 @@
      * @return selection item
      */
     public <Y> CompoundSelection<Y> construct(Class<Y> result, Selection<?>... selections) {
-        return new NewInstanceSelection<Y>(result, selections);
+        return new CompoundSelections.NewInstance<Y>(result, selections);
     }
 
     public <R> Case<R> selectCase() {
@@ -672,12 +674,11 @@
     }
 
     public <V, M extends Map<?, V>> Expression<Collection<V>> values(M map) {
-        throw new AbstractMethodError();
+        return new Expressions.Constant<Collection<V>>(map == null ? Collections.EMPTY_LIST : map.values());
     }
 
-    public CompoundSelection<Object[]> array(Selection<?>... arg0) {
-        // TODO Auto-generated method stub
-        throw new AbstractMethodError();
+    public CompoundSelection<Object[]> array(Selection<?>... terms) {
+        return new CompoundSelections.Array<Object[]>(Object[].class, terms);
     }
 
     public Predicate isNotNull(Expression<?> x) {
@@ -688,8 +689,15 @@
         return new Expressions.IsNull((ExpressionImpl<?> )x);
     }
 
+    /**
+     * Define a tuple-valued selection item
+     * @param selections  selection items
+     * @return tuple-valued compound selection
+     * @throws IllegalArgumentException if an argument is a tuple- or
+     *          array-valued selection item
+     */
     public CompoundSelection<Tuple> tuple(Selection<?>... selections) {
-        return new TupleSelection<Tuple>(Tuple.class, selections);
+        return new CompoundSelections.Tuple(TupleImpl.class, selections);
     }
     
     /**

Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaExpressionBuilder.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaExpressionBuilder.java?rev=801045&r1=801044&r2=801045&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaExpressionBuilder.java (original)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaExpressionBuilder.java Wed Aug  5 02:07:54 2009
@@ -26,6 +26,7 @@
 import java.util.Map;
 import java.util.Set;
 
+import javax.persistence.Tuple;
 import javax.persistence.criteria.Expression;
 import javax.persistence.criteria.Fetch;
 import javax.persistence.criteria.Join;
@@ -35,6 +36,8 @@
 import javax.persistence.criteria.Selection;
 import javax.persistence.metamodel.Type.PersistenceType;
 
+import org.apache.openjpa.kernel.QueryOperations;
+import org.apache.openjpa.kernel.ResultShape;
 import org.apache.openjpa.kernel.exps.AbstractExpressionBuilder;
 import org.apache.openjpa.kernel.exps.ExpressionFactory;
 import org.apache.openjpa.kernel.exps.QueryExpressions;
@@ -69,9 +72,9 @@
         
         evalOrderingAndProjection(exps, factory, q);
 
-        //exps.operation = QueryOperations.OP_SELECT;
+        exps.operation = QueryOperations.OP_SELECT;
         //exps.range = null; // Value[]
-        exps.resultClass = q.getRuntimeResultClass();
+        exps.resultClass = q.getResultType();
         exps.parameterTypes = q.getParameterTypes();
         return exps;
     }
@@ -105,26 +108,38 @@
     }
 
     protected void evalOrderingAndProjection(QueryExpressions exps, ExpressionFactory factory, CriteriaQueryImpl<?> q) {
-        Map<Expression<?>, Value> exp2Vals = evalOrdering(exps, factory, q);
-        evalProjections(exps, factory, q, exp2Vals);
+        Map<ExpressionImpl<?>, Value> exp2Vals = evalOrdering(exps, factory, q);
+        evalProjections(exps, factory, q, exp2Vals, q.getResultShape());
     }
     
-    protected Map<Expression<?>, Value> evalOrdering(QueryExpressions exps, ExpressionFactory factory, 
+    /**
+     * Evaluates the ordering expressions by converting them to kernel values.
+     * Sets the ordering fields of kernel QueryExpressions.
+     *   
+     * @param exps kernel QueryExpressions
+     * @param factory for kernel expressions
+     * @param q a criteria query
+     * 
+     * @return map of kernel values indexed by criteria query expressions that created it.
+     * These kernel values are required to be held in a map to avoid recomputing for the
+     * same CriteriaQuery Expressions appearing in ordering terms as well as projection
+     * term. 
+     * 
+     */
+    protected Map<ExpressionImpl<?>, Value> evalOrdering(QueryExpressions exps, ExpressionFactory factory, 
         CriteriaQueryImpl<?> q) {
         List<Order> orders = q.getOrderList();
         MetamodelImpl model = q.getMetamodel(); 
         int ordercount = (orders == null) ? 0 : orders.size();
-        Map<Expression<?>, Value> exp2Vals = new HashMap<Expression<?>, Value>();
+        Map<ExpressionImpl<?>, Value> exp2Vals = new HashMap<ExpressionImpl<?>, Value>();
         exps.ordering = new Value[ordercount];
         exps.orderingClauses = new String[ordercount];
         exps.orderingAliases = new String[ordercount];
         exps.ascending = new boolean[ordercount];
         for (int i = 0; i < ordercount; i++) {
             OrderImpl order = (OrderImpl)orders.get(i);
-            //Expression<? extends Comparable> expr = order.getExpression();
-            Expression<?> expr = order.getExpression();
-            Value val = Expressions.toValue(
-                    (ExpressionImpl<?>)expr, factory, model, q);
+            ExpressionImpl<?> expr = order.getExpression();
+            Value val = Expressions.toValue(expr, factory, model, q);
             exps.ordering[i] = val;
             String alias = expr.getAlias();
             exps.orderingAliases[i] = alias;
@@ -218,7 +233,7 @@
     }
 
     protected void evalProjections(QueryExpressions exps, ExpressionFactory factory, CriteriaQueryImpl<?> q,
-        Map<Expression<?>, Value> exp2Vals) {
+        Map<ExpressionImpl<?>, Value> exp2Vals, ResultShape<?> shape) {
         List<Selection<?>> selections = q.getSelectionList();
         MetamodelImpl model = q.getMetamodel();
         if (isDefaultProjection(selections, q)) {
@@ -229,26 +244,38 @@
         List<Value> projections = new ArrayList<Value>();
         List<String> aliases = new ArrayList<String>();
         List<String> clauses = new ArrayList<String>();
-        getProjections(exps, selections, projections, aliases, clauses, factory, q, model, exp2Vals);
+        getProjections(exps, selections, projections, aliases, clauses, factory, q, model, exp2Vals, shape);
         exps.projections = projections.toArray(new Value[projections.size()]);
         exps.projectionAliases = aliases.toArray(new String[aliases.size()]);
         exps.projectionClauses = clauses.toArray(new String[clauses.size()]);
+        exps.shape = shape;
     }
 
+    /**
+     * Scans the projection terms to populate the kernel QueryExpressions projection clauses
+     * and aliases.
+     *   
+     * @param exps 
+     * @param selections
+     * @param projections list of kernel values for projections 
+     * @param aliases list of kernel projection aliases 
+     * @param clauses list of kernel projection clauses
+     * @param factory for kernel expressions
+     * @param q a Criteria Query
+     * @param model of domain entities
+     * @param exp2Vals the evaluated kernel values indexed by the Criteria Expressions
+     */
     private void getProjections(QueryExpressions exps, List<Selection<?>> selections, 
         List<Value> projections, List<String> aliases, List<String> clauses, 
         ExpressionFactory factory, CriteriaQueryImpl<?> q, MetamodelImpl model, 
-        Map<Expression<?>, Value> exp2Vals) {
+        Map<ExpressionImpl<?>, Value> exp2Vals, ResultShape<?> shape) {
         for (Selection<?> s : selections) {
-            if (s instanceof TupleSelection<?> ) {
-                getProjections(exps, ((TupleSelection<?>)s).getSelectionItems(), projections, aliases, 
-                    clauses, factory, q, model, exp2Vals);
-            } else if (s instanceof NewInstanceSelection<?>) {
-                getProjections(exps, ((NewInstanceSelection<?>)s).getSelectionItems(), projections, aliases, 
-                   clauses, factory, q, model, exp2Vals);               
+            if (s.isCompoundSelection()) {
+                getProjections(exps, s.getCompoundSelectionItems(), projections, aliases, 
+                    clauses, factory, q, model, exp2Vals, q.getNestedShape(s));
             } else {
-                Value val = (exp2Vals != null && exp2Vals.containsKey(s) ? exp2Vals.get(s) :
-                    ((ExpressionImpl<?>)s).toValue(factory, model, q));
+                Value val = (exp2Vals != null && exp2Vals.containsKey(s) 
+                        ? exp2Vals.get(s) : ((ExpressionImpl<?>)s).toValue(factory, model, q));
                 String alias = s.getAlias();
                 val.setAlias(alias);
                 projections.add(val);
@@ -257,6 +284,7 @@
             }         
         }
     }
+    
 
     protected boolean isDefaultProjection(List<Selection<?>> selections, CriteriaQueryImpl<?> q) {
         if (selections == null)

Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaQueryImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaQueryImpl.java?rev=801045&r1=801044&r2=801045&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaQueryImpl.java (original)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaQueryImpl.java Wed Aug  5 02:07:54 2009
@@ -32,6 +32,7 @@
 
 import javax.persistence.Tuple;
 import javax.persistence.criteria.AbstractQuery;
+import javax.persistence.criteria.CompoundSelection;
 import javax.persistence.criteria.CriteriaQuery;
 import javax.persistence.criteria.Expression;
 import javax.persistence.criteria.Order;
@@ -43,7 +44,9 @@
 import javax.persistence.metamodel.EntityType;
 
 import org.apache.commons.collections.map.LinkedMap;
+import org.apache.openjpa.kernel.ResultShape;
 import org.apache.openjpa.kernel.StoreQuery;
+import org.apache.openjpa.kernel.ResultShape.FillStrategy;
 import org.apache.openjpa.kernel.exps.Context;
 import org.apache.openjpa.kernel.exps.ExpressionFactory;
 import org.apache.openjpa.kernel.exps.QueryExpressions;
@@ -78,8 +81,11 @@
     private Boolean             _distinct;
     private SubqueryImpl<?>     _delegator;
     private final Class<T>      _resultClass;
-    private Class<?>            _runtimeResultClass;
+    private ResultShape<?>      _shape;
+    private boolean             _multiselect;
+    private Map<Selection<?>, ResultShape<?>> _nestedShapes;
     
+
     // AliasContext
     private int aliasCount = 0;
     private static String ALIAS_BASE = "autoAlias";
@@ -96,7 +102,6 @@
     public CriteriaQueryImpl(MetamodelImpl model, Class<T> resultClass) {
         this._model = model;
         this._resultClass = resultClass;
-        _runtimeResultClass = Tuple.class.isAssignableFrom(resultClass) ? TupleImpl.class : resultClass;
         _aliases = new HashMap<Selection<?>, String>(); 
     }
     
@@ -182,9 +187,9 @@
      * @return the modified query
      */
     public CriteriaQuery<T> multiselect(Selection<?>... selections) {
-        if (selections.length > 1 && _resultClass == Object.class)
-            _runtimeResultClass = Object[].class;
-        return select(selections);
+        _multiselect = true;
+        _selections = Arrays.asList(selections); // do not telescope
+        return this;
     }
 
     /**
@@ -249,6 +254,7 @@
     }
 
     public CriteriaQuery<T> select(Selection<?>... selections) {
+        _multiselect = false;
         _selections = Arrays.asList(selections);
         return this;
     }
@@ -522,12 +528,100 @@
     public Class<T> getResultType() {
         return _resultClass;
     }
-    
-    public Class<?> getRuntimeResultClass() {
-        return _runtimeResultClass;
-    }
 
     public CriteriaQuery<T> multiselect(List<Selection<?>> list) {
         return multiselect(list.toArray(new Selection<?>[list.size()]));
     }
+    
+    // ===================================================================================
+    // Result Shape processing
+    // ===================================================================================
+    
+    /**
+     * Gets the shape of the query result. 
+     * Can be null if called before build
+     */
+    ResultShape<?> getResultShape() {
+        if (_shape == null) {
+            _shape = buildShape();
+        }
+        return _shape;
+    }
+    
+    /**
+     * Gets the shape of a selection item. Creates the shape if necessary.
+     */
+    ResultShape<?> getNestedShape(Selection<?> s) {
+        if (_nestedShapes == null) {
+            _nestedShapes = new HashMap<Selection<?>, ResultShape<?>>();
+        } else if (_nestedShapes.containsKey(s)) {
+            return _nestedShapes.get(s);
+        }
+        Class<?> type = s.getJavaType() == null ? Object.class : s.getJavaType();
+        ResultShape<?> result = null;
+        FillStrategy strategy = FillStrategy.ASSIGN;
+        if (s.isCompoundSelection()) {
+            if (s instanceof CompoundSelections.NewInstance) {
+                strategy = FillStrategy.CONSTRUCTOR;
+            } else if (s instanceof CompoundSelections.Array) {
+                strategy = FillStrategy.ARRAY;
+            } else if (s instanceof CompoundSelections.Tuple) {
+                strategy = FillStrategy.MAP;
+            }
+            result = new ResultShape(type, strategy);
+            List<Selection<?>> terms = ((CompoundSelection<?>)s).getCompoundSelectionItems();
+            for (Selection<?> term : terms) {
+                result.nest(getNestedShape(term));
+            }
+        } else {
+            result = new ResultShape(type, strategy, true);
+        }
+        _nestedShapes.put(s, result);
+        return result;
+    }
+    
+    /**
+     * Builds the result shape by creating shape for the complete result and how it nests each selection terms.
+     * The shape varies based on whether the terms were selected based on multiselect() or select(). 
+     */
+    private ResultShape<?> buildShape() {
+        Class<?> runtimeResultClass = _resultClass;
+        FillStrategy strategy = FillStrategy.ASSIGN;
+        ResultShape<?> result = null;
+        if (_multiselect) {
+            if (Tuple.class.isAssignableFrom(_resultClass)) {
+                runtimeResultClass = TupleImpl.class;
+                strategy = FillStrategy.MAP;
+           } else if (_resultClass == Object.class) {
+               if (_selections.size() > 1) { 
+                   runtimeResultClass = Object[].class;
+                   strategy = FillStrategy.ARRAY;
+               }
+           } else {
+               strategy = _resultClass.isArray() ? FillStrategy.ARRAY : FillStrategy.CONSTRUCTOR;
+           } 
+           result = new ResultShape(runtimeResultClass, strategy);
+           for (Selection<?> term : _selections) {
+               result.nest(getNestedShape(term));
+           }
+        } else { // not multiselect
+            if (Tuple.class.isAssignableFrom(_resultClass)) {
+                runtimeResultClass = TupleImpl.class;
+                strategy = FillStrategy.MAP;
+            }
+            result = new ResultShape(runtimeResultClass, strategy);
+            if (_selections == null)
+                return result;
+            if (_selections.size() == 1) {
+                result = getNestedShape(_selections.get(0));
+            } else {
+                for (Selection<?> term : _selections) {
+                    result.nest(getNestedShape(term));
+                }
+            }
+        }
+    
+        return result;
+   }
+
 }

Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/ExpressionImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/ExpressionImpl.java?rev=801045&r1=801044&r2=801045&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/ExpressionImpl.java (original)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/ExpressionImpl.java Wed Aug  5 02:07:54 2009
@@ -27,7 +27,6 @@
 
 import org.apache.openjpa.kernel.exps.ExpressionFactory;
 import org.apache.openjpa.kernel.exps.Value;
-import org.apache.openjpa.persistence.TupleElementImpl;
 import org.apache.openjpa.persistence.meta.MetamodelImpl;
 
 /**



Mime
View raw message