2
* Hibernate, Relational Persistence for Idiomatic Java
4
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5
* indicated by the @author tags or express copyright attribution
6
* statements applied by the authors. All third-party contributions are
7
* distributed under license by Red Hat Middleware LLC.
9
* This copyrighted material is made available to anyone wishing to use, modify,
10
* copy, or redistribute it subject to the terms and conditions of the GNU
11
* Lesser General Public License, as published by the Free Software Foundation.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
18
* You should have received a copy of the GNU Lesser General Public License
19
* along with this distribution; if not, write to:
20
* Free Software Foundation, Inc.
21
* 51 Franklin Street, Fifth Floor
22
* Boston, MA 02110-1301 USA
25
package org.hibernate.hql;
27
import org.slf4j.Logger;
28
import org.slf4j.LoggerFactory;
29
import org.hibernate.MappingException;
30
import org.hibernate.engine.SessionFactoryImplementor;
31
import org.hibernate.hql.classic.ParserHelper;
32
import org.hibernate.util.StringHelper;
34
import java.util.ArrayList;
35
import java.util.HashSet;
39
* Provides query splitting methods, which were originally in QueryTranslator.
41
* TODO: This will need to be refactored at some point.
45
public final class QuerySplitter {
47
private static final Logger log = LoggerFactory.getLogger( QuerySplitter.class );
49
private static final Set BEFORE_CLASS_TOKENS = new HashSet();
50
private static final Set NOT_AFTER_CLASS_TOKENS = new HashSet();
53
BEFORE_CLASS_TOKENS.add( "from" );
54
BEFORE_CLASS_TOKENS.add( "delete" );
55
BEFORE_CLASS_TOKENS.add( "update" );
56
//beforeClassTokens.add("new"); DEFINITELY DON'T HAVE THIS!!
57
BEFORE_CLASS_TOKENS.add( "," );
58
NOT_AFTER_CLASS_TOKENS.add( "in" );
59
//notAfterClassTokens.add(",");
60
NOT_AFTER_CLASS_TOKENS.add( "from" );
61
NOT_AFTER_CLASS_TOKENS.add( ")" );
65
* Private empty constructor.
66
* (or else checkstyle says: 'warning: Utility classes should not have a public or default constructor.')
68
private QuerySplitter() {
72
* Handle Hibernate "implicit" polymorphism, by translating the query string into
73
* several "concrete" queries against mapped classes.
75
public static String[] concreteQueries(String query, SessionFactoryImplementor factory) throws MappingException {
77
//scan the query string for class names appearing in the from clause and replace
78
//with all persistent implementors of the class/interface, returning multiple
79
//query strings (make sure we don't pick up a class in the select clause!)
81
//TODO: this is one of the ugliest and most fragile pieces of code in Hibernate....
83
String[] tokens = StringHelper.split( StringHelper.WHITESPACE + "(),", query, true );
84
if ( tokens.length == 0 ) return new String[]{query}; // just especially for the trivial collection filter
85
ArrayList placeholders = new ArrayList();
86
ArrayList replacements = new ArrayList();
87
StringBuffer templateQuery = new StringBuffer( 40 );
92
boolean isSelectClause = false;
94
templateQuery.append( tokens[0] );
95
if ( "select".equals( tokens[0].toLowerCase() ) ) isSelectClause = true;
97
for ( int i = 1; i < tokens.length; i++ ) {
99
//update last non-whitespace token, if necessary
100
if ( !ParserHelper.isWhitespace( tokens[i - 1] ) ) last = tokens[i - 1].toLowerCase();
102
// select-range is terminated by declaration of "from"
103
if ( "from".equals( tokens[i].toLowerCase() ) ) isSelectClause = false;
105
String token = tokens[i];
106
if ( !ParserHelper.isWhitespace( token ) || last == null ) {
108
//scan for next non-whitespace token
109
if ( nextIndex <= i ) {
110
for ( nextIndex = i + 1; nextIndex < tokens.length; nextIndex++ ) {
111
next = tokens[nextIndex].toLowerCase();
112
if ( !ParserHelper.isWhitespace( next ) ) break;
116
boolean process = !isSelectClause &&
117
isJavaIdentifier( token ) &&
118
isPossiblyClassName( last, next );
121
String importedClassName = getImportedClass( token, factory );
122
if ( importedClassName != null ) {
123
String[] implementors = factory.getImplementors( importedClassName );
124
String placeholder = "$clazz" + count++ + "$";
125
if ( implementors != null ) {
126
placeholders.add( placeholder );
127
replacements.add( implementors );
129
token = placeholder; // Note this!!
135
templateQuery.append( token );
138
String[] results = StringHelper.multiply( templateQuery.toString(), placeholders.iterator(), replacements.iterator() );
139
if ( results.length == 0 ) log.warn( "no persistent classes found for query class: " + query );
143
private static boolean isPossiblyClassName(String last, String next) {
144
return "class".equals( last ) || (
145
BEFORE_CLASS_TOKENS.contains( last ) &&
146
!NOT_AFTER_CLASS_TOKENS.contains( next )
150
private static boolean isJavaIdentifier(String token) {
151
return Character.isJavaIdentifierStart( token.charAt( 0 ) );
154
public static String getImportedClass(String name, SessionFactoryImplementor factory) {
155
return factory.getImportedClassName( name );