src/main/java/com/octo/java/sql/exp/JavaSQLFunc.java
public interface Evaluable<T> {
  T eval(Object... params);
}
src/main/java/com/octo/java/sql/exp/SetClause.java
public class SetClause implements Visitable {
  private final Column column;
  private final Object value;

  public SetClause(final Column column, final Object value) {
    super();
    this.column = column;
    this.value = value;
  }

  public void accept(final QueryVisitor visitor) throws QueryException {
    visitor.visit(this);
  }

  public Column getColumn() {
    return column;
  }

  public Object getValue() {
    return value;
  }

}
src/main/java/com/octo/java/sql/exp/Exp.java
public abstract class Exp implements Visitable {
  private boolean valid = true;

  public void invalidate() {
    valid = false;
  }

  public boolean isValid() {
    return valid;
  }

  public AndExp and(final Column andColumn, final Operator andOperator,
      final Object andValue) {
    return new AndExp(this, new OpExp(andColumn, andOperator, andValue));
  }

  public AndExp and(final Column andColumn) {
    return new AndExp(this, new OpExp(andColumn));
  }

  public AndExp and(final SQLFunc func) {
    return new AndExp(this, new OpExp(func));
  }

  public AndExp and(final Exp exp) {
    return new AndExp(this, exp);
  }

  public OrExp or(final Column orColumn, final Operator orOperator,
      final Object orValue, final boolean orValueIsColumnName) {
    return new OrExp(this, new OpExp(orColumn, orOperator, orValue));
  }

  public OrExp or(final Column orColumn) {
    return new OrExp(this, new OpExp(orColumn));
  }

  public OrExp or(final Exp exp) {
    return new OrExp(this, exp);
  }

  public Exp eq(final Object value) throws QueryGrammarException {
    return applyOperation(Operator.EQ, value);
  }

  public Exp neq(final Object value) throws QueryGrammarException {
    return applyOperation(Operator.NEQ, value);
  }

  public Exp geq(final Long value) throws QueryGrammarException {
    return applyOperation(Operator.GEQ, value);
  }

  public Exp leq(final Long value) throws QueryGrammarException {
    return applyOperation(Operator.LEQ, value);
  }

  public Exp like(final String value) throws QueryGrammarException {
    return applyOperation(Operator.LIKE, value);
  }

  public Exp between(final Object valueStart, final Object valueEnd)
      throws QueryGrammarException {
    return applyBetweenOperation(valueStart, valueEnd);
  }

  public Exp startWith(String value) throws QueryGrammarException {
    value = isEmpty(value) ? null : value.concat("%");
    return applyOperation(Operator.LIKE, value);
  }

  public Exp contains(String value) throws QueryGrammarException {
    value = isEmpty(value) ? null : "%".concat(value.concat("%"));
    return applyOperation(Operator.LIKE, value);
  }

  public Exp in(final Object... values) throws QueryGrammarException {
    return applyInOperation(values);
  }

  public Exp in(final Collection<Object> values) throws QueryGrammarException {
    if (values == null) {
      return this;
    } else {
      return applyInOperation(values.toArray(new Object[values.size()]));
    }
  }

  public Exp notIn(final Object... values) throws QueryGrammarException {
    return applyNotInOperation(values);
  }

  public Exp notIn(final Collection<Object> values)
      throws QueryGrammarException {
    if (values == null) {
      return this;
    } else {
      return applyNotInOperation(values.toArray(new Object[values.size()]));
    }
  }

  public Exp isNull() throws QueryGrammarException {
    return applyOperation(Operator.IS, Constant.NULL);
  }

  public Exp isNotNull() throws QueryGrammarException {
    return applyOperation(Operator.IS_NOT, Constant.NULL);
  }

  /**
   * If operator is "between" do a between operation, else apply operation with
   * op.
   * 
   * @param op
   * @param valueStart
   * @param valueEnd
   * @return
   * @throws QueryGrammarException
   */
  public Exp betweenOrOp(final Operator op, final Object valueStart,
      final Object valueEnd) throws QueryGrammarException {
    if (Operator.BTW.equals(op)) {
      return applyBetweenOperation(valueStart, valueEnd);
    } else {
      return applyOperation(op, valueStart);
    }
  }

