/*
* 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 java.util.Locale;
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.SemanticException;
import org.hibernate.persister.queryable.NavigableResolutionException;
import org.hibernate.query.sqm.produce.spi.ResolutionContext;
import org.hibernate.query.sqm.tree.SqmJoinType;
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;
/**
* Template support for PathResolver implementations
*
* @author Steve Ebersole
*/
public abstract class AbstractNavigableBindingResolver implements NavigableBindingResolver {
private final ResolutionContext context;
public AbstractNavigableBindingResolver(ResolutionContext context) {
this.context = context;
}
protected ResolutionContext context() {
return context;
}
protected SqmNavigableSourceReference resolveAnyIntermediateAttributePathJoins(
SqmNavigableSourceReference sourceBinding,
String[] pathParts) {
// build joins for any intermediate path parts
for ( int i = 0, max = pathParts.length-1; i < max; i++ ) {
sourceBinding = buildIntermediateAttributeJoin( sourceBinding, pathParts[i] );
}
return sourceBinding;
}
protected SqmNavigableSourceReference buildIntermediateAttributeJoin(
SqmNavigableSourceReference sourceBinding,
String navigableName) {
final Navigable intermediateNavigable = resolveNavigable( sourceBinding, navigableName );
validateIntermediateAttributeJoin( sourceBinding, intermediateNavigable );
return (SqmNavigableSourceReference) buildAttributeJoin( sourceBinding, intermediateNavigable, null );
}
protected SqmNavigableReference buildAttributeJoin(
SqmNavigableSourceReference sourceBinding,
Navigable joinedNavigable,
EntityValuedExpressableType subclassIndicator) {
final SqmAttributeReference attributeBinding = (SqmAttributeReference) context().getParsingContext()
.findOrCreateNavigableBinding( sourceBinding, joinedNavigable );
if ( attributeBinding.getExportedFromElement() == null ) {
attributeBinding.injectExportedFromElement(
context().getFromElementBuilder().buildAttributeJoin(
attributeBinding,
null,
subclassIndicator,
getIntermediateJoinType(),
areIntermediateJoinsFetched(),
canReuseImplicitJoins()
)
);
}
return attributeBinding;
}
protected void validateIntermediateAttributeJoin(
SqmNavigableSourceReference sourceBinding,
Navigable joinedAttributeDescriptor) {
if ( !SingularPersistentAttribute.class.isInstance( joinedAttributeDescriptor ) ) {
throw new SemanticException(
String.format(
Locale.ROOT,
"Attribute [%s -> %s] is plural, cannot be used as non-terminal in path expression",
sourceBinding.asLoggableText(),
joinedAttributeDescriptor.getNavigableRole().getNavigableName()
)
);
}
else {
// make sure it is Bindable
final SingularPersistentAttribute singularAttribute = (SingularPersistentAttribute) joinedAttributeDescriptor;
if ( !canBeDereferenced( singularAttribute.getAttributeTypeClassification() ) ) {
throw new SemanticException(
String.format(
Locale.ROOT,
"SingularAttribute [%s -> %s] reports is cannot be de-referenced, therefore cannot be used as non-terminal in path expression",
sourceBinding.asLoggableText(),
joinedAttributeDescriptor.getNavigableRole().getNavigableName()
)
);
}
}
}
private boolean canBeDereferenced(SingularAttributeClassification classification) {
return classification == SingularAttributeClassification.EMBEDDED
|| classification == SingularAttributeClassification.MANY_TO_ONE
|| classification == SingularAttributeClassification.ONE_TO_ONE;
}
protected SqmJoinType getIntermediateJoinType() {
return SqmJoinType.LEFT;
}
protected boolean areIntermediateJoinsFetched() {
return false;
}
protected Navigable resolveNavigable(SqmNavigableSourceReference sourceBinding, String navigableName) {
final Navigable navigable = sourceBinding.getReferencedNavigable().findNavigable( navigableName );
if ( navigable == null ) {
throw new NavigableResolutionException(
"Could not locate navigable named [" + navigableName + "] relative to [" +
sourceBinding.getReferencedNavigable().asLoggableText() + "]"
);
}
return navigable;
}
protected void resolveAttributeJoinIfNot(SqmNavigableReference navigableBinding) {
if ( !SqmAttributeReference.class.isInstance( navigableBinding ) ) {
return;
}
SqmAttributeReference attributeBinding = (SqmAttributeReference) navigableBinding;
if ( attributeBinding.getExportedFromElement() != null ) {
return;
}
if ( !joinable( attributeBinding ) ) {
return;
}
attributeBinding.injectExportedFromElement(
context().getFromElementBuilder().buildAttributeJoin(
attributeBinding,
null,
null,
SqmJoinType.INNER,
false,
true
)
);
}
private boolean joinable(SqmAttributeReference attributeBinding) {
if ( attributeBinding.getReferencedNavigable() instanceof SingularPersistentAttribute ) {
final SingularPersistentAttribute attrRef = (SingularPersistentAttribute) attributeBinding.getReferencedNavigable();
return attrRef.getAttributeTypeClassification() != SingularAttributeClassification.BASIC
&& attrRef.getAttributeTypeClassification() != SingularAttributeClassification.ANY;
}
// Plural attributes are always joinable
return true;
}
}