001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdbc.sql.exp;
012:
013: import com.versant.core.util.CharBuf;
014: import com.versant.core.jdbc.sql.SqlDriver;
015: import com.versant.core.jdbc.query.SqlStruct;
016:
017: import java.util.Map;
018:
019: /**
020: * An expression comprised of two args and an operator.
021: */
022: public class BinaryOpExp extends BinaryExp {
023:
024: public static final int EQUAL = 1;
025: public static final int NOT_EQUAL = 2;
026: public static final int GT = 3;
027: public static final int LT = 4;
028: public static final int GE = 5;
029: public static final int LE = 6;
030: public static final int LIKE = 7;
031: public static final int CONCAT = 8;
032: public static final int PLUS = 9;
033: public static final int MINUS = 10;
034: public static final int TIMES = 11;
035: public static final int DIVIDE = 12;
036:
037: public int op;
038:
039: /**
040: * The index of the first character of our operator in the sql output
041: * buffer.
042: */
043: private int firstCharIndex;
044:
045: public BinaryOpExp(SqlExp left, int op, SqlExp right) {
046: super (left, right);
047: this .op = op;
048: }
049:
050: public BinaryOpExp(int op) {
051: this .op = op;
052: }
053:
054: public BinaryOpExp() {
055: }
056:
057: public SqlExp createInstance() {
058: return new BinaryOpExp();
059: }
060:
061: public SqlExp getClone(SqlExp exp, Map cloneMap) {
062: super .getClone(exp, cloneMap);
063: BinaryOpExp cst = (BinaryOpExp) exp;
064:
065: cst.op = op;
066: cst.firstCharIndex = firstCharIndex;
067:
068: return exp;
069: }
070:
071: public static String toOpString(int op) {
072: switch (op) {
073: case EQUAL:
074: return "=";
075: case NOT_EQUAL:
076: return "<>";
077: case GT:
078: return ">";
079: case LT:
080: return "<";
081: case GE:
082: return ">=";
083: case LE:
084: return "<=";
085: case LIKE:
086: return "LIKE";
087: case CONCAT:
088: return "||";
089: case PLUS:
090: return "+";
091: case MINUS:
092: return "-";
093: case TIMES:
094: return "*";
095: case DIVIDE:
096: return "/";
097: }
098: return "<unknown " + op + ">";
099: }
100:
101: public String toString() {
102: return super .toString() + " " + toOpString(op);
103: }
104:
105: /**
106: * Append SQL for this node to s.
107: *
108: * @param driver The driver being used
109: * @param s Append the SQL here
110: * @param leftSibling
111: */
112: public void appendSQLImp(SqlDriver driver, CharBuf s,
113: SqlExp leftSibling) {
114: childList.appendSQL(driver, s, null);
115:
116: SqlExp right = childList.next;
117:
118: // convert == and != to 'is null' or 'is not null' if the right hand
119: // side is a null literal
120: if ((op == EQUAL || op == NOT_EQUAL)
121: && right instanceof LiteralExp
122: && ((LiteralExp) right).type == LiteralExp.TYPE_NULL) {
123: if (op == EQUAL) {
124: s.append(" is null");
125: } else {
126: s.append(" is not null");
127: }
128: return;
129: }
130:
131: s.append(' ');
132: firstCharIndex = s.size();
133: s.append(driver.getSqlBinaryOp(op));
134: s.append(' ');
135: if (!removeConcat(driver, s, right)) {
136: right.appendSQL(driver, s, childList);
137: }
138:
139: // make space for 'is null' or 'is not null' if required
140: if (childList.next instanceof ParamExp) {
141: if (op == EQUAL) {
142: int n = s.size() - firstCharIndex;
143: for (; n < 11; n++)
144: s.append(' ');
145: } else if (op == NOT_EQUAL) {
146: int n = s.size() - firstCharIndex;
147: for (; n < 11; n++)
148: s.append(' ');
149: }
150: }
151: }
152:
153: /**
154: * If this is a LIKE and the driver does not handle LIKE well and the
155: * right subtree contains a percent literal and a parameter then output
156: * just the parameter with modification to add the percent before the
157: * query runs.
158: */
159: private boolean removeConcat(SqlDriver driver, CharBuf s,
160: SqlExp right) {
161: if (op == LIKE && driver.isLikeStupid()
162: && right instanceof BinaryOpExp
163: && ((BinaryOpExp) right).isConcatParamAndPercent()) {
164: ParamExp pe;
165: int mod;
166: if (right.childList instanceof ParamExp) {
167: pe = (ParamExp) right.childList;
168: mod = SqlStruct.Param.MOD_APPEND_PERCENT;
169: } else {
170: pe = (ParamExp) right.childList.next;
171: mod = SqlStruct.Param.MOD_PREPEND_PERCENT;
172: }
173: pe.usage.mod = mod;
174: pe.appendSQL(driver, s, null);
175: return true;
176: } else {
177: return false;
178: }
179: }
180:
181: private boolean isConcatParamAndPercent() {
182: if (op != CONCAT)
183: return false;
184: if (childList instanceof ParamExp) {
185: return isLiteralPercent(childList.next);
186: } else if (childList.next instanceof ParamExp) {
187: return isLiteralPercent(childList);
188: }
189: return false;
190: }
191:
192: private static boolean isLiteralPercent(SqlExp e) {
193: return e instanceof LiteralExp
194: && ((LiteralExp) e).value.equals("%");
195: }
196:
197: /**
198: * Get the index of first character of the 'is null' parameter
199: * replacement span for this expression.
200: */
201: public int getFirstCharIndex() {
202: return firstCharIndex;
203: }
204:
205: /**
206: * Is this a negative expression for the purposes of replacing parameter
207: * values with 'is null' (false) or 'is not null' (true)?
208: */
209: public boolean isNegative() {
210: return op == NOT_EQUAL;
211: }
212: }
|