  public abstract Exp applyOperation(final Operator operator, final Object value)
      throws QueryGrammarException;

  public abstract Exp applyInOperation(final Object... values)
      throws QueryGrammarException;

  public abstract Exp applyNotInOperation(final Object... values)
      throws QueryGrammarException;

  public abstract Exp applyBetweenOperation(final Object valueStart,
      final Object valueEnd) throws QueryGrammarException;
}
src/main/java/com/octo/java/sql/query/QueryGrammarException.java
public class QueryGrammarException extends QueryException {
  private static final long serialVersionUID = 562421290186643510L;

  public QueryGrammarException(final String message) {
    super(message);
  }
}
src/main/java/com/octo/java/sql/exp/Column.java
public class Column implements Visitable {
  private final String name;

  public Column(final String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }

  @Override
  public int hashCode() {
    return name.hashCode();
  }

  @Override
  public boolean equals(final Object obj) {
    if (this == obj)
      return true;
    if (!(obj instanceof Column))
      return false;

    final Column otherObj = (Column) obj;
    if (name == null)
      return otherObj.getName() == null;
    else
      return name.equals(otherObj.getName());
  }

  @Override
  public String toString() {
    return name;
  }

  public void accept(final QueryVisitor visitor) {
    visitor.visit(this);
  }

  public String getTableName() {
    if (name.indexOf(".") > 0)
      return name.split("\\.")[0];
    else
      return null;
  }
}
src/main/java/com/octo/java/sql/exp/SQLFunc.java
public class SQLFunc implements Visitable {
  protected final String funcName;
  protected Object[] params;
  protected String alias;

  SQLFunc(final String funcName) {
    this.funcName = funcName;
  }

  public SQLFunc(final String funcName, final Object... params) {
    this.funcName = funcName;
    this.params = params;
  }

  public String getName() {
    return funcName;
  }

  public SQLFunc as(final String alias) {
    this.alias = alias;
    return this;
  }

  public void accept(final QueryVisitor visitor) throws QueryException {
    visitor.visit(this);
  }

  public Object[] getParams() {
    return params;
  }

  public String getAlias() {
    return alias;
  }
}
src/main/java/com/octo/java/sql/query/DeleteQuery.java
public class DeleteQuery extends Query<DeleteQuery> {
  private final String[] tables;

  /**
   * Constructor can only be called by factory methods in Query class
   * 
   * @param tables
   */
  DeleteQuery(final String... tables) {
    this.tables = tables;
  }

  public void accept(final QueryVisitor visitor) throws QueryException {
    visitor.visit(this);
  }

  public String[] getTables() {
    return tables;
  }
}
src/main/java/com/octo/java/sql/exp/BetweenExp.java
public class BetweenExp extends Exp {
  private final Column column;
  private final Object valueStart;
  private final Object valueEnd;

  BetweenExp(final Column column, final Object valueStart, final Object valueEnd) {
    super();
    this.column = column;
    this.valueStart = valueStart;
    this.valueEnd = valueEnd;
  }

  @Override
  public Exp applyInOperation(final Object... newValues)
      throws QueryGrammarException {
    throw new QueryGrammarException(
        "Cannot apply IN operation on an BETWEEN expression.");
  }

  @Override
  public Exp applyNotInOperation(final Object... values)
      throws QueryGrammarException {
    throw new QueryGrammarException(
        "Cannot apply NOT IN operation on an BETWEEN expression.");
  }

  @Override
  public Exp applyOperation(final Operator operator, final Object value)
      throws QueryGrammarException {
    throw new QueryGrammarException("Cannot apply " + operator
        + " operation on an BETWEEN expression.");
  }

  @Override
  public Exp applyBetweenOperation(final Object start, final Object end)
      throws QueryGrammarException {
    throw new QueryGrammarException(
        "Cannot apply BETWEEN on a BETWEEN expression.");
  }

  public void accept(final QueryVisitor visitor) throws QueryException {
    visitor.visit(this);
  }

  public Column getColumn() {
    return column;
  }

  public Object getValueStart() {
    return valueStart;
  }

