KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > hql > classic > PathExpressionParser


1 //$Id: PathExpressionParser.java,v 1.11 2005/04/28 06:50:06 oneovthafew Exp $
2
package org.hibernate.hql.classic;
3
4 import java.util.LinkedList JavaDoc;
5 import java.util.Map JavaDoc;
6
7 import org.hibernate.MappingException;
8 import org.hibernate.QueryException;
9 import org.hibernate.engine.JoinSequence;
10 import org.hibernate.hql.CollectionSubqueryFactory;
11 import org.hibernate.persister.collection.CollectionPropertyMapping;
12 import org.hibernate.persister.collection.QueryableCollection;
13 import org.hibernate.persister.entity.EntityPersister;
14 import org.hibernate.persister.entity.PropertyMapping;
15 import org.hibernate.persister.entity.Queryable;
16 import org.hibernate.sql.JoinFragment;
17 import org.hibernate.type.AssociationType;
18 import org.hibernate.type.CollectionType;
19 import org.hibernate.type.EntityType;
20 import org.hibernate.type.Type;
21 import org.hibernate.type.TypeFactory;
22
23 /**
24  * Parses an expression of the form foo.bar.baz and builds up an expression
25  * involving two less table joins than there are path components.
26  */

27 public class PathExpressionParser implements Parser {
28
29     //TODO: this class does too many things! we need a different
30
//kind of path expression parser for each of the diffferent
31
//ways in which path expressions can occur
32

33     //We should actually rework this class to not implement Parser
34
//and just process path expressions in the most convenient way.
35

36     //The class is now way to complex!
37

38     private int dotcount;
39     private String JavaDoc currentName;
40     private String JavaDoc currentProperty;
41     private String JavaDoc oneToOneOwnerName;
42     private AssociationType ownerAssociationType;
43     private String JavaDoc[] columns;
44     private String JavaDoc collectionName;
45     private String JavaDoc collectionOwnerName;
46     private String JavaDoc collectionRole;
47     private final StringBuffer JavaDoc componentPath = new StringBuffer JavaDoc();
48     private Type type;
49     private final StringBuffer JavaDoc path = new StringBuffer JavaDoc();
50     private boolean ignoreInitialJoin;
51     private boolean continuation;
52     private int joinType = JoinFragment.INNER_JOIN; //default mode
53
private boolean useThetaStyleJoin = true;
54     private PropertyMapping currentPropertyMapping;
55     private JoinSequence joinSequence;
56
57     private boolean expectingCollectionIndex;
58     private LinkedList JavaDoc collectionElements = new LinkedList JavaDoc();
59
60     void setJoinType(int joinType) {
61         this.joinType = joinType;
62     }
63
64     void setUseThetaStyleJoin(boolean useThetaStyleJoin) {
65         this.useThetaStyleJoin = useThetaStyleJoin;
66     }
67
68     private void addJoin(String JavaDoc name, AssociationType joinableType) throws QueryException {
69         try {
70             joinSequence.addJoin( joinableType, name, joinType, currentColumns() );
71         }
72         catch ( MappingException me ) {
73             throw new QueryException( me );
74         }
75     }
76
77     private void addJoin(String JavaDoc name, AssociationType joinableType, String JavaDoc[] foreignKeyColumns) throws QueryException {
78         try {
79             joinSequence.addJoin( joinableType, name, joinType, foreignKeyColumns );
80         }
81         catch ( MappingException me ) {
82             throw new QueryException( me );
83         }
84     }
85
86     String JavaDoc continueFromManyToMany(String JavaDoc entityName, String JavaDoc[] joinColumns, QueryTranslatorImpl q) throws QueryException {
87         start( q );
88         continuation = true;
89         currentName = q.createNameFor( entityName );
90         q.addType( currentName, entityName );
91         Queryable classPersister = q.getEntityPersister( entityName );
92         //QueryJoinFragment join = q.createJoinFragment(useThetaStyleJoin);
93
addJoin( currentName, TypeFactory.manyToOne( entityName ), joinColumns );
94         currentPropertyMapping = classPersister;
95         return currentName;
96     }
97
98     public void ignoreInitialJoin() {
99         ignoreInitialJoin = true;
100     }
101
102     public void token(String JavaDoc token, QueryTranslatorImpl q) throws QueryException {
103
104         if ( token != null ) path.append( token );
105
106         String JavaDoc alias = q.getPathAlias( path.toString() );
107         if ( alias != null ) {
108             reset( q ); //reset the dotcount (but not the path)
109
currentName = alias; //after reset!
110
currentPropertyMapping = q.getPropertyMapping( currentName );
111             if ( !ignoreInitialJoin ) {
112                 JoinSequence ojf = q.getPathJoin( path.toString() );
113                 try {
114                     joinSequence.addCondition( ojf.toJoinFragment( q.getEnabledFilters(), true ).toWhereFragmentString() ); //after reset!
115
}
116                 catch ( MappingException me ) {
117                     throw new QueryException( me );
118                 }
119                 // we don't need to worry about any condition in the ON clause
120
// here (toFromFragmentString), since anything in the ON condition
121
// is already applied to the whole query
122
}
123         }
124         else if ( ".".equals( token ) ) {
125             dotcount++;
126         }
127         else {
128             if ( dotcount == 0 ) {
129                 if ( !continuation ) {
130                     if ( !q.isName( token ) ) throw new QueryException( "undefined alias: " + token );
131                     currentName = token;
132                     currentPropertyMapping = q.getPropertyMapping( currentName );
133                 }
134             }
135             else if ( dotcount == 1 ) {
136                 if ( currentName != null ) {
137                     currentProperty = token;
138                 }
139                 else if ( collectionName != null ) {
140                     //processCollectionProperty(token, q.getCollectionPersister(collectionRole), collectionName);
141
continuation = false;
142                 }
143                 else {
144                     throw new QueryException( "unexpected" );
145                 }
146             }
147             else { // dotcount>=2
148

149                 // Do the corresponding RHS
150
Type propertyType = getPropertyType();
151
152                 if ( propertyType == null ) {
153                     throw new QueryException( "unresolved property: " + path );
154                 }
155
156                 if ( propertyType.isComponentType() ) {
157                     dereferenceComponent( token );
158                 }
159                 else if ( propertyType.isEntityType() ) {
160                     if ( !isCollectionValued() ) dereferenceEntity( token, ( EntityType ) propertyType, q );
161                 }
162                 else if ( propertyType.isCollectionType() ) {
163                     dereferenceCollection( token, ( ( CollectionType ) propertyType ).getRole(), q );
164
165                 }
166                 else {
167                     if ( token != null ) throw new QueryException( "dereferenced: " + path );
168                 }
169
170             }
171         }
172     }
173
174     private void dereferenceEntity(String JavaDoc propertyName, EntityType propertyType, QueryTranslatorImpl q)
175             throws QueryException {
176         //NOTE: we avoid joining to the next table if the named property is just the foreign key value
177

178         //if its "id"
179
boolean isIdShortcut = EntityPersister.ENTITY_ID.equals( propertyName ) &&
180                 propertyType.isReferenceToPrimaryKey();
181
182         //or its the id property name
183
final String JavaDoc idPropertyName;
184         try {
185             idPropertyName = propertyType.getIdentifierOrUniqueKeyPropertyName( q.getFactory() );
186         }
187         catch ( MappingException me ) {
188             throw new QueryException( me );
189         }
190         boolean isNamedIdPropertyShortcut = idPropertyName != null &&
191                 idPropertyName.equals( propertyName );
192
193         if ( isIdShortcut || isNamedIdPropertyShortcut ) {
194             // special shortcut for id properties, skip the join!
195
// this must only occur at the _end_ of a path expression
196
if ( componentPath.length() > 0 ) componentPath.append( '.' );
197             componentPath.append( propertyName );
198         }
199         else {
200             String JavaDoc entityClass = propertyType.getAssociatedEntityName();
201             String JavaDoc name = q.createNameFor( entityClass );
202             q.addType( name, entityClass );
203             addJoin( name, propertyType );
204             if ( propertyType.isOneToOne() ) oneToOneOwnerName = currentName;
205             ownerAssociationType = propertyType;
206             currentName = name;
207             currentProperty = propertyName;
208             q.addPathAliasAndJoin( path.substring( 0, path.toString().lastIndexOf( '.' ) ), name, joinSequence.copy() );
209             componentPath.setLength( 0 );
210             currentPropertyMapping = q.getEntityPersister( entityClass );
211         }
212     }
213
214     private void dereferenceComponent(String JavaDoc propertyName) {
215         if ( propertyName != null ) {
216             if ( componentPath.length() > 0 ) componentPath.append( '.' );
217             componentPath.append( propertyName );
218         }
219     }
220
221     private void dereferenceCollection(String JavaDoc propertyName, String JavaDoc role, QueryTranslatorImpl q) throws QueryException {
222         collectionRole = role;
223         QueryableCollection collPersister = q.getCollectionPersister( role );
224         String JavaDoc name = q.createNameForCollection( role );
225         addJoin( name, collPersister.getCollectionType() );
226         //if ( collPersister.hasWhere() ) join.addCondition( collPersister.getSQLWhereString(name) );
227
collectionName = name;
228         collectionOwnerName = currentName;
229         currentName = name;
230         currentProperty = propertyName;
231         componentPath.setLength( 0 );
232         currentPropertyMapping = new CollectionPropertyMapping( collPersister );
233     }
234
235     private String JavaDoc getPropertyPath() {
236         if ( currentProperty == null ) {
237             return EntityPersister.ENTITY_ID;
238         }
239         else {
240             if ( componentPath.length() > 0 ) {
241                 return new StringBuffer JavaDoc()
242                         .append( currentProperty )
243                         .append( '.' )
244                         .append( componentPath.toString() )
245                         .toString();
246             }
247             else {
248                 return currentProperty;
249             }
250         }
251     }
252
253     private PropertyMapping getPropertyMapping() {
254         return currentPropertyMapping;
255     }
256
257     private void setType() throws QueryException {
258         if ( currentProperty == null ) {
259             type = getPropertyMapping().getType();
260         }
261         else {
262             type = getPropertyType();
263         }
264     }
265
266     protected Type getPropertyType() throws QueryException {
267         String JavaDoc propertyPath = getPropertyPath();
268         Type propertyType = getPropertyMapping().toType( propertyPath );
269         if ( propertyType == null ) {
270             throw new QueryException( "could not resolve property type: " + propertyPath );
271         }
272         return propertyType;
273     }
274
275     protected String JavaDoc[] currentColumns() throws QueryException {
276         String JavaDoc propertyPath = getPropertyPath();
277         String JavaDoc[] propertyColumns = getPropertyMapping().toColumns( currentName, propertyPath );
278         if ( propertyColumns == null ) {
279             throw new QueryException( "could not resolve property columns: " + propertyPath );
280         }
281         return propertyColumns;
282     }
283
284     private void reset(QueryTranslatorImpl q) {
285         //join = q.createJoinFragment(useThetaStyleJoin);
286
dotcount = 0;
287         currentName = null;
288         currentProperty = null;
289         collectionName = null;
290         collectionRole = null;
291         componentPath.setLength( 0 );
292         type = null;
293         collectionName = null;
294         columns = null;
295         expectingCollectionIndex = false;
296         continuation = false;
297         currentPropertyMapping = null;
298     }
299
300     public void start(QueryTranslatorImpl q) {
301         if ( !continuation ) {
302             reset( q );
303             path.setLength( 0 );
304             joinSequence = new JoinSequence( q.getFactory() ).setUseThetaStyle( useThetaStyleJoin );
305         }
306     }
307
308     public void end(QueryTranslatorImpl q) throws QueryException {
309         ignoreInitialJoin = false;
310
311         Type propertyType = getPropertyType();
312         if ( propertyType != null && propertyType.isCollectionType() ) {
313             collectionRole = ( ( CollectionType ) propertyType ).getRole();
314             collectionName = q.createNameForCollection( collectionRole );
315             prepareForIndex( q );
316         }
317         else {
318             columns = currentColumns();
319             setType();
320         }
321
322         //important!!
323
continuation = false;
324
325     }
326
327     private void prepareForIndex(QueryTranslatorImpl q) throws QueryException {
328
329         QueryableCollection collPersister = q.getCollectionPersister( collectionRole );
330
331         if ( !collPersister.hasIndex() ) throw new QueryException( "unindexed collection before []: " + path );
332         String JavaDoc[] indexCols = collPersister.getIndexColumnNames();
333         if ( indexCols.length != 1 ) throw new QueryException( "composite-index appears in []: " + path );
334         //String[] keyCols = collPersister.getKeyColumnNames();
335

336         JoinSequence fromJoins = new JoinSequence( q.getFactory() )
337                 .setUseThetaStyle( useThetaStyleJoin )
338                 .setRoot( collPersister, collectionName )
339                 .setNext( joinSequence.copy() );
340
341         if ( !continuation ) addJoin( collectionName, collPersister.getCollectionType() );
342
343         joinSequence.addCondition( collectionName + '.' + indexCols[0] + " = " ); //TODO: get SQL rendering out of here
344

345         CollectionElement elem = new CollectionElement();
346         elem.elementColumns = collPersister.getElementColumnNames(collectionName);
347         elem.elementType = collPersister.getElementType();
348         elem.isOneToMany = collPersister.isOneToMany();
349         elem.alias = collectionName;
350         elem.joinSequence = joinSequence;
351         collectionElements.addLast( elem );
352         setExpectingCollectionIndex();
353
354         q.addCollection( collectionName, collectionRole );
355         q.addFromJoinOnly( collectionName, fromJoins );
356     }
357
358     static final class CollectionElement {
359         Type elementType;
360         boolean isOneToMany;
361         String JavaDoc alias;
362         String JavaDoc[] elementColumns;
363         JoinSequence joinSequence;
364         StringBuffer JavaDoc indexValue = new StringBuffer JavaDoc();
365     }
366
367     public CollectionElement lastCollectionElement() {
368         return ( CollectionElement ) collectionElements.removeLast();
369     }
370
371     public void setLastCollectionElementIndexValue(String JavaDoc value) {
372         ( ( CollectionElement ) collectionElements.getLast() ).indexValue.append( value );
373     }
374
375     public boolean isExpectingCollectionIndex() {
376         return expectingCollectionIndex;
377     }
378
379     protected void setExpectingCollectionIndex() throws QueryException {
380         expectingCollectionIndex = true;
381     }
382
383     public JoinSequence getWhereJoin() {
384         return joinSequence;
385     }
386
387     public String JavaDoc getWhereColumn() throws QueryException {
388         if ( columns.length != 1 ) {
389             throw new QueryException( "path expression ends in a composite value: " + path );
390         }
391         return columns[0];
392     }
393
394     public String JavaDoc[] getWhereColumns() {
395         return columns;
396     }
397
398     public Type getWhereColumnType() {
399         return type;
400     }
401
402     public String JavaDoc getName() {
403         return currentName == null ? collectionName : currentName;
404     }
405
406
407     public String JavaDoc getCollectionSubquery(Map JavaDoc enabledFilters) throws QueryException {
408         return CollectionSubqueryFactory.createCollectionSubquery( joinSequence, enabledFilters, currentColumns() );
409     }
410
411     public boolean isCollectionValued() throws QueryException {
412         //TODO: is there a better way?
413
return collectionName != null && !getPropertyType().isCollectionType();
414     }
415
416     public void addAssociation(QueryTranslatorImpl q) throws QueryException {
417         q.addJoin( getName(), joinSequence );
418     }
419
420     public String JavaDoc addFromAssociation(QueryTranslatorImpl q) throws QueryException {
421         if ( isCollectionValued() ) {
422             return addFromCollection( q );
423         }
424         else {
425             q.addFrom( currentName, joinSequence );
426             return currentName;
427         }
428     }
429
430     public String JavaDoc addFromCollection(QueryTranslatorImpl q) throws QueryException {
431         Type collectionElementType = getPropertyType();
432
433         if ( collectionElementType == null ) {
434             throw new QueryException( "must specify 'elements' for collection valued property in from clause: " + path );
435         }
436
437         if ( collectionElementType.isEntityType() ) {
438             // an association
439
QueryableCollection collectionPersister = q.getCollectionPersister( collectionRole );
440             Queryable entityPersister = ( Queryable ) collectionPersister.getElementPersister();
441             String JavaDoc clazz = entityPersister.getEntityName();
442
443             final String JavaDoc elementName;
444             if ( collectionPersister.isOneToMany() ) {
445                 elementName = collectionName;
446                 //allow index() function:
447
q.decoratePropertyMapping( elementName, collectionPersister );
448             }
449             else { //many-to-many
450
q.addCollection( collectionName, collectionRole );
451                 elementName = q.createNameFor( clazz );
452                 addJoin( elementName, ( AssociationType ) collectionElementType );
453             }
454             q.addFrom( elementName, clazz, joinSequence );
455             currentPropertyMapping = new CollectionPropertyMapping( collectionPersister );
456             return elementName;
457         }
458         else {
459             // collections of values
460
q.addFromCollection( collectionName, collectionRole, joinSequence );
461             return collectionName;
462         }
463
464     }
465
466     String JavaDoc getCollectionName() {
467         return collectionName;
468     }
469
470     String JavaDoc getCollectionRole() {
471         return collectionRole;
472     }
473
474     String JavaDoc getCollectionOwnerName() {
475         return collectionOwnerName;
476     }
477
478     String JavaDoc getOneToOneOwnerName() {
479         return oneToOneOwnerName;
480     }
481
482     AssociationType getOwnerAssociationType() {
483         return ownerAssociationType;
484     }
485
486     String JavaDoc getCurrentProperty() {
487         return currentProperty;
488     }
489
490     String JavaDoc getCurrentName() {
491         return currentName;
492     }
493
494     public void fetch(QueryTranslatorImpl q, String JavaDoc entityName) throws QueryException {
495         if ( isCollectionValued() ) {
496             q.setCollectionToFetch( getCollectionRole(), getCollectionName(), getCollectionOwnerName(), entityName );
497         }
498         else {
499             q.addEntityToFetch( entityName, getOneToOneOwnerName(), getOwnerAssociationType() );
500         }
501     }
502 }
503
Popular Tags