1 package org.hibernate.hql.ast.tree; 3 4 import org.hibernate.QueryException; 5 import org.hibernate.engine.JoinSequence; 6 import org.hibernate.hql.CollectionProperties; 7 import org.hibernate.hql.antlr.SqlTokenTypes; 8 import org.hibernate.hql.ast.util.ASTPrinter; 9 import org.hibernate.hql.ast.util.ASTUtil; 10 import org.hibernate.hql.ast.util.ColumnHelper; 11 import org.hibernate.persister.collection.QueryableCollection; 12 import org.hibernate.persister.entity.EntityPersister; 13 import org.hibernate.sql.JoinFragment; 14 import org.hibernate.type.CollectionType; 15 import org.hibernate.type.EntityType; 16 import org.hibernate.type.Type; 17 import org.hibernate.util.StringHelper; 18 19 import antlr.SemanticException; 20 import antlr.collections.AST; 21 22 import org.apache.commons.logging.Log; 23 import org.apache.commons.logging.LogFactory; 24 25 33 public class DotNode extends FromReferenceNode implements DisplayableNode, SelectExpression { 34 35 38 private static final Log log = LogFactory.getLog( DotNode.class ); 39 40 private static final int DEREF_UNKNOWN = 0; 41 private static final int DEREF_ENTITY = 1; 42 private static final int DEREF_COMPONENT = 2; 43 private static final int DEREF_COLLECTION = 3; 44 private static final int DEREF_PRIMITIVE = 4; 45 private static final int DEREF_IDENTIFIER = 5; 46 private static final int DEREF_JAVA_CONSTANT = 6; 47 48 51 private String propertyName; 52 55 private String path; 56 59 private String propertyPath; 60 61 64 private String [] columns; 65 66 69 private int joinType = JoinFragment.INNER_JOIN; 70 71 74 private boolean fetch = false; 75 76 79 private int dereferenceType = DEREF_UNKNOWN; 80 81 private FromElement impliedJoin; 82 83 89 public void setJoinType(int joinType) { 90 this.joinType = joinType; 91 } 92 93 private String [] getColumns() throws QueryException { 94 if ( columns == null ) { 95 String tableAlias = getLhs().getFromElement().getTableAlias(); 97 columns = getFromElement().toColumns( tableAlias, propertyPath, false ); 98 } 99 return columns; 100 } 101 102 public String getDisplayText() { 103 StringBuffer buf = new StringBuffer (); 104 FromElement fromElement = getFromElement(); 105 buf.append( "{propertyName=" ).append( propertyName ); 106 buf.append( ",dereferenceType=" ).append( ASTPrinter.getConstantName( getClass(), dereferenceType ) ); 107 buf.append( ",propertyPath=" ).append( propertyPath ); 108 buf.append( ",path=" ).append( getPath() ); 109 if ( fromElement != null ) { 110 buf.append( ",tableAlias=" ).append( fromElement.getTableAlias() ); 111 buf.append( ",className=" ).append( fromElement.getClassName() ); 112 buf.append( ",classAlias=" ).append( fromElement.getClassAlias() ); 113 } 114 else { 115 buf.append( ",no from element" ); 116 } 117 buf.append( '}' ); 118 return buf.toString(); 119 } 120 121 126 public void resolveFirstChild() throws SemanticException { 127 FromReferenceNode lhs = ( FromReferenceNode ) getFirstChild(); 128 SqlNode property = ( SqlNode ) lhs.getNextSibling(); 129 130 String propName = property.getText(); 132 propertyName = propName; 133 if ( propertyPath == null ) { 135 propertyPath = propName; 136 } 137 lhs.resolve( true, true, null, this ); 140 setFromElement( lhs.getFromElement() ); } 142 143 public void resolveInFunctionCall(boolean generateJoin, boolean implicitJoin) throws SemanticException { 144 if ( isResolved() ) { 145 return; 146 } 147 Type propertyType = prepareLhs(); if ( propertyType!=null && propertyType.isCollectionType() ) { 149 resolveIndex(null); 150 } 151 else { 152 resolveFirstChild(); 153 super.resolve(generateJoin, implicitJoin); 154 } 155 } 156 157 158 public void resolveIndex(AST parent) throws SemanticException { 159 if ( isResolved() ) { 160 return; 161 } 162 Type propertyType = prepareLhs(); dereferenceCollection( ( CollectionType ) propertyType, true, true, null, parent ); 164 } 165 166 public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent) 167 throws SemanticException { 168 if ( isResolved() ) { 170 return; 171 } 172 Type propertyType = prepareLhs(); 174 if ( propertyType == null ) { 177 if ( parent == null ) { 178 getWalker().getLiteralProcessor().lookupConstant( this ); 179 } 180 return; 183 } 184 if ( propertyType.isComponentType() ) { 186 dereferenceComponent( parent ); 187 initText(); 188 } 189 else if ( propertyType.isEntityType() ) { 191 dereferenceEntity( ( EntityType ) propertyType, implicitJoin, classAlias, generateJoin, parent ); 192 initText(); 193 } 194 else if ( propertyType.isCollectionType() ) { 196 dereferenceCollection( ( CollectionType ) propertyType, implicitJoin, false, classAlias, parent ); 197 } 198 else { dereferenceType = DEREF_PRIMITIVE; 200 initText(); 201 } 202 setResolved(); 203 } 204 205 private void initText() { 206 String [] cols = getColumns(); 207 String text = StringHelper.join( ", ", cols ); 208 if ( cols.length > 1 && getWalker().isComparativeExpressionClause() ) { 209 text = "(" + text + ")"; 210 } 211 setText( text ); 212 } 213 214 private Type prepareLhs() throws SemanticException { 215 FromReferenceNode lhs = getLhs(); 216 lhs.prepareForDot( propertyName ); 217 Type propertyType = getDataType(); 218 return propertyType; 219 } 220 221 private void dereferenceCollection(CollectionType collectionType, boolean implicitJoin, boolean indexed, String classAlias, AST parent) 222 throws SemanticException { 223 224 dereferenceType = DEREF_COLLECTION; 225 String role = collectionType.getRole(); 226 227 boolean isSizeProperty = getNextSibling()!=null && 229 CollectionProperties.isAnyCollectionProperty( getNextSibling().getText() ); 230 231 if ( isSizeProperty ) indexed = true; 233 QueryableCollection queryableCollection = getSessionFactoryHelper().requireQueryableCollection( role ); 234 String propName = getPath(); 235 FromClause currentFromClause = getWalker().getCurrentFromClause(); 236 237 FromElementFactory factory = new FromElementFactory( 240 currentFromClause, 241 getLhs().getFromElement(), 242 propName, 243 classAlias, 244 getColumns(), 245 implicitJoin 246 ); 247 FromElement elem = factory.createCollection( queryableCollection, role, joinType, fetch, indexed ); 248 249 if ( log.isDebugEnabled() ) { 250 log.debug( "dereferenceCollection() : Created new FROM element for " + propName + " : " + elem ); 251 } 252 253 setImpliedJoin( elem ); 254 setFromElement( elem ); 256 if ( isSizeProperty ) { 257 elem.setText(""); 258 elem.setUseWhereFragment(false); 259 } 260 261 if ( !implicitJoin ) { 262 EntityPersister entityPersister = elem.getEntityPersister(); 263 if ( entityPersister != null ) { 264 getWalker().addQuerySpaces( entityPersister.getQuerySpaces() ); 265 } 266 } 267 getWalker().addQuerySpaces( queryableCollection.getCollectionSpaces() ); } 269 270 private void dereferenceEntity(EntityType entityType, boolean implicitJoin, String classAlias, boolean generateJoin, AST parent) throws SemanticException { 271 checkForCorrelatedSubquery( "dereferenceEntity" ); 272 if ( unresolvedComponent( generateJoin ) ) { 274 if ( log.isDebugEnabled() ) { 275 log.debug( "dereferenceEntity() : resolving unresolved component '" + propertyPath + "' ... " ); 276 } 277 dereferenceEntityJoin( classAlias, entityType, implicitJoin, parent ); 278 return; 279 } 280 281 DotNode parentAsDotNode = null; 291 String property = propertyName; 292 boolean joinIsNeeded = false; 293 294 if ( isDotNode( parent ) ) { 295 parentAsDotNode = ( DotNode ) parent; 296 property = parentAsDotNode.propertyName; 297 joinIsNeeded = generateJoin && !isReferenceToPrimaryKey( parentAsDotNode.propertyName, entityType ); 298 } 299 else { 300 joinIsNeeded = generateJoin && ( !getWalker().isInSelect() || !getWalker().isShallowQuery() ); 301 } 302 303 if ( joinIsNeeded ) { 304 dereferenceEntityJoin( classAlias, entityType, implicitJoin, parent ); 305 } 306 else { 307 dereferenceEntityIdentifier( property, parentAsDotNode ); 308 } 309 310 } 311 312 private boolean unresolvedComponent(boolean generateJoin) { 313 AST c = getFirstChild(); 314 if ( generateJoin && isDotNode( c ) ) { 315 DotNode dot = ( DotNode ) c; 316 if ( dot.dereferenceType == DEREF_COMPONENT || dot.dereferenceType == DEREF_IDENTIFIER ) { 317 if ( StringHelper.isNotEmpty( propertyPath ) ) { 318 return true; 319 } 320 } 321 } 322 return false; 323 } 324 325 private boolean isDotNode(AST n) { 326 return n != null && n.getType() == SqlTokenTypes.DOT; 327 } 328 329 private void dereferenceEntityJoin(String classAlias, EntityType propertyType, boolean impliedJoin, AST parent) 330 throws SemanticException { 331 dereferenceType = DEREF_ENTITY; 332 if ( log.isDebugEnabled() ) { 333 log.debug( "dereferenceEntityJoin() : generating join for " + propertyName + " in " 334 + getFromElement().getClassName() + " " 335 + ( ( classAlias == null ) ? "{no alias}" : "(" + classAlias + ")" ) 336 + " parent = " + ASTUtil.getDebugString( parent ) 337 ); 338 } 339 String associatedEntityName = propertyType.getAssociatedEntityName(); 341 String tableAlias = getAliasGenerator().createName( associatedEntityName ); 342 343 String [] joinColumns = getColumns(); 344 String joinPath = getPath(); 345 346 if ( impliedJoin && getWalker().isInFrom() ) { 347 int impliedJoinType = getWalker().getImpliedJoinType(); 348 joinType = impliedJoinType; 349 } 350 351 FromClause currentFromClause = getWalker().getCurrentFromClause(); 352 FromElement elem = null; 353 elem = currentFromClause.findJoinByPath( joinPath ); 354 355 382 if ( elem == null ) { 383 JoinSequence joinSequence = getSessionFactoryHelper() 386 .createJoinSequence( impliedJoin, propertyType, tableAlias, joinType, joinColumns ); 387 388 FromElementFactory factory = new FromElementFactory( 389 currentFromClause, 390 getLhs().getFromElement(), 391 joinPath, 392 classAlias, 393 joinColumns, 394 impliedJoin 395 ); 396 elem = factory.createEntityJoin( 397 associatedEntityName, 398 tableAlias, 399 joinSequence, 400 fetch, 401 getWalker().isInFrom(), 402 propertyType 403 ); 404 } 405 else { 406 currentFromClause.addDuplicateAlias(classAlias, elem); 407 } 408 setImpliedJoin( elem ); 409 getWalker().addQuerySpaces( elem.getEntityPersister().getQuerySpaces() ); 410 setFromElement( elem ); } 412 413 private void setImpliedJoin(FromElement elem) { 414 this.impliedJoin = elem; 415 if ( getFirstChild().getType() == SqlTokenTypes.DOT ) { 416 DotNode dotLhs = ( DotNode ) getFirstChild(); 417 if ( dotLhs.getImpliedJoin() != null ) { 418 this.impliedJoin = dotLhs.getImpliedJoin(); 419 } 420 } 421 } 422 423 public FromElement getImpliedJoin() { 424 return impliedJoin; 425 } 426 427 private boolean isReferenceToPrimaryKey(String propertyName, EntityType propertyType) { 428 if ( EntityPersister.ENTITY_ID.equals( propertyName ) ) { 429 return propertyType.isReferenceToPrimaryKey(); 431 } 432 else { 433 String keyPropertyName = getSessionFactoryHelper() 434 .getIdentifierOrUniqueKeyPropertyName( propertyType ); 435 return keyPropertyName != null && keyPropertyName.equals( propertyName ); 436 } 437 } 438 439 453 private void checkForCorrelatedSubquery(String methodName) { 454 if ( isCorrelatedSubselect() ) { 455 if ( log.isDebugEnabled() ) { 456 log.debug( methodName + "() : correlated subquery" ); 457 } 458 } 459 } 460 461 private boolean isCorrelatedSubselect() { 462 return getWalker().isSubQuery() && 463 getFromElement().getFromClause() != getWalker().getCurrentFromClause(); 464 } 465 466 private void dereferenceComponent(AST parent) { 467 dereferenceType = DEREF_COMPONENT; 468 setPropertyNameAndPath( parent ); 469 } 470 471 private void dereferenceEntityIdentifier(String propertyName, DotNode dotParent) { 472 if ( log.isDebugEnabled() ) { 475 log.debug( "dereferenceShortcut() : property " + 476 propertyName + " in " + getFromElement().getClassName() + 477 " does not require a join." ); 478 } 479 480 initText(); 481 setPropertyNameAndPath( dotParent ); if ( dotParent != null ) { 484 dotParent.dereferenceType = DEREF_IDENTIFIER; 485 dotParent.setText( getText() ); 486 dotParent.columns = getColumns(); 487 } 488 } 489 490 private void setPropertyNameAndPath(AST parent) { 491 if ( isDotNode( parent ) ) { 492 DotNode dotNode = ( DotNode ) parent; 493 AST lhs = dotNode.getFirstChild(); 494 AST rhs = lhs.getNextSibling(); 495 propertyName = rhs.getText(); 496 propertyPath = propertyPath + "." + propertyName; dotNode.propertyPath = propertyPath; 498 if ( log.isDebugEnabled() ) { 499 log.debug( "Unresolved property path is now '" + dotNode.propertyPath + "'" ); 500 } 501 } 502 else { 503 AST lhs = getFirstChild(); 505 AST rhs = lhs.getNextSibling(); 506 propertyPath = rhs.getText(); 507 } 508 } 509 510 public Type getDataType() { 511 if ( super.getDataType() == null ) { 512 FromElement fromElement = getLhs().getFromElement(); 513 if ( fromElement == null ) { 514 return null; 515 } 516 Type propertyType = fromElement.getPropertyType( propertyName, propertyPath ); 518 if ( log.isDebugEnabled() ) { 519 log.debug( "getDataType() : " + propertyPath + " -> " + propertyType ); 520 } 521 super.setDataType( propertyType ); 522 } 523 return super.getDataType(); 524 } 525 526 public void setPropertyPath(String propertyPath) { 527 this.propertyPath = propertyPath; 528 } 529 530 public String getPropertyPath() { 531 return propertyPath; 532 } 533 534 public FromReferenceNode getLhs() { 535 FromReferenceNode lhs = ( ( FromReferenceNode ) getFirstChild() ); 536 if ( lhs == null ) { 537 throw new IllegalStateException ( "DOT node with no left-hand-side!" ); 538 } 539 return lhs; 540 } 541 542 547 public String getPath() { 548 if ( path == null ) { 549 FromReferenceNode lhs = getLhs(); 550 if ( lhs == null ) { 551 path = getText(); 552 } 553 else { 554 SqlNode rhs = ( SqlNode ) lhs.getNextSibling(); 555 path = lhs.getPath() + "." + rhs.getOriginalText(); 556 } 557 } 558 return path; 559 } 560 561 public void setFetch(boolean fetch) { 562 this.fetch = fetch; 563 } 564 565 public void setScalarColumnText(int i) throws SemanticException { 566 String [] sqlColumns = getColumns(); 567 ColumnHelper.generateScalarColumns( this, sqlColumns, i ); 568 } 569 570 575 public void resolveSelectExpression() throws SemanticException { 576 if ( getWalker().isShallowQuery() || getWalker().getCurrentFromClause().isSubQuery() ) { 577 resolve(false, true); 578 } 579 else { 580 resolve(true, false); 581 Type type = getDataType(); 582 if ( type.isEntityType() ) { 583 FromElement fromElement = getFromElement(); 584 fromElement.setIncludeSubclasses( true ); if ( useThetaStyleImplicitJoins ) { 586 fromElement.getJoinSequence().setUseThetaStyle( true ); FromElement origin = fromElement.getOrigin(); 589 if ( origin != null ) { 590 ASTUtil.makeSiblingOfParent( origin, fromElement ); 591 } 592 } 593 } 594 } 595 } 596 597 600 public static boolean useThetaStyleImplicitJoins = false; 601 602 public void setResolvedConstant(String text) { 603 path = text; 604 dereferenceType = DEREF_JAVA_CONSTANT; 605 setResolved(); } 607 } 608 | Popular Tags |