  public Object getValueEnd() {
    return valueEnd;
  }
}
src/main/java/com/octo/java/sql/exp/InExp.java
public class InExp extends Exp {
  private final Column column;
  private final Object[] values;
  private final boolean negative;

  InExp(final Column column, final boolean negative, final Object... values) {
    super();
    this.column = column;
    this.values = values;
    this.negative = negative;
  }

  @Override
  public Exp applyInOperation(final Object... newValues)
      throws QueryGrammarException {
    throw new QueryGrammarException(
        "Cannot apply IN operation on an IN expression.");
  }

  @Override
  public Exp applyNotInOperation(final Object... values)
      throws QueryGrammarException {
    throw new QueryGrammarException(
        "Cannot apply NOT IN operation on an IN expression.");
  }

  @Override
  public Exp applyOperation(final Operator operator, final Object value)
      throws QueryGrammarException {
    throw new QueryGrammarException("Cannot apply " + operator
        + " operation on an IN expression.");
  }

  @Override
  public Exp applyBetweenOperation(final Object valueStart,
      final Object valueEnd) throws QueryGrammarException {
    throw new QueryGrammarException("Cannot apply IN on a BETWEEN expression.");
  }

  public void accept(final QueryVisitor visitor) throws QueryException {
    visitor.visit(this);
  }

  public Column getColumn() {
    return column;
  }

  public Object[] getValues() {
    return values;
  }

  public boolean isNegative() {
    return negative;
  }
}
src/main/java/com/octo/java/sql/exp/OpExp.java
public class OpExp extends Exp {
  private final Object lhsValue;
  private final Object rhsValue;
  private final Operator operator;

  public OpExp(final Object lhsValue) {
    super();
    this.lhsValue = lhsValue;
    this.rhsValue = null;
    this.operator = null;
  }

  public OpExp(final Object lhsValue, final Operator operator,
      final Object rhsValue) {
    super();
    this.lhsValue = lhsValue;
    this.rhsValue = rhsValue;
    this.operator = operator;
  }

  //
  // @Override
  // public boolean isValid() {
  // // Under Oracle, an empty string is considered to be null
  // // So it's not possible to be equal to an empty string since it's not
  // // possible to have one in db
  // // So to be coherent, we skip the parameter also when the string is empty
  // if (rhsValue instanceof String) {
  // return StringUtils.isNotEmpty((String) rhsValue);
  // }
  // return rhsValue != null;
  // }

  @Override
  public Exp applyOperation(final Operator newOperator, final Object newRhsValue)
      throws QueryGrammarException {
    if (operator != null) {
      throw new QueryGrammarException("Cannot apply " + newOperator.getValue()
          + " operation on an " + operator.getValue() + " expression.");
    }
    return new OpExp(lhsValue, newOperator, newRhsValue);
  }

  @Override
  public Exp applyInOperation(final Object... values)
      throws QueryGrammarException {
    if (!(lhsValue instanceof Column))
      throw new QueryGrammarException(
          "Can only apply 'in' operation on a Column");
    return new InExp((Column) lhsValue, false, values);
  }

  @Override
  public Exp applyNotInOperation(final Object... values)
      throws QueryGrammarException {
    if (!(lhsValue instanceof Column))
      throw new QueryGrammarException(
          "Can only apply 'in' operation on a Column");
    return new InExp((Column) lhsValue, true, values);
  }

  @Override
  public Exp applyBetweenOperation(final Object valueStart,
      final Object valueEnd) throws QueryGrammarException {
    if (!(lhsValue instanceof Column))
      throw new QueryGrammarException(
          "Can only apply 'between' operation on a Column");
    return new BetweenExp((Column) lhsValue, valueStart, valueEnd);
  }

  public void accept(final QueryVisitor visitor) throws QueryException {
    visitor.visit(this);
  }

  public Object getLhsValue() {
    return lhsValue;
  }

  public Operator getOperator() {
    return operator;
  }

  public Object getRhsValue() {
    return rhsValue;
  }
}
src/main/java/com/octo/java/sql/query/SelectQuery.java
public enum Order {
  DESC, ASC;
}
src/main/java/com/octo/java/sql/query/UpdateQuery.java
public class UpdateQuery extends Query<UpdateQuery> {
  private final List<SetClause> setClauses = new ArrayList<SetClause>();
  private final String table;

