/* * Hibernate, Relational Persistence for Idiomatic Java * * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html */ package org.hibernate.query.sqm.produce.internal.hql.navigable; import org.hibernate.persister.common.spi.Navigable; import org.hibernate.persister.common.spi.SingularPersistentAttribute; import org.hibernate.persister.common.spi.SingularPersistentAttribute.SingularAttributeClassification; import org.hibernate.persister.queryable.spi.EntityValuedExpressableType; import org.hibernate.query.sqm.produce.spi.ResolutionContext; import org.hibernate.query.sqm.tree.expression.domain.SqmAttributeReference; import org.hibernate.query.sqm.tree.expression.domain.SqmNavigableReference; import org.hibernate.query.sqm.tree.expression.domain.SqmNavigableSourceReference; import org.hibernate.query.sqm.tree.from.SqmDowncast; import org.hibernate.query.sqm.tree.from.SqmFromExporter; import org.jboss.logging.Logger; /** * @author Steve Ebersole */ public class PathResolverBasicImpl extends AbstractNavigableBindingResolver { private static final Logger log = Logger.getLogger( PathResolverBasicImpl.class ); public PathResolverBasicImpl(ResolutionContext context) { super( context ); } protected boolean shouldRenderTerminalAttributeBindingAsJoin() { return false; } @Override public boolean canReuseImplicitJoins() { return true; } @Override public SqmNavigableReference resolvePath(String... pathParts) { return resolveTreatedPath( null, pathParts ); } @Override public SqmNavigableReference resolvePath(SqmNavigableSourceReference sourceBinding, String... pathParts) { return resolveTreatedPath( sourceBinding, null, pathParts ); } private String[] sansFirstElement(String[] pathParts) { assert pathParts.length > 1; final String[] result = new String[pathParts.length - 1]; System.arraycopy( pathParts, 1, result, 0, result.length ); return result; } @Override public SqmNavigableReference resolveTreatedPath(EntityValuedExpressableType subclassIndicator, String... pathParts) { assert pathParts.length > 0; // The given pathParts indicate either: // * a dot-identifier sequence whose root could either be // * an identification variable // * an attribute name exposed from a FromElement // * a single identifier which could represent: // * an identification variable // * an attribute name exposed from a FromElement if ( pathParts.length > 1 ) { // we had a dot-identifier sequence... // see if the root is an identification variable final SqmNavigableSourceReference identifiedBinding = (SqmNavigableSourceReference) context().getFromElementLocator() .findNavigableBindingByIdentificationVariable( pathParts[0] ); if ( identifiedBinding != null ) { validatePathRoot( identifiedBinding ); return resolveTreatedPath( identifiedBinding, subclassIndicator, sansFirstElement( pathParts ) ); } // otherwise see if the root might be the name of an attribute exposed from a FromElement final SqmNavigableSourceReference root = (SqmNavigableSourceReference) context().getFromElementLocator() .findNavigableBindingExposingAttribute( pathParts[0] ); if ( root != null ) { validatePathRoot( root ); return resolveTreatedPath( root, subclassIndicator, pathParts ); } } else { // we had a single identifier... // see if the identifier is an identification variable final SqmNavigableReference identifiedFromElement = context().getFromElementLocator() .findNavigableBindingByIdentificationVariable( pathParts[0] ); if ( identifiedFromElement != null ) { return resolveFromElementAliasAsTerminal( (SqmFromExporter) identifiedFromElement ); } // otherwise see if the identifier might be the name of an attribute exposed from a FromElement final SqmNavigableReference root = context().getFromElementLocator().findNavigableBindingExposingAttribute( pathParts[0] ); if ( root != null ) { // todo : consider passing along subclassIndicator return resolveTerminalAttributeBinding( (SqmNavigableSourceReference) root, pathParts[0], subclassIndicator ); } } return null; } protected void validatePathRoot(SqmNavigableReference root) { } @Override public SqmNavigableReference resolveTreatedPath( SqmNavigableSourceReference sourceBinding, EntityValuedExpressableType subclassIndicator, String... pathParts) { final SqmNavigableSourceReference intermediateJoinBindings = resolveAnyIntermediateAttributePathJoins( sourceBinding, pathParts ); return resolveTerminalAttributeBinding( intermediateJoinBindings, pathParts[pathParts.length-1] ); } protected SqmNavigableReference resolveTerminalAttributeBinding( SqmNavigableSourceReference sourceBinding, String terminalName) { return resolveTerminalAttributeBinding( sourceBinding, terminalName, null ); } protected SqmNavigableReference resolveTerminalAttributeBinding( SqmNavigableSourceReference sourceBinding, String terminalName, EntityValuedExpressableType intrinsicSubclassIndicator) { final Navigable navigable = resolveNavigable( sourceBinding, terminalName ); if ( shouldRenderTerminalAttributeBindingAsJoin() && isJoinable( navigable ) ) { log.debugf( "Resolved terminal navigable-binding [%s.%s ->%s] as navigable-join", sourceBinding.asLoggableText(), terminalName, navigable ); return buildAttributeJoin( // see note in #resolveTreatedTerminal regarding cast sourceBinding, navigable, intrinsicSubclassIndicator ); } else { log.debugf( "Resolved terminal navigable-binding [%s.%s ->%s] as navigable-reference", sourceBinding.asLoggableText(), terminalName, navigable ); // todo (6.0) : we should probably force these to forcefully resolve the join. return context().getParsingContext().findOrCreateNavigableBinding( sourceBinding, navigable ); } } private boolean isJoinable(Navigable attribute) { if ( SingularPersistentAttribute.class.isInstance( attribute ) ) { final SingularPersistentAttribute attrRef = (SingularPersistentAttribute) attribute; return attrRef.getAttributeTypeClassification() == SingularAttributeClassification.EMBEDDED || attrRef.getAttributeTypeClassification() == SingularAttributeClassification.MANY_TO_ONE || attrRef.getAttributeTypeClassification() == SingularAttributeClassification.ONE_TO_ONE; } else { // plural attributes can always be joined. return true; } } protected SqmNavigableReference resolveFromElementAliasAsTerminal(SqmFromExporter exporter) { log.debugf( "Resolved terminal as from-element alias : %s", exporter.getExportedFromElement().getIdentificationVariable() ); return exporter.getExportedFromElement().getBinding(); } protected SqmNavigableReference resolveTreatedTerminal( ResolutionContext context, SqmNavigableSourceReference lhs, String terminalName, EntityValuedExpressableType subclassIndicator) { final Navigable joinedAttribute = resolveNavigable( lhs, terminalName ); log.debugf( "Resolved terminal treated-path : %s -> %s", joinedAttribute, subclassIndicator ); final SqmAttributeReference joinBinding = (SqmAttributeReference) buildAttributeJoin( // todo : just do a cast for now, but this needs to be thought out (Binding -> SqmFrom) // ^^ SqmFrom specifically needed mainly needed for "FromElementSpace" // but perhaps that resolution could be delayed lhs, joinedAttribute, subclassIndicator ); joinBinding.addDowncast( new SqmDowncast( subclassIndicator ) ); return new TreatedNavigableReference( joinBinding, subclassIndicator ); } }