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

org.hibernate.hql.ast.spi.SingleEntityQueryRendererDelegate Maven / Gradle / Ivy

There is a newer version: 1.5.0.Final
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * JBoss, Home of Professional Open Source
 * Copyright 2011 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.HashMap;
import java.util.List;
import java.util.Map;

import org.antlr.runtime.tree.Tree;
import org.hibernate.hql.ast.common.JoinType;
import org.hibernate.hql.ast.origin.hql.resolve.path.PropertyPath;
import org.hibernate.hql.ast.spi.predicate.ComparisonPredicate.Type;

/**
 * This extends the ANTLR generated AST walker to transform a parsed tree
 * into a query object and collect the target entity types of the query.
 * 
* TODO: *
  • It is currently human-written but should evolve into another ANTLR * generated tree walker, not extending GeneratedHQLResolver but using its * output as a generic normalization AST transformer.
  • *
  • We are assembling the query directly, but this doesn't take * into account parameter types which might need some transformation; * the Hibernate Search provided {@code QueryBuilder} could do this.
  • *
  • Implement more predicates
  • *
  • Support multiple types being targeted by the Query
  • *
  • Support positional parameters (currently only consumed named parameters)
  • * * @author Sanne Grinovero (C) 2011 Red Hat Inc. * @author Gunnar Morling */ public abstract class SingleEntityQueryRendererDelegate implements QueryRendererDelegate { protected static final String SORT_ASC_SPEC = "asc"; /** * States which this object can have during tree walking * * @author Gunnar Morling */ protected enum Status { DEFINING_SELECT, DEFINING_FROM, DEFINING_ORDER_BY } /** * The current status */ protected Status status; protected String targetTypeName; protected Class targetType; protected final SingleEntityQueryBuilder builder; protected PropertyPath propertyPath; protected final List projections = new ArrayList(); /** * Persister space: keep track of aliases and entity names. */ private final Map aliasToEntityType = new HashMap(); private final Map namedParameters; /** * How to resolve entity names to class instances */ private final EntityNamesResolver entityNames; public SingleEntityQueryRendererDelegate(EntityNamesResolver entityNames, SingleEntityQueryBuilder builder, Map namedParameters) { this.entityNames = entityNames; this.namedParameters = namedParameters != null ? namedParameters : Collections.emptyMap(); this.builder = builder; } /** * See rule entityName */ @Override public void registerPersisterSpace(Tree entityName, Tree alias) { registerAlias( entityName.getText(), alias.getText() ); setTargetTypeName( entityName.getText() ); setTargetType( entityName.getText() ); builder.setEntityType( targetTypeName ); } private void registerAlias(String entityName, String alias) { String put = aliasToEntityType.put( alias, entityName ); if ( put != null && !put.equalsIgnoreCase( entityName ) ) { throw new UnsupportedOperationException( "Alias reuse currently not supported: alias " + alias + " already assigned to type " + put ); } } private void setTargetType(String entityName) { Class targetedType = entityNames.getClassFromName( entityName ); if ( targetedType == null ) { throw new IllegalStateException( "Unknown entity name " + entityName ); } targetType = targetedType; } private void setTargetTypeName(String entityName) { if ( targetTypeName != null ) { throw new IllegalStateException( "Can't target multiple types: " + targetTypeName + " already selected before " + entityName ); } targetTypeName = entityName; } @Override public boolean isUnqualifiedPropertyReference() { return true; // TODO - very likely always true for our supported use cases } @Override public boolean isPersisterReferenceAlias() { if ( aliasToEntityType.size() == 1 ) { return true; // should be safe } else { throw new UnsupportedOperationException( "Unexpected use case: not implemented yet?" ); } } @Override public void pushFromStrategy( JoinType joinType, Tree assosiationFetchTree, Tree propertyFetchTree, Tree alias) { throw new UnsupportedOperationException( "Not yet implemented" ); } @Override public void pushSelectStrategy() { status = Status.DEFINING_SELECT; } @Override public void pushOrderByStrategy() { status = Status.DEFINING_ORDER_BY; } @Override public void popStrategy() { status = null; } @Override public void activateOR() { builder.pushOrPredicate(); } @Override public void activateAND() { builder.pushAndPredicate(); } @Override public void activateNOT() { builder.pushNotPredicate(); } @Override public void predicateLess(String comparativePredicate) { addComparisonPredicate( comparativePredicate, Type.LESS ); } @Override public void predicateLessOrEqual(String comparativePredicate) { addComparisonPredicate( comparativePredicate, Type.LESS_OR_EQUAL ); } /** * This implements the equality predicate; the comparison * predicate could be a constant, a subfunction or * some random type parameter. * The tree node has all details but with current tree rendering * it just passes it's text so we have to figure out the options again. */ @Override public void predicateEquals(final String comparativePredicate) { addComparisonPredicate( comparativePredicate, Type.EQUALS ); } @Override public void predicateNotEquals(String comparativePredicate) { builder.pushNotPredicate(); addComparisonPredicate( comparativePredicate, Type.EQUALS ); builder.popBooleanPredicate(); } @Override public void predicateGreaterOrEqual(String comparativePredicate) { addComparisonPredicate( comparativePredicate, Type.GREATER_OR_EQUAL ); } @Override public void predicateGreater(String comparativePredicate) { addComparisonPredicate( comparativePredicate, Type.GREATER ); } private void addComparisonPredicate(String comparativePredicate, Type comparisonType) { Object comparisonValue = fromNamedQuery( comparativePredicate ); builder.addComparisonPredicate( propertyPath.getNodeNamesWithoutAlias(), comparisonType, comparisonValue ); } @Override public void predicateIn(List list) { List values = fromNamedQuery( list ); builder.addInPredicate( propertyPath.getNodeNamesWithoutAlias(), values ); } @Override public void predicateBetween(String lower, String upper) { Object lowerComparisonValue = fromNamedQuery( lower ); Object upperComparisonValue = fromNamedQuery( upper ); builder.addRangePredicate( propertyPath.getNodeNamesWithoutAlias(), lowerComparisonValue, upperComparisonValue ); } @Override public void predicateLike(String patternValue, Character escapeCharacter) { Object pattern = fromNamedQuery( patternValue ); builder.addLikePredicate( propertyPath.getNodeNamesWithoutAlias(), (String) pattern, escapeCharacter ); } @Override public void predicateIsNull() { builder.addIsNullPredicate( propertyPath.getNodeNamesWithoutAlias() ); } @Override public void sortSpecification(String collateName, String orderSpec) { // orderSpec is already normalized to be lowercase and non-null addSortField( propertyPath, collateName, SORT_ASC_SPEC.equals( orderSpec ) ); } /** * Add field sort criteria. To be implemented by subclasses. * * @param propertyPath the path of the field being sorted * @param collateName optional collation name * @param isAscending sort direction */ protected abstract void addSortField(PropertyPath propertyPath, String collateName, boolean isAscending); private Object fromNamedQuery(String comparativePredicate) { if ( comparativePredicate.startsWith( ":" ) ) { return namedParameters.get( comparativePredicate.substring( 1 ) ); } else { return comparativePredicate; } } private List fromNamedQuery(List list) { List elements = new ArrayList( list.size() ); for ( String string : list ) { elements.add( fromNamedQuery( string ) ); } return elements; } @Override public void deactivateBoolean() { builder.popBooleanPredicate(); } @Override public abstract R getResult(); }