  /**
   * Constructor can only be called by factory methods in Query class
   * 
   * @param columns
   */
  UpdateQuery(final String table) {
    this.table = table;
  }

  public UpdateQuery set(final Column column, final Object value) {
    setClauses.add(new SetClause(column, value));
    return this;
  }

  public void accept(final QueryVisitor visitor) throws QueryException {
    visitor.visit(this);
  }

  public String getTable() {
    return table;
  }

  public List<SetClause> getSetClauses() {
    return setClauses;
  }
}
src/main/java/com/octo/java/sql/exp/ExpSeq.java
public abstract class ExpSeq extends Exp {
  protected final List<Exp> clauses;

  ExpSeq(final Exp... clauses) {
    super();
    this.clauses = Arrays.asList(clauses);
  }

  @Override
  public Exp applyOperation(final Operator operator, final Object value)
      throws QueryGrammarException {
    final int lastIndex = clauses.size() - 1;
    final Exp lastClause = clauses.get(lastIndex);
    clauses.set(lastIndex, lastClause.applyOperation(operator, value));
    return this;
  }

  @Override
  public Exp applyBetweenOperation(final Object valueStart,
      final Object valueEnd) throws QueryGrammarException {
    final int lastIndex = clauses.size() - 1;
    final Exp lastClause = clauses.get(lastIndex);
    clauses.set(lastIndex, lastClause.applyBetweenOperation(valueStart,
        valueEnd));
    return this;
  }

  @Override
  public Exp applyInOperation(final Object... values)
      throws QueryGrammarException {
    final int lastIndex = clauses.size() - 1;
    final Exp lastClause = clauses.get(lastIndex);
    clauses.set(lastIndex, lastClause.applyInOperation(values));
    return this;
  }

  @Override
  public Exp applyNotInOperation(final Object... values)
      throws QueryGrammarException {
    final int lastIndex = clauses.size() - 1;
    final Exp lastClause = clauses.get(lastIndex);
    clauses.set(lastIndex, lastClause.applyNotInOperation(values));
    return this;
  }

  public void accept(final QueryVisitor visitor) throws QueryException {
    visitor.visit(this);
  }

  public abstract Operator getOperator();

  public List<Exp> getClauses() {
    return clauses;
  }
}
src/main/java/com/octo/java/sql/exp/Operator.java
public enum Operator {
  IS("IS"), BTW("BETWEEN"), LIKE("LIKE"), EQ("="), NEQ("!="), GEQ(">="), LEQ(
      "<="), NOT("NOT"), IN("IN"), IS_NOT("IS NOT"), AND("AND"), OR("OR");

  private final String value;

  Operator(final String value) {
    this.value = value;
  }

  public String getValue() {
    return value;
  }
}
src/main/java/com/octo/java/sql/query/QueryException.java
public class QueryException extends Exception {
  private static final long serialVersionUID = -2306972223647011582L;

  public QueryException(final String message) {
    super(message);
  }
}
src/main/java/com/octo/java/sql/exp/Constant.java
public enum Constant implements Visitable {
  NULL("NULL"), STAR("*");

  private final String value;

  Constant(final String value) {
    this.value = value;
  }

  public String getValue() {
    return value;
  }

  public void accept(final QueryVisitor visitor) {
    visitor.visit(this);
  }
}
src/main/java/com/octo/java/sql/exp/JoinClause.java
public class JoinClause implements Visitable {
  public static enum JoinType {
    LEFT_OUTER_JOIN("LEFT OUTER JOIN"), RIGHT_OUTER_JOIN("RIGHT OUTER JOIN"), INNER_JOIN(
        "INNER JOIN");

    public final String value;

    JoinType(final String value) {
      this.value = value;
    }
  }

  private final String table;
  private final JoinType joinType;
  private Exp onClause;
  private final SelectQuery query;
  private boolean valid = true;

  public JoinClause(final String table, final JoinType joinType,
      final SelectQuery query) {
    super();
    this.table = table;
    this.joinType = joinType;
    this.query = query;
  }

