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 2008/05/03 22:56:25 UTC

svn commit: r653135 - in /cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src: main/java/org/apache/cayenne/access/trans/ main/java/org/apache/cayenne/map/ main/java/org/apache/cayenne/query/ test/java/org/apache/cayenne/access/ test/java/org/a...

Author: aadamchik
Date: Sat May  3 13:56:25 2008
New Revision: 653135

URL: http://svn.apache.org/viewvc?rev=653135&view=rev
Log:
CAY-802  Split Expressions
(first cut ... backend still ignores aliases)

Added:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/AliasPathComponent.java
Modified:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssembler.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssemblerHelper.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/AttributePathComponent.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbEntity.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/Entity.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjEntity.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/PathComponent.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/PathComponentIterator.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/RelationshipPathComponent.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/QualifiedQuery.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextJoinAliasesTest.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/MockEntity.java

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssembler.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssembler.java?rev=653135&r1=653134&r2=653135&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssembler.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssembler.java Sat May  3 13:56:25 2008
@@ -21,7 +21,9 @@
 
 import java.sql.PreparedStatement;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.cayenne.access.QueryLogger;
 import org.apache.cayenne.access.QueryTranslator;
@@ -29,6 +31,7 @@
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbRelationship;
 import org.apache.cayenne.map.JoinType;
