1 package org.hibernate.hql.classic; 3 4 import java.util.LinkedList ; 5 import java.util.Map ; 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 27 public class PathExpressionParser implements Parser { 28 29 33 36 38 private int dotcount; 39 private String currentName; 40 private String currentProperty; 41 private String oneToOneOwnerName; 42 private AssociationType ownerAssociationType; 43 private String [] columns; 44 private String collectionName; 45 private String collectionOwnerName; 46 private String collectionRole; 47 private final StringBuffer componentPath = new StringBuffer (); 48 private Type type; 49 private final StringBuffer path = new StringBuffer (); 50 private boolean ignoreInitialJoin; 51 private boolean continuation; 52 private int joinType = JoinFragment.INNER_JOIN; private boolean useThetaStyleJoin = true; 54 private PropertyMapping currentPropertyMapping; 55 private JoinSequence joinSequence; 56 57 private boolean expectingCollectionIndex; 58 private LinkedList collectionElements = new LinkedList (); 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 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 name, AssociationType joinableType, String [] 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 continueFromManyToMany(String entityName, String [] 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 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 token, QueryTranslatorImpl q) throws QueryException { 103 104 if ( token != null ) path.append( token ); 105 106 String alias = q.getPathAlias( path.toString() ); 107 if ( alias != null ) { 108 reset( q ); currentName = alias; 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() ); } 116 catch ( MappingException me ) { 117 throw new QueryException( me ); 118 } 119 } 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 continuation = false; 142 } 143 else { 144 throw new QueryException( "unexpected" ); 145 } 146 } 147 else { 149 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 propertyName, EntityType propertyType, QueryTranslatorImpl q) 175 throws QueryException { 176 178 boolean isIdShortcut = EntityPersister.ENTITY_ID.equals( propertyName ) && 180 propertyType.isReferenceToPrimaryKey(); 181 182 final String 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 if ( componentPath.length() > 0 ) componentPath.append( '.' ); 197 componentPath.append( propertyName ); 198 } 199 else { 200 String entityClass = propertyType.getAssociatedEntityName(); 201 String 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 propertyName) { 215 if ( propertyName != null ) { 216 if ( componentPath.length() > 0 ) componentPath.append( '.' ); 217 componentPath.append( propertyName ); 218 } 219 } 220 221 private void dereferenceCollection(String propertyName, String role, QueryTranslatorImpl q) throws QueryException { 222 collectionRole = role; 223 QueryableCollection collPersister = q.getCollectionPersister( role ); 224 String name = q.createNameForCollection( role ); 225 addJoin( name, collPersister.getCollectionType() ); 226 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 getPropertyPath() { 236 if ( currentProperty == null ) { 237 return EntityPersister.ENTITY_ID; 238 } 239 else { 240 if ( componentPath.length() > 0 ) { 241 return new StringBuffer () 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 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 [] currentColumns() throws QueryException { 276 String propertyPath = getPropertyPath(); 277 String [] 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 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 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 [] indexCols = collPersister.getIndexColumnNames(); 333 if ( indexCols.length != 1 ) throw new QueryException( "composite-index appears in []: " + path ); 334 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] + " = " ); 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 alias; 362 String [] elementColumns; 363 JoinSequence joinSequence; 364 StringBuffer indexValue = new StringBuffer (); 365 } 366 367 public CollectionElement lastCollectionElement() { 368 return ( CollectionElement ) collectionElements.removeLast(); 369 } 370 371 public void setLastCollectionElementIndexValue(String 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 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 [] getWhereColumns() { 395 return columns; 396 } 397 398 public Type getWhereColumnType() { 399 return type; 400 } 401 402 public String getName() { 403 return currentName == null ? collectionName : currentName; 404 } 405 406 407 public String getCollectionSubquery(Map enabledFilters) throws QueryException { 408 return CollectionSubqueryFactory.createCollectionSubquery( joinSequence, enabledFilters, currentColumns() ); 409 } 410 411 public boolean isCollectionValued() throws QueryException { 412 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 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 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 QueryableCollection collectionPersister = q.getCollectionPersister( collectionRole ); 440 Queryable entityPersister = ( Queryable ) collectionPersister.getElementPersister(); 441 String clazz = entityPersister.getEntityName(); 442 443 final String elementName; 444 if ( collectionPersister.isOneToMany() ) { 445 elementName = collectionName; 446 q.decoratePropertyMapping( elementName, collectionPersister ); 448 } 449 else { 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 q.addFromCollection( collectionName, collectionRole, joinSequence ); 461 return collectionName; 462 } 463 464 } 465 466 String getCollectionName() { 467 return collectionName; 468 } 469 470 String getCollectionRole() { 471 return collectionRole; 472 } 473 474 String getCollectionOwnerName() { 475 return collectionOwnerName; 476 } 477 478 String getOneToOneOwnerName() { 479 return oneToOneOwnerName; 480 } 481 482 AssociationType getOwnerAssociationType() { 483 return ownerAssociationType; 484 } 485 486 String getCurrentProperty() { 487 return currentProperty; 488 } 489 490 String getCurrentName() { 491 return currentName; 492 } 493 494 public void fetch(QueryTranslatorImpl q, String 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 |