  public SelectQuery on(final Column column, final Operator operator,
      final Object value) {
    onClause = new OpExp(column, operator, value);
    return query;
  }

  public SelectQuery on(final Exp exp) {
    onClause = exp;
    return query;
  }

  public JoinClause on(final Column column) {
    onClause = new OpExp(column);
    return this;
  }

  public SelectQuery eq(final Object value) throws QueryGrammarException {
    assertOnClauseIsInitialized("eq");
    onClause = onClause.eq(value);
    return query;
  }

  private void assertOnClauseIsInitialized(final String operation)
      throws QueryGrammarException {
    if (onClause == null)
      throw new QueryGrammarException("Cannot apply '" + operation
          + "' operation without an initialized join clause.");
  }

  public void accept(final QueryVisitor visitor) throws QueryException {
    visitor.visit(this);
  }

  public Exp getOnClause() {
    return onClause;
  }

  public JoinType getType() {
    return joinType;
  }

  public String getTable() {
    return table;
  }

  public boolean isValid() {
    return valid;
  }

  public void invalidate() {
    valid = false;
  }
}
src/main/java/com/octo/java/sql/query/InsertQuery.java
public class InsertQuery extends Query<InsertQuery> {
  private final ListOrderedMap columnsValues = new ListOrderedMap();
  private final String table;

  /**
   * Constructor
   * 
   * @param table
   */
  InsertQuery(final String table) {
    this.table = table;
  }

  public InsertQuery set(final String column, final Object value)
      throws QueryGrammarException {
    if (columnsValues.containsKey(column))
      throw new QueryGrammarException("Column '" + column
          + "' has already been set.");
    columnsValues.put(column, value);
    return this;
  }

  public InsertQuery set(final String column, final Object value,
      final Object defaultValueIfNull) throws QueryGrammarException {
    if (value == null)
      return set(column, defaultValueIfNull);
    else
      return set(column, value);
  }

  public void accept(final QueryVisitor visitor) throws QueryException {
    visitor.visit(this);
  }

  public ListOrderedMap getColumnsValues() {
    return columnsValues;
  }

  public String getTable() {
    return table;
  }

}
src/main/java/com/octo/java/sql/exp/Nullable.java
public class Nullable implements Visitable {
  private final Object value;

  public Nullable(final Object value) {
    this.value = value;
  }

  public Object getValue() {
    return value;
  }

  public boolean isNull() {
    return value == null;
  }

  @Override
  public boolean equals(final Object obj) {
    if (obj == null)
      return false;
    if (!(obj instanceof Nullable))
      return false;

    final Nullable otherObj = (Nullable) obj;
    if (value == null)
      return otherObj.getValue() == null;
    else
      return value.equals(otherObj.getValue());
  }

  public void accept(final QueryVisitor visitor) throws QueryException {
    visitor.visit(this);
  }
}
src/main/java/com/octo/java/sql/query/SelectQuery.java
public class SelectQuery extends Query<SelectQuery> {
  public enum Order {
    DESC, ASC;
  }

  /**
   * Contains String or SQLFunc
   */
  protected final Object[] columns;
  protected String[] tables;
  private final List<JoinClause> joinClauses = new ArrayList<JoinClause>();
  private final Map<String, Order> orderBy = new LinkedHashMap<String, Order>();
  private Long limit = null;
  private String lastOrderByColumn = null;
  private boolean lastOrderByNeeded = true;
  private final List<SelectQuery> unions = new ArrayList<SelectQuery>();
  private String alias;

  /**
   * Constructor can only be called by factory methods in Query class
   * 
   * @param columns
   *          to put both SQLFunc & Colsname
   */
  SelectQuery(final Object... columns) {
    if ((columns != null) && (columns.length == 1)
        && Constant.STAR.getValue().equals(columns[0]))
      this.columns = new Object[] { Constant.STAR };
    else
      this.columns = columns;
  }

  public SelectQuery from(final String... newTables) {
    tables = newTables;
    return this;
  }

  public JoinClause innerJoin(final String table) {
    final JoinClause result = new JoinClause(table,
        JoinClause.JoinType.INNER_JOIN, this);
    joinClauses.add(result);
    return result;
  }

