/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2010, Red Hat Inc. or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.hibernate.ejb.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.ejb.criteria.CriteriaBuilderImpl; import org.hibernate.ejb.criteria.CriteriaQueryCompiler; import org.hibernate.ejb.criteria.ParameterRegistry; import org.hibernate.ejb.criteria.PathImplementor; import org.hibernate.ejb.criteria.PathSource; import org.hibernate.ejb.criteria.expression.ExpressionImpl; import org.hibernate.ejb.criteria.expression.PathTypeExpression; /** * Convenience base class for various {@link Path} implementors. * * @author Steve Ebersole */ 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; /** * Constructs a basic path instance. * * @param criteriaBuilder The criteria builder * @param javaType The java type of this path * @param pathSource The source (or origin) from which this path originates */ @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; } /** * {@inheritDoc} */ public PathSource<?> getParentPath() { return getPathSource(); } /** * {@inheritDoc} */ @SuppressWarnings({ "unchecked" }) public Expression<Class<? extends X>> type() { return typeExpression; } /** * {@inheritDoc} */ public String getPathIdentifier() { return getPathSource().getPathIdentifier() + "." + getAttribute().getName(); } protected abstract boolean canBeDereferenced(); protected final RuntimeException illegalDereference() { String message = "Illegal attempt to dereference path source"; PathSource<?> source = getPathSource(); if ( source != null ) { message += " [" + getPathSource().getPathIdentifier() + "]"; } return new IllegalArgumentException(message); } 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 ); } /** * {@inheritDoc} */ @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(), this, attribute ); registerAttributePath( attribute.getName(), path ); } return path; } /** * {@inheritDoc} */ @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; } /** * {@inheritDoc} */ @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; } /** * {@inheritDoc} */ @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 ); } } /** * Get the attribute by name from the underlying model. This allows subclasses to * define exactly how the attribute is derived. * * @param attributeName The name of the attribute to locate * * @return The attribute; should never return null. * * @throws IllegalArgumentException If no such attribute exists */ protected final Attribute locateAttribute(String attributeName) { final Attribute attribute = locateAttributeInternal( attributeName ); if ( attribute == null ) { throw unknownAttribute( attributeName ); } return attribute; } /** * Get the attribute by name from the underlying model. This allows subclasses to * define exactly how the attribute is derived. Called from {@link #locateAttribute} * which also applies nullness checking for proper error reporting. * * @param attributeName The name of the attribute to locate * * @return The attribute; may be null. * * @throws IllegalArgumentException If no such attribute exists */ protected abstract Attribute locateAttributeInternal(String attributeName); /** * {@inheritDoc} */ public void registerParameters(ParameterRegistry registry) { // none to register } public void prepareAlias(CriteriaQueryCompiler.RenderingContext renderingContext) { // Make sure we delegate up to our source (eventually up to the path root) to // prepare the path properly. PathSource<?> source = getPathSource(); if ( source != null ) { source.prepareAlias( renderingContext ); } } /** * {@inheritDoc} */ public String render(CriteriaQueryCompiler.RenderingContext renderingContext) { PathSource<?> source = getPathSource(); if ( source != null ) { source.prepareAlias( renderingContext ); return source.getPathIdentifier() + "." + getAttribute().getName(); } else { return getAttribute().getName(); } } /** * {@inheritDoc} */ public String renderProjection(CriteriaQueryCompiler.RenderingContext renderingContext) { return render( renderingContext ); } }