Open Source Repository

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



org/hibernate/dialect/function/DerbyConcatFunction.java
/*
 * 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.dialect.function;

import java.util.List;
import java.util.Iterator;

import org.hibernate.Hibernate;
import org.hibernate.QueryException;
import org.hibernate.engine.Mapping;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.type.Type;

/**
 * A specialized concat() function definition in which:<ol>
 <li>we translate to use the concat operator ('||')</li>
 <li>wrap dynamic parameters in CASTs to VARCHAR</li>
 </ol>
 <p/>
 * This last spec is to deal with a limitation on DB2 and variants (e.g. Derby)
 * where dynamic parameters cannot be used in concatenation unless they are being
 * concatenated with at least one non-dynamic operand.  And even then, the rules
 * are so convoluted as to what is allowed and when the CAST is needed and when
 * it is not that we just go ahead and do the CASTing.
 *
 @author Steve Ebersole
 */
public class DerbyConcatFunction implements SQLFunction {
  /**
   * {@inheritDoc}
   <p/>
   * Here we always return {@link Hibernate#STRING}.
   */
  public Type getReturnType(Type columnType, Mapping mappingthrows QueryException {
    return Hibernate.STRING;
  }

  /**
   * {@inheritDoc}
   <p/>
   * Here we always return <tt>true</tt>
   */
  public boolean hasArguments() {
    return true;
  }

  /**
   * {@inheritDoc}
   <p/>
   * Here we always return <tt>true</tt>
   */
  public boolean hasParenthesesIfNoArguments() {
    return true;
  }

  /**
   * {@inheritDoc}
   <p/>
   * Here's the meat..  The whole reason we have a separate impl for this for Derby is to re-define
   * this method.  The logic here says that if not all the incoming args are dynamic parameters
   * (i.e. <tt>?</tt>) then we simply use the Derby concat operator (<tt>||</tt>) on the unchanged
   * arg elements.  However, if all the args are dynamic parameters, then we need to wrap the individual
   * arg elements in <tt>cast</tt> function calls, use the concantenation operator on the <tt>cast</tt>
   * returns, and then wrap that whole thing in a call to the Derby <tt>varchar</tt> function.
   */
  public String render(List args, SessionFactoryImplementor factorythrows QueryException {
    boolean areAllArgsParams = true;
    Iterator itr = args.iterator();
    while itr.hasNext() ) {
      final String arg = String itr.next();
      if "?".equalsarg ) ) {
        areAllArgsParams = false;
        break;
      }
    }

    if areAllArgsParams ) {
      return join(
          args.iterator(),
          new StringTransformer() {
            public String transform(String string) {
              return "cast( ? as varchar(32672) )";
            }
          },
          new StringJoinTemplate() {
            public String getBeginning() {
              return "varchar( ";
            }
            public String getSeparator() {
              return " || ";
            }
            public String getEnding() {
              return " )";
            }
          }
      );
    }
    else {
      return join(
          args.iterator(),
          new StringTransformer() {
            public String transform(String string) {
              return string;
            }
          },
          new StringJoinTemplate() {
            public String getBeginning() {
              return "(";
            }
            public String getSeparator() {
              return "||";
            }
            public String getEnding() {
              return ")";
            }
          }
      );
    }
  }

  private static interface StringTransformer {
    public String transform(String string);
  }

  private static interface StringJoinTemplate {
    /**
     * Getter for property 'beginning'.
     *
     @return Value for property 'beginning'.
     */
    public String getBeginning();
    /**
     * Getter for property 'separator'.
     *
     @return Value for property 'separator'.
     */
    public String getSeparator();
    /**
     * Getter for property 'ending'.
     *
     @return Value for property 'ending'.
     */
    public String getEnding();
  }

  private String join(Iterator/*<String>*/ elements, StringTransformer elementTransformer, StringJoinTemplate template) {
    StringBuffer buffer = new StringBuffertemplate.getBeginning() );
    while elements.hasNext() ) {
      final String element = String elements.next();
      buffer.appendelementTransformer.transformelement ) );
      if elements.hasNext() ) {
        buffer.appendtemplate.getSeparator() );
      }
    }
    return buffer.appendtemplate.getEnding() ).toString();
  }
}