  public JoinClause leftOuterJoin(final String table) {
    final JoinClause result = new JoinClause(table,
        JoinClause.JoinType.LEFT_OUTER_JOIN, this);
    joinClauses.add(result);
    return result;
  }

  public SelectQuery orderBy(final String value) {
    this.orderBy.put(value, null);
    lastOrderByColumn = value;
    return this;
  }

  public SelectQuery orderBy(final String value, final boolean isOrderByNeeded) {
    lastOrderByNeeded = isOrderByNeeded;
    if (isOrderByNeeded) {
      return orderBy(value);
    }
    return this;
  }

  public SelectQuery desc() throws QueryGrammarException {
    if (!lastOrderByNeeded) {
      return this;
    }
    assertOrderBySpecified("desc");
    orderBy.put(lastOrderByColumn, Order.DESC);
    return this;
  }

  private void assertOrderBySpecified(final String operation)
      throws QueryGrammarException {
    if (lastOrderByColumn == null) {
      throw new QueryGrammarException("Cannot apply '" + operation
          + "' operator without order by column.");
    }
  }

  public SelectQuery asc() throws QueryGrammarException {
    if (!lastOrderByNeeded) {
      return this;
    }
    assertOrderBySpecified("asc");
    orderBy.put(lastOrderByColumn, Order.ASC);
    return this;
  }

  public SelectQuery limit(final Long newLimit) throws QueryGrammarException {
    this.limit = newLimit;
    return this;
  }

  public SelectQuery as(final String alias) {
    this.alias = alias;
    return this;
  }

  public SelectQuery union(final SelectQuery innerQuery) {
    unions.add(innerQuery);
    return this;
  }

  public void accept(final QueryVisitor visitor) throws QueryException {
    visitor.visit(this);
  }

  public Object[] getColumns() {
    return columns;
  }

  public String[] getTables() {
    return tables;
  }

  public List<JoinClause> getJoinClauses() {
    return joinClauses;
  }

  public String getAlias() {
    return alias;
  }

  public Map<String, Order> getOrderBy() {
    return orderBy;
  }

  public Long getLimit() {
    return limit;
  }

  public List<SelectQuery> getUnions() {
    return unions;
  }
}


