/* Copyright (c) 2008 Health Market Science, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License. This library 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 library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA You can contact Health Market Science at info@healthmarketscience.com or at the following address: Health Market Science 2700 Horizon Drive Suite 200 King of Prussia, PA 19406 */ package com.healthmarketscience.sqlbuilder; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.healthmarketscience.common.util.AppendableExt; /** * Outputs a binary midfix based condition * <code>"(<column1> <binaryOp> <column2>)"</code>. * * @author James Ahlborn */ public class BinaryCondition extends Condition { /** * Enum representing the binary midfix operations supported in a SQL * condition, e.g. * <code>"(<column1> <binaryOp> <column2>)"</code>. */ public enum Op { LESS_THAN(" < "), LESS_THAN_OR_EQUAL_TO(" <= "), GREATER_THAN(" > "), GREATER_THAN_OR_EQUAL_TO(" >= "), EQUAL_TO(" = "), NOT_EQUAL_TO(" <> "), LIKE(" LIKE "), NOT_LIKE(" NOT LIKE "), ILIKE(" COLLATE latin1_swedish_ci LIKE "), NOT_ILIKE(" COLLATE latin1_swedish_ci NOT LIKE "); private final String _opStr; private Op(String opStr) { _opStr = opStr; } @Override public String toString() { return _opStr; } } private Object _binaryOp; private SqlObject _leftValue; private SqlObject _rightValue; private ValueObject _escapeChar; public BinaryCondition(Op binaryOp, SqlObject leftValue, SqlObject rightValue) { this(binaryOp, (Object) leftValue, (Object) rightValue); } /** * {@code Object} -> {@code SqlObject} conversions handled by * {@link Converter#toColumnSqlObject(Object)}. */ public BinaryCondition(Op binaryOp, Object leftValue, Object rightValue) { this((Object) binaryOp, leftValue, rightValue); } /** * {@code Object} -> {@code SqlObject} conversions handled by * {@link Converter#toColumnSqlObject(Object)}. */ public BinaryCondition(Object binaryOpStr, Object leftValue, Object rightValue) { _binaryOp = binaryOpStr; _leftValue = Converter.toColumnSqlObject(leftValue); _rightValue = Converter.toColumnSqlObject(rightValue); } @Override protected void collectSchemaObjects(ValidationContext vContext) { _leftValue.collectSchemaObjects(vContext); _rightValue.collectSchemaObjects(vContext); } /** * Sets the escape charactor for a [NOT] LIKE condition pattern. */ public BinaryCondition setLikeEscapeChar(Character escapeChar) { if ((_binaryOp != Op.LIKE) && (_binaryOp != Op.NOT_LIKE)) { throw new IllegalArgumentException( "Escape char only valid for [NOT] LIKE"); } _escapeChar = ((escapeChar != null) ? (new ValueObject(escapeChar)) : null); return this; } @Override public void appendTo(AppendableExt app) throws IOException { openParen(app); app.append(_leftValue).append(_binaryOp).append(_rightValue); if (_escapeChar != null) { app.append(" ESCAPE ").append(_escapeChar); } closeParen(app); } /** * Escapes the special chars '%', '_', and the given char itself in the * given literal string using the given escape character. * * @param literal string to escape as a literal pattern * @param escapeChar escape character to use to escape the literal * @return the escaped string */ public static String escapeLikeLiteral(String literal, char escapeChar) { // escape instances of the escape char, '%' or '_' String escapeStr = String.valueOf(escapeChar); return literal.replaceAll("([%_" + Pattern.quote(escapeStr) + "])", Matcher.quoteReplacement(escapeStr) + "$1"); } /** * Convenience method for generating a Condition for testing if a column is * less than another column (inclusive or exclusive). * <p> * {@code Object} -> {@code SqlObject} conversions handled by * {@link Converter#toColumnSqlObject(Object)}. */ public static BinaryCondition lessThan(Object value1, Object value2, boolean inclusive) { return new BinaryCondition((inclusive ? Op.LESS_THAN_OR_EQUAL_TO : Op.LESS_THAN), Converter.toColumnSqlObject(value1), Converter.toColumnSqlObject(value2)); } /** * Convenience method for generating a Condition for testing if a column is * greater than a given value (inclusive or exclusive). * <p> * {@code Object} -> {@code SqlObject} conversions handled by * {@link Converter#toColumnSqlObject(Object)}. */ public static BinaryCondition greaterThan(Object value1, Object value2, boolean inclusive) { return new BinaryCondition((inclusive ? Op.GREATER_THAN_OR_EQUAL_TO : Op.GREATER_THAN), Converter.toColumnSqlObject(value1), Converter.toColumnSqlObject(value2)); } /** * Convenience method for generating a Condition for testing if a column is * equal to another column. * <p> * {@code Object} -> {@code SqlObject} conversions handled by * {@link Converter#toColumnSqlObject(Object)}. */ public static BinaryCondition equalTo(Object value1, Object value2) { return new BinaryCondition(Op.EQUAL_TO, Converter.toColumnSqlObject(value1), Converter.toColumnSqlObject(value2)); } /** * Convenience method for generating a Condition for testing if a column is * not equal to another column. * <p> * {@code Object} -> {@code SqlObject} conversions handled by * {@link Converter#toColumnSqlObject(Object)}. */ public static BinaryCondition notEqualTo(Object value1, Object value2) { return new BinaryCondition(Op.NOT_EQUAL_TO, Converter.toColumnSqlObject(value1), Converter.toColumnSqlObject(value2)); } /** * Convenience method for generating a Condition for testing if a column is * "like" a given value (sql pattern matching). * <p> * {@code Object} -> {@code SqlObject} conversions handled by * {@link Converter#toColumnSqlObject(Object)}. */ public static BinaryCondition like(Object value1, Object value2) { return new BinaryCondition(Op.LIKE, Converter.toColumnSqlObject(value1), Converter.toColumnSqlObject(value2)); } /** * Convenience method for generating a Condition for testing if a column is * "like" a given value (sql pattern matching). Forces case insensitive comparison. * <p> * {@code Object} -> {@code SqlObject} conversions handled by * {@link Converter#toColumnSqlObject(Object)}. */ public static BinaryCondition iLike(Object value1, Object value2) { return new BinaryCondition(Op.ILIKE, Converter.toColumnSqlObject(value1), Converter.toColumnSqlObject(value2)); } /** * Convenience method for generating a Condition for testing if a column is not * "like" a given value (sql pattern matching). Forces case insensitive comparison. * <p> * {@code Object} -> {@code SqlObject} conversions handled by * {@link Converter#toColumnSqlObject(Object)}. */ public static BinaryCondition notiLike(Object value1, Object value2) { return new BinaryCondition(Op.NOT_ILIKE, Converter.toColumnSqlObject(value1), Converter.toColumnSqlObject(value2)); } /** * Convenience method for generating a Condition for testing if a column is * not "like" a given value (sql pattern matching). * <p> * {@code Object} -> {@code SqlObject} conversions handled by * {@link Converter#toColumnSqlObject(Object)}. */ public static BinaryCondition notLike(Object value1, Object value2) { return new BinaryCondition(Op.NOT_LIKE, Converter.toColumnSqlObject(value1), Converter.toColumnSqlObject(value2)); } }