+import org.apache.cayenne.query.QualifiedQuery;
 
 /**
  * Abstract superclass of Query translators.
@@ -53,6 +56,18 @@
     public abstract void dbRelationshipAdded(DbRelationship dbRel);
 
     /**
+     * Returns aliases for the joins defined in the query.
+     * 
+     * @since 3.0
+     */
+    protected Map<String, String> getJoinAliases() {
+        if (query instanceof QualifiedQuery) {
+            return ((QualifiedQuery) query).getJoinAliases();
+        }
+        return Collections.emptyMap();
+    }
+
+    /**
      * Appends a join with given semantics to the query.
      * 
      * @since 3.0

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssemblerHelper.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssemblerHelper.java?rev=653135&r1=653134&r2=653135&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssemblerHelper.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssemblerHelper.java Sat May  3 13:56:25 2008
@@ -84,13 +84,15 @@
         return getQueryAssembler().getRootDbEntity();
     }
 
-    /** Processes parts of the OBJ_PATH expression. */
+    /** 
+     * Processes parts of the OBJ_PATH expression. 
+     */
     protected void appendObjPath(StringBuffer buf, Expression pathExp) {
 
         ObjRelationship lastRelationship = null;
 
         for (PathComponent<ObjAttribute, ObjRelationship> component : getObjEntity()
-                .pathComponents(pathExp)) {
+                .pathComponents(pathExp, queryAssembler.getJoinAliases())) {
 
             ObjRelationship relationship = component.getRelationship();
             ObjAttribute attribute = component.getAttribute();
@@ -105,7 +107,8 @@
                 else {
                     // find and add joins ....
                     for (DbRelationship dbRel : relationship.getDbRelationships()) {
-                        queryAssembler.dbRelationshipAdded(dbRel, component.getJoinType());
+                        queryAssembler
+                                .dbRelationshipAdded(dbRel, component.getJoinType());
                     }
                 }
                 lastRelationship = relationship;
@@ -128,7 +131,7 @@
     protected void appendDbPath(StringBuffer buf, Expression pathExp) {
 
         for (PathComponent<DbAttribute, DbRelationship> component : getDbEntity()
-                .pathComponents(pathExp)) {
+                .pathComponents(pathExp, queryAssembler.getJoinAliases())) {
 
             DbRelationship relationship = component.getRelationship();
 
@@ -141,7 +144,8 @@
                 }
                 else {
                     // find and add joins ....
-                    queryAssembler.dbRelationshipAdded(relationship, component.getJoinType());
+                    queryAssembler.dbRelationshipAdded(relationship, component
+                            .getJoinType());
                 }
             }
             else {
@@ -369,13 +373,14 @@
      * primary key. If this is a "to one" relationship, column expression for the source
      * foreign key is added.
      * 
-     * @deprecated since 3.0 use {@link #processRelTermination(StringBuffer, ObjRelationship, JoinType)}.
+     * @deprecated since 3.0 use
+     *             {@link #processRelTermination(StringBuffer, ObjRelationship, JoinType)}.
      */
     protected void processRelTermination(StringBuffer buf, ObjRelationship rel) {
         processRelTermination(buf, rel, JoinType.INNER);
-       
+
     }
-    
+
     /**
      * Processes case when an OBJ_PATH expression ends with relationship. If this is a "to
      * many" relationship, a join is added and a column expression for the target entity
@@ -384,7 +389,10 @@
      * 
      * @since 3.0
      */
-    protected void processRelTermination(StringBuffer buf, ObjRelationship rel, JoinType joinType) {
+    protected void processRelTermination(
+            StringBuffer buf,
+            ObjRelationship rel,
+            JoinType joinType) {
         Iterator<DbRelationship> dbRels = rel.getDbRelationships().iterator();
 
         // scan DbRelationships
@@ -409,13 +417,13 @@
      * primary key. If this is a "to one" relationship, column expression for the source
      * foreign key is added.
      * 
-     * @deprecated since 3.0 use {@link #processRelTermination(StringBuffer, DbRelationship, JoinType)}.
+     * @deprecated since 3.0 use
+     *             {@link #processRelTermination(StringBuffer, DbRelationship, JoinType)}.
      */
     protected void processRelTermination(StringBuffer buf, DbRelationship rel) {
         processRelTermination(buf, rel, JoinType.INNER);
     }
-    
-    
+
     /**
      * Handles case when a DB_NAME expression ends with relationship. If this is a "to
      * many" relationship, a join is added and a column expression for the target entity
@@ -424,7 +432,10 @@
      * 
      * @since 3.0
      */
-    protected void processRelTermination(StringBuffer buf, DbRelationship rel, JoinType joinType) {
+    protected void processRelTermination(
+            StringBuffer buf,
+            DbRelationship rel,
+            JoinType joinType) {
         if (rel.isToMany()) {
             // append joins
             queryAssembler.dbRelationshipAdded(rel, joinType);

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java?rev=653135&r1=653134&r2=653135&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java Sat May  3 13:56:25 2008
@@ -354,11 +354,12 @@
 
                 PathComponent<DbAttribute, DbRelationship> lastComponent = null;
                 for (PathComponent<DbAttribute, DbRelationship> component : table
-                        .pathComponents(pathExp)) {
+                        .pathComponents(pathExp, getJoinAliases())) {
 
                     // do not add join for the last DB Rel
                     if (component.getRelationship() != null && !component.isLast()) {
-                        dbRelationshipAdded(component.getRelationship(), component.getJoinType());
+                        dbRelationshipAdded(component.getRelationship(), component
+                                .getJoinType());
                     }
 
                     lastComponent = component;
@@ -420,7 +421,7 @@
 
                 DbRelationship r = null;
                 for (PathComponent<DbAttribute, DbRelationship> component : table
-                        .pathComponents(dbPrefetch)) {
+                        .pathComponents(dbPrefetch, getJoinAliases())) {
                     r = component.getRelationship();
                     dbRelationshipAdded(r, JoinType.INNER);
                 }

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/AliasPathComponent.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/AliasPathComponent.java?rev=653135&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/AliasPathComponent.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/AliasPathComponent.java Sat May  3 13:56:25 2008
@@ -0,0 +1,81 @@
+/*****************************************************************
+ *   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.map;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * Represents an alias for the relationship path.
+ * 
+ * @since 3.0
+ * @author Andrus Adamchik
+ */
+class AliasPathComponent<T extends Attribute, U extends Relationship> implements
+        PathComponent<T, U> {
+
+    private Entity root;
+    private String alias;
+    private String path;
+    private boolean last;
+
+    AliasPathComponent(Entity root, String alias, String path, boolean last) {
+        this.root = root;
+        this.alias = alias;
+        this.path = path;
+        this.last = last;
+    }
+
+    public Iterable<PathComponent<T, U>> getAliasedPath() {
+        return new Iterable<PathComponent<T, U>>() {
+
+            // suppress warning until we parameterize Entity as Entity<T extends
+            // Attribute, U extends Relationship>
+            @SuppressWarnings("unchecked")
+            public Iterator iterator() {
+                return new PathComponentIterator(root, path, Collections
+                        .<String, String> emptyMap());
+            }
+        };
+    }
+
+    public T getAttribute() {
+        return null;
+    }
+
+    public JoinType getJoinType() {
+        return JoinType.UNDEFINED;
+    }
+
+    public String getName() {
+        return alias;
+    }
+
+    public U getRelationship() {
+        return null;
+    }
+
+    public boolean isAlias() {
+        return true;
+    }
+
+    public boolean isLast() {
+        return last;
+    }
+}

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/AttributePathComponent.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/AttributePathComponent.java?rev=653135&r1=653134&r2=653135&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/AttributePathComponent.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/AttributePathComponent.java Sat May  3 13:56:25 2008
@@ -50,4 +50,12 @@
     public boolean isLast() {
         return true;
     }
+    
+    public boolean isAlias() {
+        return false;
+    }
+    
+    public Iterable<PathComponent<T, U>> getAliasedPath() {
+        return null;
+    }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbEntity.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbEntity.java?rev=653135&r1=653134&r2=653135&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbEntity.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbEntity.java Sat May  3 13:56:25 2008
@@ -275,19 +275,18 @@
      * @since 3.0
      */
     @Override
+    @SuppressWarnings("unchecked")
     public Iterable<PathComponent<DbAttribute, DbRelationship>> pathComponents(
-            final Expression pathExp) {
+            final Expression pathExp,
+            final Map aliasMap) {
 
         if (pathExp.getType() == Expression.DB_PATH) {
 
             return new Iterable<PathComponent<DbAttribute, DbRelationship>>() {
 
-                // suppress warning until we parameterize Entity as Entity<T extends
-                // Attribute, U extends Relationship>
-                @SuppressWarnings("unchecked")
                 public Iterator iterator() {
                     return new PathComponentIterator(DbEntity.this, (String) pathExp
-                            .getOperand(0));
+                            .getOperand(0), aliasMap);
                 }
             };
         }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/Entity.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/Entity.java?rev=653135&r1=653134&r2=653135&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/Entity.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/Entity.java Sat May  3 13:56:25 2008
@@ -23,6 +23,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.SortedMap;
 import java.util.StringTokenizer;
 import java.util.TreeMap;
@@ -290,7 +291,9 @@
      */
     public Object lastPathComponent(Expression pathExp) {
 
-        for (PathComponent<Attribute, Relationship> component : pathComponents(pathExp)) {
+        for (PathComponent<Attribute, Relationship> component : pathComponents(
+                pathExp,
+                Collections.EMPTY_MAP)) {
             if (component.isLast()) {
                 return component.getAttribute() != null
                         ? component.getAttribute()
@@ -303,14 +306,20 @@
 
     /**
      * Processes expression argument and returns an Iterable over the path components.
-     * Note that if path is invalid and can not be resolved from this entity, this method
-     * will still return an Iterator, but an attempt to read the first invalid path
-     * component will result in ExpressionException.
+     * Path expression can use aliases. In this case an optional aliasMap parameter will
+     * be consulted to resolve them.
+     * <p>
+     * This method is lazy in a sense that if path is invalid and can not be resolved from
+     * this entity, this method will still return an Iterator, but an attempt to read the
+     * first invalid path component will result in ExpressionException.
+     * </p>
      * 
      * @since 3.0
      */
+    @SuppressWarnings("unchecked")
     public abstract <T extends Attribute, U extends Relationship> Iterable<PathComponent<T, U>> pathComponents(
-            Expression pathExp);
+            Expression pathExp,
+            Map aliasMap);
 
     /**
      * Processes expression <code>pathExp</code> and returns an Iterator of path

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjEntity.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjEntity.java?rev=653135&r1=653134&r2=653135&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjEntity.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjEntity.java Sat May  3 13:56:25 2008
@@ -835,19 +835,19 @@
      * 
      * @since 3.0
      */
+    @Override
+    @SuppressWarnings("unchecked")
     public Iterable<PathComponent<ObjAttribute, ObjRelationship>> pathComponents(
-            final Expression pathExp) {
+            final Expression pathExp,
+            final Map aliasMap) {
 
         if (pathExp.getType() == Expression.OBJ_PATH) {
 
             return new Iterable<PathComponent<ObjAttribute, ObjRelationship>>() {
 
-                // suppress warning until we parameterize Entity as Entity<T extends
-                // Attribute, U extends Relationship>
-                @SuppressWarnings("unchecked")
                 public Iterator iterator() {
                     return new PathComponentIterator(ObjEntity.this, (String) pathExp
-                            .getOperand(0));
+                            .getOperand(0), aliasMap);
                 }
             };
         }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java?rev=653135&r1=653134&r2=653135&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java Sat May  3 13:56:25 2008
@@ -524,9 +524,9 @@
             throw new CayenneRuntimeException(
                     "Can't resolve DbRelationships, null source ObjEntity");
         }
-        
+
         DbEntity dbEntity = entity.getDbEntity();
-        if(dbEntity == null) {
+        if (dbEntity == null) {
             return null;
         }
 
@@ -534,8 +534,8 @@
 
         try {
             for (PathComponent<DbAttribute, DbRelationship> pathComponent : dbEntity
-                    .pathComponents(new ASTDbPath(path))) {
-                
+                    .pathComponents(new ASTDbPath(path), Collections.emptyMap())) {
+
                 if (validPath.length() > 0) {
                     validPath.append(Entity.PATH_SEPARATOR);
                 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/PathComponent.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/PathComponent.java?rev=653135&r1=653134&r2=653135&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/PathComponent.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/PathComponent.java Sat May  3 13:56:25 2008
@@ -39,4 +39,16 @@
     JoinType getJoinType();
 
     boolean isLast();
+
+    /**
+     * Returns true if this component is an alias for a different path. Only the first
+     * path component can be an alias. Aliased path can be obtained by calling
+     * {@link #getAliasedPath()}.
+     */
+    boolean isAlias();
+
+    /**
+     * Returns an aliased path or null if this component is not an alias.
+     */
+    Iterable<PathComponent<T, U>> getAliasedPath();
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/PathComponentIterator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/PathComponentIterator.java?rev=653135&r1=653134&r2=653135&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/PathComponentIterator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/PathComponentIterator.java Sat May  3 13:56:25 2008
@@ -19,6 +19,7 @@
 package org.apache.cayenne.map;
 
 import java.util.Iterator;
+import java.util.Map;
 import java.util.StringTokenizer;
 
 import org.apache.cayenne.exp.ExpressionException;
@@ -32,11 +33,13 @@
     private StringTokenizer toks;
     private Entity currentEntity;
     private String path;
+    private Map<String, String> aliasMap;
 
-    PathComponentIterator(Entity root, String path) {
+    PathComponentIterator(Entity root, String path, Map<String, String> aliasMap) {
         currentEntity = root;
         toks = new StringTokenizer(path, Entity.PATH_SEPARATOR);
         this.path = path;
+        this.aliasMap = aliasMap;
     }
 
     public boolean hasNext() {
@@ -45,13 +48,14 @@
 
     public PathComponent<Attribute, Relationship> next() {
         String pathComp = toks.nextToken();
-        
+
         JoinType relationshipJoinType = JoinType.INNER;
-        
+
         // we only support LEFT JOINS for now...
-        if(pathComp.endsWith(Entity.OUTER_JOIN_INDICATOR)) {
+        if (pathComp.endsWith(Entity.OUTER_JOIN_INDICATOR)) {
             relationshipJoinType = JoinType.LEFT_OUTER;
-            pathComp = pathComp.substring(0, pathComp.length() - Entity.OUTER_JOIN_INDICATOR.length());
+            pathComp = pathComp.substring(0, pathComp.length()
+                    - Entity.OUTER_JOIN_INDICATOR.length());
         }
 
         // see if this is an attribute
@@ -78,6 +82,16 @@
                     !hasNext());
         }
 
+        String aliasedPath = (aliasMap != null) ? aliasMap.get(pathComp) : null;
+        if (aliasedPath != null) {
+            // TODO: andrus 5/3/2008 - reset current entity to skip the aliased path
+            return new AliasPathComponent<Attribute, Relationship>(
+                    currentEntity,
+                    pathComp,
+                    aliasedPath,
+                    !hasNext());
+        }
+
         // build error message
         StringBuilder buf = new StringBuilder();
         buf

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/RelationshipPathComponent.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/RelationshipPathComponent.java?rev=653135&r1=653134&r2=653135&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/RelationshipPathComponent.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/RelationshipPathComponent.java Sat May  3 13:56:25 2008
@@ -54,4 +54,12 @@
     public boolean isLast() {
         return last;
     }
+
+    public boolean isAlias() {
+        return false;
+    }
+
+    public Iterable<PathComponent<T, U>> getAliasedPath() {
+        return null;
+    }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/QualifiedQuery.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/QualifiedQuery.java?rev=653135&r1=653134&r2=653135&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/QualifiedQuery.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/QualifiedQuery.java Sat May  3 13:56:25 2008
@@ -19,6 +19,10 @@
 
 package org.apache.cayenne.query;
 
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
 import org.apache.cayenne.exp.Expression;
 
 /**
@@ -29,6 +33,32 @@
 public abstract class QualifiedQuery extends AbstractQuery {
 
     protected Expression qualifier;
+    protected Map<String, String> joinAliases;
+
+    /**
+     * Returns a map of join aliases.
+     * 
+     * @since 3.0
+     */
+    public Map<String, String> getJoinAliases() {
+        return joinAliases != null ? joinAliases : Collections
+                .<String, String> emptyMap();
+    }
+
+    /**
+     * Creates a named alias that would resolve into a separate chain of joins when
+     * translated to SQL. I.e. if the same path has more than one alias, the joins will be
+     * duplicated.
+     * 
+     * @since 3.0
+     */
+    public void aliasJoin(String alias, String expressionPath) {
+        if (joinAliases == null) {
+            joinAliases = new HashMap<String, String>();
+        }
+
+        joinAliases.put(alias, expressionPath);
+    }
 
     /**
      * Sets new query qualifier.

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextJoinAliasesTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextJoinAliasesTest.java?rev=653135&r1=653134&r2=653135&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextJoinAliasesTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextJoinAliasesTest.java Sat May  3 13:56:25 2008
@@ -20,7 +20,6 @@
 
 import java.util.Date;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 import org.apache.art.Artist;
@@ -53,18 +52,18 @@
         Artist picasso = DataObjectUtils.objectForPK(context, Artist.class, 1);
         Artist dali = DataObjectUtils.objectForPK(context, Artist.class, 2);
 
-//        SelectQuery query = new SelectQuery(Gallery.class);
-//        String path = "exhibitArray.artistExhibitArray";
-//        query.aliasJoin("p", path);
-//        query.aliasJoin("d", path);
-//
-//        query.andQualifier(ExpressionFactory.matchExp("p.toArtist", picasso));
-//        query.andQualifier(ExpressionFactory.matchExp("d.toArtist", dali));
-//
-//        List<Gallery> galleries = context.performQuery(query);
-//
-//        assertEquals(1, galleries.size());
-//        assertEquals("G1", galleries.get(0).getGalleryName());
+        SelectQuery query = new SelectQuery(Gallery.class);
+        String path = "exhibitArray.artistExhibitArray";
+        query.aliasJoin("p", path);
+        query.aliasJoin("d", path);
+
+        query.andQualifier(ExpressionFactory.matchExp("p.toArtist", picasso));
+        query.andQualifier(ExpressionFactory.matchExp("d.toArtist", dali));
+
+        // List<Gallery> galleries = context.performQuery(query);
+        //
+        // assertEquals(1, galleries.size());
+        // assertEquals("G1", galleries.get(0).getGalleryName());
     }
 
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/MockEntity.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/MockEntity.java?rev=653135&r1=653134&r2=653135&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/MockEntity.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/MockEntity.java Sat May  3 13:56:25 2008
@@ -20,10 +20,10 @@
 package org.apache.cayenne.map;
 
 import java.util.Iterator;
+import java.util.Map;
 
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.ExpressionException;
-import org.apache.cayenne.map.Entity;
 import org.apache.cayenne.util.XMLEncoder;
 
 /**
@@ -53,7 +53,7 @@
     
     @Override
     public <T extends Attribute, U extends Relationship> Iterable<PathComponent<T, U>> pathComponents(
-            Expression pathExp) {
+            Expression pathExp, Map joinAliases) {
         return null;
     }