Question: import static org.apache.commons.collections.CollectionUtils.isEmpty;
import static org.apache.commons.lang.ArrayUtils.isEmpty;
import static org.apache.commons.lang.StringUtils.isEmpty;
import static org.apache.commons.lang.StringUtils.join;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.collections.map.ListOrderedMap;
import com.octo.java.sql.exp.BetweenExp;
import com.octo.java.sql.exp.Column;
import com.octo.java.sql.exp.Constant;
import com.octo.java.sql.exp.Exp;
import com.octo.java.sql.exp.ExpSeq;
import com.octo.java.sql.exp.InExp;
import com.octo.java.sql.exp.JoinClause;
import com.octo.java.sql.exp.Nullable;
import com.octo.java.sql.exp.OpExp;
import com.octo.java.sql.exp.Operator;
import com.octo.java.sql.exp.SQLFunc;
import com.octo.java.sql.exp.SetClause;
import com.octo.java.sql.exp.JavaSQLFunc.Evaluable;
import com.octo.java.sql.query.DeleteQuery;
import com.octo.java.sql.query.InsertQuery;
import com.octo.java.sql.query.QueryException;
import com.octo.java.sql.query.QueryGrammarException;
import com.octo.java.sql.query.SelectQuery;
import com.octo.java.sql.query.UpdateQuery;
import com.octo.java.sql.query.SelectQuery.Order;
/**
 * Copyright (C) 2010 David Rousselie <drousselie@octo.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.octo.java.sql.query.visitor;





public class DefaultQueryBuilder extends BaseVisitor {
  public static final String DEFAULT_BASE_VARIABLE_NAME = "param";
  private static final String OPEN_BRACKET = "(";
  private static final String BETWEEN = "BETWEEN";
  private static final String CLOSE_BRACKET = ")";
  private static final String ON = "ON";
  private static final String AS = "AS";
  private static final String SELECT = "SELECT";
  private static final String FROM = "FROM";
  private static final String WHERE = "WHERE";
  private static final String ORDER_BY = "ORDER BY";
  private static final String UNION = "UNION";
  private static final String INSERT = "INSERT INTO";
  private static final String VALUES = "VALUES";
  private static final String UPDATE = "UPDATE";
  private static final String SET = "SET";
  private static final String DELETE_FROM = "DELETE FROM";

  protected final StringBuilder result = new StringBuilder();
  private int variableIndex = 1;
  private final Map<String, Object> params = new HashMap<String, Object>();
  private boolean addBracketToNextSelectQuery = false;
  private final Map<String, Evaluable<?>> functions = new HashMap<String, Evaluable<?>>();

  public void addFunction(final String functionName,
      final Evaluable<String> function) {
    functions.put(functionName, function);
  }

  public StringBuilder getResult() {
    return result;
  }

  public Map<String, Object> getParams() {
    return params;
  }

  /**
   * Add a variable to parameters map and return its name
   * 
   * @param columnName
   * @return
   */
  protected String addVariable(final Object value, final String baseColumnName) {
    final String columnName = baseColumnName + variableIndex++;
    params.put(columnName, value);
    return columnName;
  }

  private String getVariableName(final Object obj, final String defaultName) {
    if (obj instanceof Column)
      return ((Column) obj).getName();
    else if (obj instanceof SQLFunc)
      return ((SQLFunc) obj).getName();
    else
      return defaultName;
  }

  private void acceptOrVisitValue(final Object value, final String baseName)
      throws QueryException {
    if (value instanceof Visitable)
      ((Visitable) value).accept(this);
    else {
      final String variableName = addVariable(value, baseName);
      result.append(":").append(variableName);
    }
  }

  public void visitValue(final Object value) {
    final String variableName = addVariable(value, DEFAULT_BASE_VARIABLE_NAME);
    result.append(":").append(variableName);
  }

  protected void buildWhereClause(final Exp whereClause) throws QueryException {
    if (whereClause.isValid()) {
      result.append(" ").append(WHERE).append(" ");
      whereClause.accept(this);
    }
  }

  protected void buildLimitClause(final SelectQuery query) {
    result.append(" ").append("LIMIT").append(" ");
    result.append(":").append(addVariable(query.getLimit(), "limit"));
  }

  /**
   * Visit methods
   */
  public void visit(final Column column) {
    result.append(column.getName());
  }

  public void visit(final OpExp exp) throws QueryException {
    String baseVariableName = getVariableName(exp.getLhsValue(), null);
    if (baseVariableName == null)
      baseVariableName = getVariableName(exp.getRhsValue(), "var");

    result.append(OPEN_BRACKET);
    acceptOrVisitValue(exp.getLhsValue(), baseVariableName);
    if ((exp.getRhsValue() == null) //
        || ((exp.getRhsValue() instanceof Nullable) //
        && ((Nullable) exp.getRhsValue()).isNull())) {
      if (!Operator.EQ.equals(exp.getOperator()))
        throw new QueryGrammarException("Cannot use NULL value with operator "
            + exp.getOperator().getValue());
      result.append(" ").append(Operator.IS.getValue());
      result.append(" ").append(Constant.NULL);
    } else {
      result.append(" ").append(exp.getOperator().getValue()).append(" ");
      acceptOrVisitValue(exp.getRhsValue(), baseVariableName);
    }
    result.append(CLOSE_BRACKET);
  }

  public void visit(final BetweenExp betweenExp) throws QueryException {
    final Column column = betweenExp.getColumn();
    if ((betweenExp.getValueStart() == null)
        || (betweenExp.getValueEnd() == null))
      throw new QueryGrammarException(
          "Cannot apply BETWEEN with one NULL value");

    result.append(OPEN_BRACKET);
    visit(column);
    result.append(" ").append(BETWEEN).append(" ");
    acceptOrVisitValue(betweenExp.getValueStart(), column.getName());
    result.append(" ").append(Operator.AND).append(" ");
    acceptOrVisitValue(betweenExp.getValueEnd(), column.getName());
    result.append(CLOSE_BRACKET);
  }

Answer:   public void visit(final ExpSeq expSeq) throws QueryException {