/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC. * * 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.sql; import java.util.HashSet; import java.util.Iterator; import org.hibernate.dialect.Dialect; /** * A translated HQL query * @author Gavin King */ public class QuerySelect { private Dialect dialect; private JoinFragment joins; private StringBuffer select = new StringBuffer(); private StringBuffer where = new StringBuffer(); private StringBuffer groupBy = new StringBuffer(); private StringBuffer orderBy = new StringBuffer(); private StringBuffer having = new StringBuffer(); private String comment; private boolean distinct=false; private static final HashSet DONT_SPACE_TOKENS = new HashSet(); static { //dontSpace.add("'"); DONT_SPACE_TOKENS.add("."); DONT_SPACE_TOKENS.add("+"); DONT_SPACE_TOKENS.add("-"); DONT_SPACE_TOKENS.add("/"); DONT_SPACE_TOKENS.add("*"); DONT_SPACE_TOKENS.add("<"); DONT_SPACE_TOKENS.add(">"); DONT_SPACE_TOKENS.add("="); DONT_SPACE_TOKENS.add("#"); DONT_SPACE_TOKENS.add("~"); DONT_SPACE_TOKENS.add("|"); DONT_SPACE_TOKENS.add("&"); DONT_SPACE_TOKENS.add("<="); DONT_SPACE_TOKENS.add(">="); DONT_SPACE_TOKENS.add("=>"); DONT_SPACE_TOKENS.add("=<"); DONT_SPACE_TOKENS.add("!="); DONT_SPACE_TOKENS.add("<>"); DONT_SPACE_TOKENS.add("!#"); DONT_SPACE_TOKENS.add("!~"); DONT_SPACE_TOKENS.add("!<"); DONT_SPACE_TOKENS.add("!>"); DONT_SPACE_TOKENS.add("("); //for MySQL DONT_SPACE_TOKENS.add(")"); } public QuerySelect(Dialect dialect) { this.dialect = dialect; joins = new QueryJoinFragment(dialect, false); } public JoinFragment getJoinFragment() { return joins; } public void addSelectFragmentString(String fragment) { if ( fragment.length()>0 && fragment.charAt(0)==',' ) fragment = fragment.substring(1); fragment = fragment.trim(); if ( fragment.length()>0 ) { if ( select.length()>0 ) select.append(", "); select.append(fragment); } } public void addSelectColumn(String columnName, String alias) { addSelectFragmentString(columnName + ' ' + alias); } public void setDistinct(boolean distinct) { this.distinct = distinct; } public void setWhereTokens(Iterator tokens) { //if ( conjunctiveWhere.length()>0 ) conjunctiveWhere.append(" and "); appendTokens(where, tokens); } public void prependWhereConditions(String conditions) { if (where.length() > 0) { where.insert(0, conditions + " and "); } else { where.append(conditions); } } public void setGroupByTokens(Iterator tokens) { //if ( groupBy.length()>0 ) groupBy.append(" and "); appendTokens(groupBy, tokens); } public void setOrderByTokens(Iterator tokens) { //if ( orderBy.length()>0 ) orderBy.append(" and "); appendTokens(orderBy, tokens); } public void setHavingTokens(Iterator tokens) { //if ( having.length()>0 ) having.append(" and "); appendTokens(having, tokens); } public void addOrderBy(String orderByString) { if ( orderBy.length() > 0 ) orderBy.append(", "); orderBy.append(orderByString); } public String toQueryString() { StringBuffer buf = new StringBuffer(50); if (comment!=null) buf.append("/* ").append(comment).append(" */ "); buf.append("select "); if (distinct) buf.append("distinct "); String from = joins.toFromFragmentString(); if ( from.startsWith(",") ) { from = from.substring(1); } else if ( from.startsWith(" inner join") ){ from = from.substring(11); } buf.append( select.toString() ) .append(" from") .append(from); String outerJoinsAfterWhere = joins.toWhereFragmentString().trim(); String whereConditions = where.toString().trim(); boolean hasOuterJoinsAfterWhere = outerJoinsAfterWhere.length() > 0; boolean hasWhereConditions = whereConditions.length() > 0; if (hasOuterJoinsAfterWhere || hasWhereConditions) { buf.append(" where "); if (hasOuterJoinsAfterWhere) { buf.append( outerJoinsAfterWhere.substring(4) ); } if (hasWhereConditions) { if (hasOuterJoinsAfterWhere) { buf.append(" and ("); } buf.append(whereConditions); if (hasOuterJoinsAfterWhere) { buf.append(")"); } } } if ( groupBy.length() > 0 ) buf.append(" group by ").append( groupBy.toString() ); if ( having.length() > 0 ) buf.append(" having ").append( having.toString() ); if ( orderBy.length() > 0 ) buf.append(" order by ").append( orderBy.toString() ); return dialect.transformSelectString( buf.toString() ); } private static void appendTokens(StringBuffer buf, Iterator iter) { boolean lastSpaceable=true; boolean lastQuoted=false; while ( iter.hasNext() ) { String token = (String) iter.next(); boolean spaceable = !DONT_SPACE_TOKENS.contains(token); boolean quoted = token.startsWith("'"); if (spaceable && lastSpaceable) { if ( !quoted || !lastQuoted ) buf.append(' '); } lastSpaceable = spaceable; buf.append(token); lastQuoted = token.endsWith("'"); } } public void setComment(String comment) { this.comment = comment; } public QuerySelect copy() { QuerySelect copy = new QuerySelect(dialect); copy.joins = this.joins.copy(); copy.select.append( this.select.toString() ); copy.where.append( this.where.toString() ); copy.groupBy.append( this.groupBy.toString() ); copy.orderBy.append( this.orderBy.toString() ); copy.having.append( this.having.toString() ); copy.comment = this.comment; copy.distinct = this.distinct; return copy; } }