package org.hibernate.jpa.criteria.path;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Path;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.MapAttribute;
import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.SingularAttribute;
import org.hibernate.jpa.criteria.CriteriaBuilderImpl;
import org.hibernate.jpa.criteria.ParameterRegistry;
import org.hibernate.jpa.criteria.PathImplementor;
import org.hibernate.jpa.criteria.PathSource;
import org.hibernate.jpa.criteria.compile.RenderingContext;
import org.hibernate.jpa.criteria.expression.ExpressionImpl;
import org.hibernate.jpa.criteria.expression.PathTypeExpression;
public abstract class AbstractPathImpl<X>
extends ExpressionImpl<X>
implements Path<X>, PathImplementor<X>, Serializable {
private final PathSource pathSource;
private final Expression<Class<? extends X>> typeExpression;
private Map<String,Path> attributePathRegistry;
@SuppressWarnings({ "unchecked" })
public AbstractPathImpl(
CriteriaBuilderImpl criteriaBuilder,
Class<X> javaType,
PathSource pathSource) {
super( criteriaBuilder, javaType );
this.pathSource = pathSource;
this.typeExpression = new PathTypeExpression( criteriaBuilder(), getJavaType(), this );
}
public PathSource getPathSource() {
return pathSource;
}
@Override
public PathSource<?> getParentPath() {
return getPathSource();
}
@Override
@SuppressWarnings({ "unchecked" })
public Expression<Class<? extends X>> type() {
return typeExpression;
}
@Override
public String getPathIdentifier() {
return getPathSource().getPathIdentifier() + "." + getAttribute().getName();
}
protected abstract boolean canBeDereferenced();
protected final RuntimeException illegalDereference() {
return new IllegalStateException(
String.format(
"Illegal attempt to dereference path source [%s] of basic type",
getPathIdentifier()
)
);
}
protected final RuntimeException unknownAttribute(String attributeName) {
String message = "Unable to resolve attribute [" + attributeName + "] against path";
PathSource<?> source = getPathSource();
if ( source != null ) {
message += " [" + source.getPathIdentifier() + "]";
}
return new IllegalArgumentException(message);
}
protected final Path resolveCachedAttributePath(String attributeName) {
return attributePathRegistry == null
? null
: attributePathRegistry.get( attributeName );
}
protected final void registerAttributePath(String attributeName, Path path) {
if ( attributePathRegistry == null ) {
attributePathRegistry = new HashMap<String,Path>();
}
attributePathRegistry.put( attributeName, path );
}
@Override
@SuppressWarnings({ "unchecked" })
public <Y> Path<Y> get(SingularAttribute<? super X, Y> attribute) {
if ( ! canBeDereferenced() ) {
throw illegalDereference();
}
SingularAttributePath<Y> path = (SingularAttributePath<Y>) resolveCachedAttributePath( attribute.getName() );
if ( path == null ) {
path = new SingularAttributePath<Y>(
criteriaBuilder(),
attribute.getJavaType(),
getPathSourceForSubPaths(),
attribute
);
registerAttributePath( attribute.getName(), path );
}
return path;
}
protected PathSource getPathSourceForSubPaths() {
return this;
}
@Override
@SuppressWarnings({ "unchecked" })
public <E, C extends Collection<E>> Expression<C> get(PluralAttribute<X, C, E> attribute) {
if ( ! canBeDereferenced() ) {
throw illegalDereference();
}
PluralAttributePath<C> path = (PluralAttributePath<C>) resolveCachedAttributePath( attribute.getName() );
if ( path == null ) {
path = new PluralAttributePath<C>( criteriaBuilder(), this, attribute );
registerAttributePath( attribute.getName(), path );
}
return path;
}
@Override
@SuppressWarnings({ "unchecked" })
public <K, V, M extends Map<K, V>> Expression<M> get(MapAttribute<X, K, V> attribute) {
if ( ! canBeDereferenced() ) {
throw illegalDereference();
}
PluralAttributePath path = (PluralAttributePath) resolveCachedAttributePath( attribute.getName() );
if ( path == null ) {
path = new PluralAttributePath( criteriaBuilder(), this, attribute );
registerAttributePath( attribute.getName(), path );
}
return path;
}
@Override
@SuppressWarnings({ "unchecked" })
public <Y> Path<Y> get(String attributeName) {
if ( ! canBeDereferenced() ) {
throw illegalDereference();
}
final Attribute attribute = locateAttribute( attributeName );
if ( attribute.isCollection() ) {
final PluralAttribute<X,Y,?> pluralAttribute = (PluralAttribute<X,Y,?>) attribute;
if ( PluralAttribute.CollectionType.MAP.equals( pluralAttribute.getCollectionType() ) ) {
return (PluralAttributePath<Y>) this.<Object,Object,Map<Object, Object>>get( (MapAttribute) pluralAttribute );
}
else {
return (PluralAttributePath<Y>) this.get( (PluralAttribute) pluralAttribute );
}
}
else {
return get( (SingularAttribute<X,Y>) attribute );
}
}
protected final Attribute locateAttribute(String attributeName) {
final Attribute attribute = locateAttributeInternal( attributeName );
if ( attribute == null ) {
throw unknownAttribute( attributeName );
}
return attribute;
}
protected abstract Attribute locateAttributeInternal(String attributeName);
@Override
public void registerParameters(ParameterRegistry registry) {
}
@Override
public void prepareAlias(RenderingContext renderingContext) {
PathSource<?> source = getPathSource();
if ( source != null ) {
source.prepareAlias( renderingContext );
}
}
@Override
public String render(RenderingContext renderingContext) {
PathSource<?> source = getPathSource();
if ( source != null ) {
source.prepareAlias( renderingContext );
return source.getPathIdentifier() + "." + getAttribute().getName();
}
else {
return getAttribute().getName();
}
}
@Override
public String renderProjection(RenderingContext renderingContext) {
return render( renderingContext );
}
}