KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > hql > ast > tree > FromClause


1 // $Id: FromClause.java,v 1.2 2005/07/20 19:35:21 steveebersole Exp $
2
package org.hibernate.hql.ast.tree;
3
4 import java.util.HashMap JavaDoc;
5 import java.util.HashSet JavaDoc;
6 import java.util.Iterator JavaDoc;
7 import java.util.LinkedList JavaDoc;
8 import java.util.List JavaDoc;
9 import java.util.Map JavaDoc;
10 import java.util.Set JavaDoc;
11
12 import org.hibernate.hql.antlr.HqlSqlTokenTypes;
13 import org.hibernate.hql.ast.util.ASTIterator;
14 import org.hibernate.hql.ast.util.ASTUtil;
15 import org.hibernate.QueryException;
16
17 import antlr.SemanticException;
18 import antlr.collections.AST;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22
23 /**
24  * Represents the 'FROM' part of a query or subquery, containing all mapped class references.
25  *
26  * @author josh
27  */

28 public class FromClause extends HqlSqlWalkerNode implements HqlSqlTokenTypes, DisplayableNode {
29     private static Log log = LogFactory.getLog( FromClause.class );
30     public static final int ROOT_LEVEL = 1;
31
32     private int level = ROOT_LEVEL;
33     private Set JavaDoc fromElements = new HashSet JavaDoc();
34     private Map JavaDoc fromElementByClassAlias = new HashMap JavaDoc();
35     private Map JavaDoc fromElementByTableAlias = new HashMap JavaDoc();
36     private Map JavaDoc fromElementsByPath = new HashMap JavaDoc();
37
38     /**
39      * All of the implicit FROM xxx JOIN yyy elements that are the destination of a collection. These are created from
40      * index operators on collection property references.
41      */

42     private Map JavaDoc collectionJoinFromElementsByPath = new HashMap JavaDoc();
43     /**
44      * Pointer to the parent FROM clause, if there is one.
45      */

46     private FromClause parentFromClause;
47     /**
48      * Collection of FROM clauses of which this is the parent.
49      */

50     private Set JavaDoc childFromClauses;
51     /**
52      * Counts the from elements as they are added.
53      */

54     private int fromElementCounter = 0;
55     /**
56      * Implied FROM elements to add onto the end of the FROM clause.
57      */

58     private List JavaDoc impliedElements = new LinkedList JavaDoc();
59
60     /**
61      * Adds a new from element to the from node.
62      *
63      * @param path The reference to the class.
64      * @param alias The alias AST.
65      * @return FromElement - The new FROM element.
66      */

67     public FromElement addFromElement(String JavaDoc path, AST alias) throws SemanticException {
68         // The path may be a reference to an alias defined in the parent query.
69
String JavaDoc classAlias = ( alias == null ) ? null : alias.getText();
70         checkForDuplicateClassAlias( classAlias );
71         FromElementFactory factory = new FromElementFactory( this, null, path, classAlias, null, false );
72         return factory.addFromElement();
73     }
74
75     void registerFromElement(FromElement element) {
76         fromElements.add( element );
77         String JavaDoc classAlias = element.getClassAlias();
78         if ( classAlias != null ) {
79             // The HQL class alias refers to the class name.
80
fromElementByClassAlias.put( classAlias, element );
81         }
82         // Associate the table alias with the element.
83
String JavaDoc tableAlias = element.getTableAlias();
84         if ( tableAlias != null ) {
85             fromElementByTableAlias.put( tableAlias, element );
86         }
87     }
88
89     void addDuplicateAlias(String JavaDoc alias, FromElement element) {
90         fromElementByClassAlias.put( alias, element );
91     }
92
93     private void checkForDuplicateClassAlias(String JavaDoc classAlias) throws SemanticException {
94         if ( classAlias != null && fromElementByClassAlias.containsKey( classAlias ) ) {
95             throw new SemanticException( "Duplicate definition of alias '"
96                     + classAlias + "'" );
97         }
98     }
99
100     /**
101      * Retreives the from-element represented by the given alias.
102      *
103      * @param aliasOrClassName The alias by which to locate the from-element.
104      * @return The from-element assigned the given alias, or null if none.
105      */

106     public FromElement getFromElement(String JavaDoc aliasOrClassName) {
107         FromElement fromElement = ( FromElement ) fromElementByClassAlias.get( aliasOrClassName );
108         if ( fromElement == null && parentFromClause != null ) {
109             fromElement = parentFromClause.getFromElement( aliasOrClassName );
110         }
111         return fromElement;
112     }
113
114     /**
115      * Convenience method to check whether a given token represents a from-element alias.
116      *
117      * @param possibleAlias The potential from-element alias to check.
118      * @return True if the possibleAlias is an alias to a from-element visible
119      * from this point in the query graph.
120      */

121     public boolean isFromElementAlias(String JavaDoc possibleAlias) {
122         boolean isAlias = fromElementByClassAlias.containsKey( possibleAlias );
123         if ( !isAlias && parentFromClause != null ) {
124             // try the parent FromClause...
125
isAlias = parentFromClause.isFromElementAlias( possibleAlias );
126         }
127         return isAlias;
128     }
129
130     /**
131      * Returns the list of from elements in order.
132      *
133      * @return the list of from elements (instances of FromElement).
134      */

135     public List JavaDoc getFromElements() {
136         return ASTUtil.collectChildren( this, fromElementPredicate );
137     }
138     
139     public FromElement getFromElement() {
140         // TODO: not sure about this one
141
// List fromElements = getFromElements();
142
// if ( fromElements == null || fromElements.isEmpty() ) {
143
// throw new QueryException( "Unable to locate from element" );
144
// }
145
return (FromElement) getFromElements().get(0);
146     }
147
148     /**
149      * Returns the list of from elements that will be part of the result set.
150      *
151      * @return the list of from elements that will be part of the result set.
152      */

153     public List JavaDoc getProjectionList() {
154         return ASTUtil.collectChildren( this, projectionListPredicate );
155     }
156
157     public List JavaDoc getCollectionFetches() {
158         return ASTUtil.collectChildren( this, collectionFetchPredicate );
159     }
160
161     public boolean hasCollectionFecthes() {
162         return getCollectionFetches().size() > 0;
163     }
164
165     public List JavaDoc getExplicitFromElements() {
166         return ASTUtil.collectChildren( this, explicitFromPredicate );
167     }
168
169     private static ASTUtil.FilterPredicate fromElementPredicate = new ASTUtil.IncludePredicate() {
170         public boolean include(AST node) {
171             FromElement fromElement = ( FromElement ) node;
172             return fromElement.isFromOrJoinFragment();
173         }
174     };
175
176     private static ASTUtil.FilterPredicate projectionListPredicate = new ASTUtil.IncludePredicate() {
177         public boolean include(AST node) {
178             FromElement fromElement = ( FromElement ) node;
179             return fromElement.inProjectionList();
180         }
181     };
182
183     private static ASTUtil.FilterPredicate collectionFetchPredicate = new ASTUtil.IncludePredicate() {
184         public boolean include(AST node) {
185             FromElement fromElement = ( FromElement ) node;
186             return fromElement.isFetch() && fromElement.getQueryableCollection() != null;
187         }
188     };
189
190     private static ASTUtil.FilterPredicate explicitFromPredicate = new ASTUtil.IncludePredicate() {
191         public boolean include(AST node) {
192             final FromElement fromElement = ( FromElement ) node;
193             return !fromElement.isImplied();
194         }
195     };
196
197     FromElement findCollectionJoin(String JavaDoc path) {
198         return ( FromElement ) collectionJoinFromElementsByPath.get( path );
199     }
200
201     /**
202      * Look for an existing implicit or explicit join by the
203      * given path.
204      */

205     FromElement findJoinByPath(String JavaDoc path) {
206         FromElement elem = findJoinByPathLocal( path );
207         if ( elem == null && parentFromClause != null ) {
208             elem = parentFromClause.findJoinByPath( path );
209         }
210         return elem;
211     }
212
213     FromElement findJoinByPathLocal(String JavaDoc path) {
214         Map JavaDoc joinsByPath = fromElementsByPath;
215         return ( FromElement ) joinsByPath.get( path );
216     }
217
218     void addJoinByPathMap(String JavaDoc path, FromElement destination) {
219         if ( log.isDebugEnabled() ) {
220             log.debug( "addJoinByPathMap() : " + path + " -> " + destination );
221         }
222         fromElementsByPath.put( path, destination );
223     }
224
225     /**
226      * Returns true if the from node contains the class alias name.
227      *
228      * @param alias The HQL class alias name.
229      * @return true if the from node contains the class alias name.
230      */

231     public boolean containsClassAlias(String JavaDoc alias) {
232         return fromElementByClassAlias.keySet().contains( alias );
233     }
234
235     /**
236      * Returns true if the from node contains the table alias name.
237      *
238      * @param alias The SQL table alias name.
239      * @return true if the from node contains the table alias name.
240      */

241     public boolean containsTableAlias(String JavaDoc alias) {
242         return fromElementByTableAlias.keySet().contains( alias );
243     }
244
245     public String JavaDoc getDisplayText() {
246         return "FromClause{" +
247                 "level=" + level +
248                 ", fromElementCounter=" + fromElementCounter +
249                 ", fromElements=" + fromElements.size() +
250                 ", fromElementByClassAlias=" + fromElementByClassAlias.keySet() +
251                 ", fromElementByTableAlias=" + fromElementByTableAlias.keySet() +
252                 ", fromElementsByPath=" + fromElementsByPath.keySet() +
253                 ", collectionJoinFromElementsByPath=" + collectionJoinFromElementsByPath.keySet() +
254                 ", impliedElements=" + impliedElements +
255                 "}";
256     }
257
258     public void setParentFromClause(FromClause parentFromClause) {
259         this.parentFromClause = parentFromClause;
260         if ( parentFromClause != null ) {
261             level = parentFromClause.getLevel() + 1;
262             parentFromClause.addChild( this );
263         }
264     }
265
266     private void addChild(FromClause fromClause) {
267         if ( childFromClauses == null ) {
268             childFromClauses = new HashSet JavaDoc();
269         }
270         childFromClauses.add( fromClause );
271     }
272
273     public FromClause locateChildFromClauseWithJoinByPath(String JavaDoc path) {
274         if ( childFromClauses != null && !childFromClauses.isEmpty() ) {
275             Iterator JavaDoc children = childFromClauses.iterator();
276             while ( children.hasNext() ) {
277                 FromClause child = ( FromClause ) children.next();
278                 if ( child.findJoinByPathLocal( path ) != null ) {
279                     return child;
280                 }
281             }
282         }
283         return null;
284     }
285
286     public void promoteJoin(FromElement elem) {
287         if ( log.isDebugEnabled() ) {
288             log.debug( "Promoting [" + elem + "] to [" + this + "]" );
289         }
290         //TODO: implement functionality
291
// this might be painful to do here, as the "join post processing" for
292
// the subquery has already been performed (meaning that for
293
// theta-join dialects, the join conditions have already been moved
294
// over to the where clause). A "simple" solution here might to
295
// perform "join post processing" once for the entire query (including
296
// any subqueries) at one fell swoop
297
}
298
299     public boolean isSubQuery() {
300         // TODO : this is broke for subqueries in statements other than selects...
301
return parentFromClause != null;
302     }
303
304     void addCollectionJoinFromElementByPath(String JavaDoc path, FromElement destination) {
305         if ( log.isDebugEnabled() ) {
306             log.debug( "addCollectionJoinFromElementByPath() : " + path + " -> " + destination );
307         }
308         collectionJoinFromElementsByPath.put( path, destination ); // Add the new node to the map so that we don't create it twice.
309
}
310
311     public FromClause getParentFromClause() {
312         return parentFromClause;
313     }
314
315     public int getLevel() {
316         return level;
317     }
318
319     public int nextFromElementCounter() {
320         return fromElementCounter++;
321     }
322
323     public void resolve() {
324         // Make sure that all from elements registered with this FROM clause are actually in the AST.
325
ASTIterator iter = new ASTIterator( this.getFirstChild() );
326         Set JavaDoc childrenInTree = new HashSet JavaDoc();
327         while ( iter.hasNext() ) {
328             childrenInTree.add( iter.next() );
329         }
330         for ( Iterator JavaDoc iterator = fromElements.iterator(); iterator.hasNext(); ) {
331             FromElement fromElement = ( FromElement ) iterator.next();
332             if ( !childrenInTree.contains( fromElement ) ) {
333                 throw new IllegalStateException JavaDoc( "Element not in AST: " + fromElement );
334             }
335         }
336     }
337
338     public void addImpliedFromElement(FromElement element) {
339         impliedElements.add( element );
340     }
341
342     public String JavaDoc toString() {
343         return "FromClause{" +
344                 "level=" + level +
345                 "}";
346     }
347 }
348
Popular Tags