Open Source Repository

Home /hibernate/hibernate-3.2.4.ga | Repository Home



org/hibernate/hql/QuerySplitter.java
//$Id: QuerySplitter.java 7646 2005-07-25 07:37:13Z oneovthafew $
package org.hibernate.hql;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.MappingException;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.hql.classic.ParserHelper;
import org.hibernate.util.StringHelper;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

/**
 * Provides query splitting methods, which were originally in QueryTranslator.
 <br>
 * TODO: This will need to be refactored at some point.
 *
 @author josh Mar 14, 2004 10:50:23 AM
 */
public final class QuerySplitter {

  private static final Log log = LogFactory.getLogQuerySplitter.class );

  private static final Set BEFORE_CLASS_TOKENS = new HashSet();
  private static final Set NOT_AFTER_CLASS_TOKENS = new HashSet();

  static {
    BEFORE_CLASS_TOKENS.add"from" );
    BEFORE_CLASS_TOKENS.add"delete" );
    BEFORE_CLASS_TOKENS.add"update" );
    //beforeClassTokens.add("new"); DEFINITELY DON'T HAVE THIS!!
    BEFORE_CLASS_TOKENS.add"," );
    NOT_AFTER_CLASS_TOKENS.add"in" );
    //notAfterClassTokens.add(",");
    NOT_AFTER_CLASS_TOKENS.add"from" );
    NOT_AFTER_CLASS_TOKENS.add")" );
  }

  /**
   * Private empty constructor.
   * (or else checkstyle says: 'warning: Utility classes should not have a public or default constructor.')
   */
  private QuerySplitter() {
  }

  /**
   * Handle Hibernate "implicit" polymorphism, by translating the query string into
   * several "concrete" queries against mapped classes.
   */
  public static String[] concreteQueries(String query, SessionFactoryImplementor factorythrows MappingException {

    //scan the query string for class names appearing in the from clause and replace
    //with all persistent implementors of the class/interface, returning multiple
    //query strings (make sure we don't pick up a class in the select clause!)

    //TODO: this is one of the ugliest and most fragile pieces of code in Hibernate....

    String[] tokens = StringHelper.splitStringHelper.WHITESPACE + "(),", query, true );
    if tokens.length == return new String[]{query}// just especially for the trivial collection filter
    ArrayList placeholders = new ArrayList();
    ArrayList replacements = new ArrayList();
    StringBuffer templateQuery = new StringBuffer40 );
    int count = 0;
    String last = null;
    int nextIndex = 0;
    String next = null;
    boolean isSelectClause = false;

    templateQuery.appendtokens[0] );
    if "select".equalstokens[0].toLowerCase() ) ) isSelectClause = true;
        
    for int i = 1; i < tokens.length; i++ ) {

      //update last non-whitespace token, if necessary
      if !ParserHelper.isWhitespacetokens[i - 1] ) ) last = tokens[i - 1].toLowerCase();

      // select-range is terminated by declaration of "from"
      if "from".equalstokens[i].toLowerCase() ) ) isSelectClause = false;

      String token = tokens[i];
      if !ParserHelper.isWhitespacetoken || last == null ) {

        //scan for next non-whitespace token
        if nextIndex <= i ) {
          for nextIndex = i + 1; nextIndex < tokens.length; nextIndex++ ) {
            next = tokens[nextIndex].toLowerCase();
            if !ParserHelper.isWhitespacenext ) ) break;
          }
        }

        boolean process = !isSelectClause && 
            isJavaIdentifiertoken && 
            isPossiblyClassNamelast, next );
            
        if (process) {
          String importedClassName = getImportedClasstoken, factory );
          if importedClassName != null ) {
            String[] implementors = factory.getImplementorsimportedClassName );
            String placeholder = "$clazz" + count++ + "$";
            if implementors != null ) {
              placeholders.addplaceholder );
              replacements.addimplementors );
            }
            token = placeholder; // Note this!!
          }
        }

      }

      templateQuery.appendtoken );

    }
    String[] results = StringHelper.multiplytemplateQuery.toString(), placeholders.iterator(), replacements.iterator() );
    if results.length == log.warn"no persistent classes found for query class: " + query );
    return results;
  }

  private static boolean isPossiblyClassName(String last, String next) {
    return "class".equalslast || 
        BEFORE_CLASS_TOKENS.containslast && 
        !NOT_AFTER_CLASS_TOKENS.containsnext 
      );
  }

  private static boolean isJavaIdentifier(String token) {
    return Character.isJavaIdentifierStarttoken.charAt) );
  }

  public static String getImportedClass(String name, SessionFactoryImplementor factory) {
    return factory.getImportedClassNamename );
  }
}