All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.query.criteria.internal.path.AbstractFromImpl Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * 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 .
 */
package org.hibernate.query.criteria.internal.path;

import java.io.Serializable;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.persistence.criteria.CollectionJoin;
import javax.persistence.criteria.Fetch;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.MapJoin;
import javax.persistence.criteria.SetJoin;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.CollectionAttribute;
import javax.persistence.metamodel.ListAttribute;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.MapAttribute;
import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.Type;

import org.hibernate.query.criteria.internal.BasicPathUsageException;
import org.hibernate.query.criteria.internal.CollectionJoinImplementor;
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
import org.hibernate.query.criteria.internal.CriteriaSubqueryImpl;
import org.hibernate.query.criteria.internal.FromImplementor;
import org.hibernate.query.criteria.internal.JoinImplementor;
import org.hibernate.query.criteria.internal.ListJoinImplementor;
import org.hibernate.query.criteria.internal.MapJoinImplementor;
import org.hibernate.query.criteria.internal.PathSource;
import org.hibernate.query.criteria.internal.SetJoinImplementor;
import org.hibernate.query.criteria.internal.compile.RenderingContext;

/**
 * Convenience base class for various {@link javax.persistence.criteria.From} implementations.
 *
 * @author Steve Ebersole
 */
public abstract class AbstractFromImpl
		extends AbstractPathImpl
		implements From, FromImplementor, Serializable {

	public static final JoinType DEFAULT_JOIN_TYPE = JoinType.INNER;

	private Set> joins;
	private Set> fetches;

	public AbstractFromImpl(CriteriaBuilderImpl criteriaBuilder, Class javaType) {
		this( criteriaBuilder, javaType, null );
	}

	public AbstractFromImpl(CriteriaBuilderImpl criteriaBuilder, Class javaType, PathSource pathSource) {
		super( criteriaBuilder, javaType, pathSource );
	}

	@Override
	@SuppressWarnings({"unchecked"})
	public PathSource getPathSource() {
		return super.getPathSource();
	}

	@Override
	public String getPathIdentifier() {
		return getAlias();
	}

	@Override
	protected boolean canBeDereferenced() {
		return true;
	}

	@Override
	public void prepareAlias(RenderingContext renderingContext) {
		if ( getAlias() == null ) {
			if ( isCorrelated() ) {
				setAlias( getCorrelationParent().getAlias() );
			}
			else {
				setAlias( renderingContext.generateAlias() );
			}
		}
	}

	@Override
	public String renderProjection(RenderingContext renderingContext) {
		prepareAlias( renderingContext );
		return getAlias();
	}

	@Override
	public String render(RenderingContext renderingContext) {
		return renderProjection( renderingContext );
	}

	@Override
	public Attribute getAttribute() {
		return null;
	}

	public From getParent() {
		return null;
	}

	@Override
	@SuppressWarnings({"unchecked"})
	protected Attribute locateAttributeInternal(String name) {
		return (Attribute) locateManagedType().getAttribute( name );
	}

	@SuppressWarnings({"unchecked"})
	protected ManagedType locateManagedType() {
		// by default, this should be the model
		return (ManagedType) getModel();
	}


	// CORRELATION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	// IMPL NOTE : another means from handling correlations is to create a series of
	//		specialized From implementations that represent the correlation roots.  While
	//		that may be cleaner code-wise, it is certainly means creating a lot of "extra"
	//		classes since we'd need one for each Subquery#correlate method

	private FromImplementor correlationParent;

	private JoinScope joinScope = new BasicJoinScope();

	/**
	 * Helper contract used to define who/what keeps track of joins and fetches made from this FROM.
	 */
	public static interface JoinScope extends Serializable {
		public void addJoin(Join join);

		public void addFetch(Fetch fetch);
	}

	protected class BasicJoinScope implements JoinScope {
		@Override
		public void addJoin(Join join) {
			if ( joins == null ) {
				joins = new LinkedHashSet>();
			}
			joins.add( join );
		}

		@Override
		public void addFetch(Fetch fetch) {
			if ( fetches == null ) {
				fetches = new LinkedHashSet>();
			}
			fetches.add( fetch );
		}
	}

	protected class CorrelationJoinScope implements JoinScope {
		@Override
		public void addJoin(Join join) {
			if ( joins == null ) {
				joins = new LinkedHashSet>();
			}
			joins.add( join );
		}

		@Override
		public void addFetch(Fetch fetch) {
			throw new UnsupportedOperationException( "Cannot define fetch from a subquery correlation" );
		}
	}

	@Override
	public boolean isCorrelated() {
		return correlationParent != null;
	}

	@Override
	public FromImplementor getCorrelationParent() {
		if ( correlationParent == null ) {
			throw new IllegalStateException(
					String.format(
							"Criteria query From node [%s] is not part of a subquery correlation",
							getPathIdentifier()
					)
			);
		}
		return correlationParent;
	}

	@Override
	@SuppressWarnings({"unchecked"})
	public FromImplementor correlateTo(CriteriaSubqueryImpl subquery) {
		final FromImplementor correlationDelegate = createCorrelationDelegate();
		correlationDelegate.prepareCorrelationDelegate( this );
		return correlationDelegate;
	}

	protected abstract FromImplementor createCorrelationDelegate();

	@Override
	public void prepareCorrelationDelegate(FromImplementor parent) {
		this.joinScope = new CorrelationJoinScope();
		this.correlationParent = parent;
	}

	@Override
	public String getAlias() {
		return isCorrelated() ? getCorrelationParent().getAlias() : super.getAlias();
	}

	// JOINS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	protected abstract boolean canBeJoinSource();

	protected RuntimeException illegalJoin() {
		return new IllegalArgumentException(
				"Collection of values [" + getPathIdentifier() + "] cannot be source of a join"
		);
	}

	@Override
	@SuppressWarnings({"unchecked"})
	public Set> getJoins() {
		return joins == null
				? Collections.EMPTY_SET
				: joins;
	}

	@Override
	public  Join join(SingularAttribute singularAttribute) {
		return join( singularAttribute, DEFAULT_JOIN_TYPE );
	}

	@Override
	public  Join join(SingularAttribute attribute, JoinType jt) {
		if ( !canBeJoinSource() ) {
			throw illegalJoin();
		}

		Join join = constructJoin( attribute, jt );
		joinScope.addJoin( join );
		return join;
	}

	private  JoinImplementor constructJoin(SingularAttribute attribute, JoinType jt) {
		if ( Type.PersistenceType.BASIC.equals( attribute.getType().getPersistenceType() ) ) {
			throw new BasicPathUsageException( "Cannot join to attribute of basic type", attribute );
		}

		// TODO : runtime check that the attribute in fact belongs to this From's model/bindable

		if ( jt.equals( JoinType.RIGHT ) ) {
			throw new UnsupportedOperationException( "RIGHT JOIN not supported" );
		}

		final Class attributeType = attribute.getBindableJavaType();
		return new SingularAttributeJoin(
				criteriaBuilder(),
				attributeType,
				this,
				attribute,
				jt
		);
	}

	@Override
	public  CollectionJoin join(CollectionAttribute collection) {
		return join( collection, DEFAULT_JOIN_TYPE );
	}

	@Override
	public  CollectionJoin join(CollectionAttribute collection, JoinType jt) {
		if ( !canBeJoinSource() ) {
			throw illegalJoin();
		}

		final CollectionJoin join = constructJoin( collection, jt );
		joinScope.addJoin( join );
		return join;
	}

	private  CollectionJoinImplementor constructJoin(
			CollectionAttribute collection,
			JoinType jt) {
		if ( jt.equals( JoinType.RIGHT ) ) {
			throw new UnsupportedOperationException( "RIGHT JOIN not supported" );
		}

		// TODO : runtime check that the attribute in fact belongs to this From's model/bindable

		final Class attributeType = collection.getBindableJavaType();
		return new CollectionAttributeJoin(
				criteriaBuilder(),
				attributeType,
				this,
				collection,
				jt
		);
	}

	@Override
	public  SetJoin join(SetAttribute set) {
		return join( set, DEFAULT_JOIN_TYPE );
	}

	@Override
	public  SetJoin join(SetAttribute set, JoinType jt) {
		if ( !canBeJoinSource() ) {
			throw illegalJoin();
		}

		final SetJoin join = constructJoin( set, jt );
		joinScope.addJoin( join );
		return join;
	}

	private  SetJoinImplementor constructJoin(SetAttribute set, JoinType jt) {
		if ( jt.equals( JoinType.RIGHT ) ) {
			throw new UnsupportedOperationException( "RIGHT JOIN not supported" );
		}

		// TODO : runtime check that the attribute in fact belongs to this From's model/bindable

		final Class attributeType = set.getBindableJavaType();
		return new SetAttributeJoin( criteriaBuilder(), attributeType, this, set, jt );
	}

	@Override
	public  ListJoin join(ListAttribute list) {
		return join( list, DEFAULT_JOIN_TYPE );
	}

	@Override
	public  ListJoin join(ListAttribute list, JoinType jt) {
		if ( !canBeJoinSource() ) {
			throw illegalJoin();
		}

		final ListJoin join = constructJoin( list, jt );
		joinScope.addJoin( join );
		return join;
	}

	private  ListJoinImplementor constructJoin(ListAttribute list, JoinType jt) {
		if ( jt.equals( JoinType.RIGHT ) ) {
			throw new UnsupportedOperationException( "RIGHT JOIN not supported" );
		}

		// TODO : runtime check that the attribute in fact belongs to this From's model/bindable

		final Class attributeType = list.getBindableJavaType();
		return new ListAttributeJoin( criteriaBuilder(), attributeType, this, list, jt );
	}

	@Override
	public  MapJoin join(MapAttribute map) {
		return join( map, DEFAULT_JOIN_TYPE );
	}

	@Override
	public  MapJoin join(MapAttribute map, JoinType jt) {
		if ( !canBeJoinSource() ) {
			throw illegalJoin();
		}

		final MapJoin join = constructJoin( map, jt );
		joinScope.addJoin( join );
		return join;
	}

	private  MapJoinImplementor constructJoin(MapAttribute map, JoinType jt) {
		if ( jt.equals( JoinType.RIGHT ) ) {
			throw new UnsupportedOperationException( "RIGHT JOIN not supported" );
		}

		// TODO : runtime check that the attribute in fact belongs to this From's model/bindable

		final Class attributeType = map.getBindableJavaType();
		return new MapAttributeJoin( criteriaBuilder(), attributeType, this, map, jt );
	}

	@Override
	public  Join join(String attributeName) {
		return join( attributeName, DEFAULT_JOIN_TYPE );
	}

	@Override
	@SuppressWarnings({"unchecked"})
	public  Join join(String attributeName, JoinType jt) {
		if ( !canBeJoinSource() ) {
			throw illegalJoin();
		}

		if ( jt.equals( JoinType.RIGHT ) ) {
			throw new UnsupportedOperationException( "RIGHT JOIN not supported" );
		}

		final Attribute attribute = (Attribute) locateAttribute( attributeName );
		if ( attribute.isCollection() ) {
			final PluralAttribute pluralAttribute = (PluralAttribute) attribute;
			if ( PluralAttribute.CollectionType.COLLECTION.equals( pluralAttribute.getCollectionType() ) ) {
				return (Join) join( (CollectionAttribute) attribute, jt );
			}
			else if ( PluralAttribute.CollectionType.LIST.equals( pluralAttribute.getCollectionType() ) ) {
				return (Join) join( (ListAttribute) attribute, jt );
			}
			else if ( PluralAttribute.CollectionType.SET.equals( pluralAttribute.getCollectionType() ) ) {
				return (Join) join( (SetAttribute) attribute, jt );
			}
			else {
				return (Join) join( (MapAttribute) attribute, jt );
			}
		}
		else {
			return (Join) join( (SingularAttribute) attribute, jt );
		}
	}

	@Override
	public  CollectionJoin joinCollection(String attributeName) {
		return joinCollection( attributeName, DEFAULT_JOIN_TYPE );
	}

	@Override
	@SuppressWarnings({"unchecked"})
	public  CollectionJoin joinCollection(String attributeName, JoinType jt) {
		final Attribute attribute = (Attribute) locateAttribute( attributeName );
		if ( !attribute.isCollection() ) {
			throw new IllegalArgumentException( "Requested attribute was not a collection" );
		}

		final PluralAttribute pluralAttribute = (PluralAttribute) attribute;
		if ( !PluralAttribute.CollectionType.COLLECTION.equals( pluralAttribute.getCollectionType() ) ) {
			throw new IllegalArgumentException( "Requested attribute was not a collection" );
		}

		return (CollectionJoin) join( (CollectionAttribute) attribute, jt );
	}

	@Override
	public  SetJoin joinSet(String attributeName) {
		return joinSet( attributeName, DEFAULT_JOIN_TYPE );
	}

	@Override
	@SuppressWarnings({"unchecked"})
	public  SetJoin joinSet(String attributeName, JoinType jt) {
		final Attribute attribute = (Attribute) locateAttribute( attributeName );
		if ( !attribute.isCollection() ) {
			throw new IllegalArgumentException( "Requested attribute was not a set" );
		}

		final PluralAttribute pluralAttribute = (PluralAttribute) attribute;
		if ( !PluralAttribute.CollectionType.SET.equals( pluralAttribute.getCollectionType() ) ) {
			throw new IllegalArgumentException( "Requested attribute was not a set" );
		}

		return (SetJoin) join( (SetAttribute) attribute, jt );
	}

	@Override
	public  ListJoin joinList(String attributeName) {
		return joinList( attributeName, DEFAULT_JOIN_TYPE );
	}

	@Override
	@SuppressWarnings({"unchecked"})
	public  ListJoin joinList(String attributeName, JoinType jt) {
		final Attribute attribute = (Attribute) locateAttribute( attributeName );
		if ( !attribute.isCollection() ) {
			throw new IllegalArgumentException( "Requested attribute was not a list" );
		}

		final PluralAttribute pluralAttribute = (PluralAttribute) attribute;
		if ( !PluralAttribute.CollectionType.LIST.equals( pluralAttribute.getCollectionType() ) ) {
			throw new IllegalArgumentException( "Requested attribute was not a list" );
		}

		return (ListJoin) join( (ListAttribute) attribute, jt );
	}

	@Override
	public  MapJoin joinMap(String attributeName) {
		return joinMap( attributeName, DEFAULT_JOIN_TYPE );
	}

	@Override
	@SuppressWarnings({"unchecked"})
	public  MapJoin joinMap(String attributeName, JoinType jt) {
		final Attribute attribute = (Attribute) locateAttribute( attributeName );
		if ( !attribute.isCollection() ) {
			throw new IllegalArgumentException( "Requested attribute was not a map" );
		}

		final PluralAttribute pluralAttribute = (PluralAttribute) attribute;
		if ( !PluralAttribute.CollectionType.MAP.equals( pluralAttribute.getCollectionType() ) ) {
			throw new IllegalArgumentException( "Requested attribute was not a map" );
		}

		return (MapJoin) join( (MapAttribute) attribute, jt );
	}


	// FETCHES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	protected boolean canBeFetchSource() {
		// the conditions should be the same...
		return canBeJoinSource();
	}

	protected RuntimeException illegalFetch() {
		return new IllegalArgumentException(
				"Collection of values [" + getPathIdentifier() + "] cannot be source of a fetch"
		);
	}

	@Override
	@SuppressWarnings({"unchecked"})
	public Set> getFetches() {
		return fetches == null
				? Collections.EMPTY_SET
				: fetches;
	}

	@Override
	public  Fetch fetch(SingularAttribute singularAttribute) {
		return fetch( singularAttribute, DEFAULT_JOIN_TYPE );
	}

	@Override
	public  Fetch fetch(SingularAttribute attribute, JoinType jt) {
		if ( !canBeFetchSource() ) {
			throw illegalFetch();
		}

		Fetch fetch = constructJoin( attribute, jt );
		joinScope.addFetch( fetch );
		return fetch;
	}

	@Override
	public  Fetch fetch(PluralAttribute pluralAttribute) {
		return fetch( pluralAttribute, DEFAULT_JOIN_TYPE );
	}

	@Override
	public  Fetch fetch(PluralAttribute pluralAttribute, JoinType jt) {
		if ( !canBeFetchSource() ) {
			throw illegalFetch();
		}

		final Fetch fetch;
		// TODO : combine Fetch and Join hierarchies (JoinImplementor extends Join,Fetch???)
		if ( PluralAttribute.CollectionType.COLLECTION.equals( pluralAttribute.getCollectionType() ) ) {
			fetch = constructJoin( (CollectionAttribute) pluralAttribute, jt );
		}
		else if ( PluralAttribute.CollectionType.LIST.equals( pluralAttribute.getCollectionType() ) ) {
			fetch = constructJoin( (ListAttribute) pluralAttribute, jt );
		}
		else if ( PluralAttribute.CollectionType.SET.equals( pluralAttribute.getCollectionType() ) ) {
			fetch = constructJoin( (SetAttribute) pluralAttribute, jt );
		}
		else {
			fetch = constructJoin( (MapAttribute) pluralAttribute, jt );
		}
		joinScope.addFetch( fetch );
		return fetch;
	}

	@Override
	public  Fetch fetch(String attributeName) {
		return fetch( attributeName, DEFAULT_JOIN_TYPE );
	}

	@Override
	@SuppressWarnings({"unchecked"})
	public  Fetch fetch(String attributeName, JoinType jt) {
		if ( !canBeFetchSource() ) {
			throw illegalFetch();
		}

		Attribute attribute = (Attribute) locateAttribute( attributeName );
		if ( attribute.isCollection() ) {
			return (Fetch) fetch( (PluralAttribute) attribute, jt );
		}
		else {
			return (Fetch) fetch( (SingularAttribute) attribute, jt );
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy