/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* JBoss, Home of Professional Open Source
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.hibernate.hql.ast.spi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import org.hibernate.hql.ast.spi.predicate.ComparisonPredicate.Type;
import org.hibernate.hql.ast.spi.predicate.ParentPredicate;
import org.hibernate.hql.ast.spi.predicate.Predicate;
import org.hibernate.hql.ast.spi.predicate.PredicateFactory;
import org.hibernate.hql.ast.spi.predicate.RootPredicate;
/**
* Builder for the creation of queries targeting a single entity, based on HQL/JPQL queries.
* <p>
* Implemented as a stack of {@link Predicate}s which allows to add elements to the constructed query in a uniform
* manner while traversing through the original HQL/JPQL query parse tree.
*
* @param <Q> the type of query created by this builder
* @author Gunnar Morling
*/
public final class SingleEntityQueryBuilder<Q> {
private final PredicateFactory<Q> predicateFactory;
private final PropertyHelper propertyHelper;
/**
* The targeted entity type of the built query.
*/
private String entityType;
/**
* The root predicate of the {@code WHERE} clause of the built query.
*/
private RootPredicate<Q> rootPredicate;
/**
* Keeps track of all the parent predicates ({@code AND}, {@code OR} etc.) of the {@code WHERE} clause of the built
* query.
*/
private final Stack<ParentPredicate<Q>> predicates = new Stack<ParentPredicate<Q>>();
private SingleEntityQueryBuilder(PredicateFactory<Q> predicateFactory, PropertyHelper propertyHelper) {
this.predicateFactory = predicateFactory;
this.propertyHelper = propertyHelper;
}
public static <Q> SingleEntityQueryBuilder<Q> getInstance(PredicateFactory<Q> predicateFactory, PropertyHelper propertyHelper) {
return new SingleEntityQueryBuilder<Q>( predicateFactory, propertyHelper );
}
public SingleEntityQueryBuilder<Q> setEntityType(String entityType) {
this.entityType = entityType;
rootPredicate = predicateFactory.getRootPredicate( entityType );
predicates.push( rootPredicate );
return this;
}
public SingleEntityQueryBuilder<Q> addComparisonPredicate(List<String> propertyPath, Type comparisonType, Object value) {
Object typedValue = propertyHelper.convertToBackendType( entityType, propertyPath, value );
pushPredicate( predicateFactory.getComparisonPredicate( entityType, comparisonType, propertyPath, typedValue ) );
return this;
}
public SingleEntityQueryBuilder<Q> addRangePredicate(String property, Object lower, Object upper) {
return addRangePredicate( Collections.singletonList( property ), lower, upper );
}
public SingleEntityQueryBuilder<Q> addRangePredicate(List<String> propertyPath, Object lower, Object upper) {
Object lowerValue = propertyHelper.convertToBackendType( entityType, propertyPath, lower );
Object upperValue = propertyHelper.convertToBackendType( entityType, propertyPath, upper );
pushPredicate( predicateFactory.getRangePredicate( entityType, propertyPath, lowerValue, upperValue ) );
return this;
}
public SingleEntityQueryBuilder<Q> addInPredicate(List<String> propertyPath, List<Object> elements) {
List<Object> typedElements = new ArrayList<Object>( elements.size() );
for ( Object element : elements ) {
typedElements.add( propertyHelper.convertToBackendType( entityType, propertyPath, element ) );
}
pushPredicate( predicateFactory.getInPredicate( entityType, propertyPath, typedElements ) );
return this;
}
public SingleEntityQueryBuilder<Q> addLikePredicate(List<String> propertyPath, String patternValue, Character escapeCharacter) {
pushPredicate( predicateFactory.getLikePredicate( entityType, propertyPath, patternValue, escapeCharacter ) );
return this;
}
public SingleEntityQueryBuilder<Q> addIsNullPredicate(List<String> propertyPath) {
pushPredicate( predicateFactory.getIsNullPredicate( entityType, propertyPath ) );
return this;
}
public SingleEntityQueryBuilder<Q> pushAndPredicate() {
pushPredicate( predicateFactory.getConjunctionPredicate() );
return this;
}
public SingleEntityQueryBuilder<Q> pushOrPredicate() {
pushPredicate( predicateFactory.getDisjunctionPredicate() );
return this;
}
public SingleEntityQueryBuilder<Q> pushNotPredicate() {
pushPredicate( predicateFactory.getNegationPredicate() );
return this;
}
private void pushPredicate(Predicate<Q> predicate) {
// Add as sub-predicate to the current top predicate
predicates.peek().add( predicate );
// push to parent predicate stack if required
if ( predicate.getType().isParent() ) {
@SuppressWarnings("unchecked")
ParentPredicate<Q> parentPredicate = predicate.as( ParentPredicate.class );
predicates.push( parentPredicate );
}
}
public SingleEntityQueryBuilder<Q> popBooleanPredicate() {
predicates.pop();
return this;
}
/**
* Returns the query created by this builder.
*
* @return the query created by this builder
*/
public Q build() {
return rootPredicate.getQuery();
}
@Override
public String toString() {
return "SingleEntityQueryBuilder [entityType=" + entityType + ", rootPredicate=" + rootPredicate + "]";
}
}