001: /*
002: * Copyright 2004-2007 Gary Bentley
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may
005: * not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015: package org.josql.expressions;
016:
017: import java.util.List;
018:
019: import org.josql.Query;
020: import org.josql.QueryExecutionException;
021: import org.josql.QueryParseException;
022:
023: import org.josql.internal.Utilities;
024:
025: /**
026: * Represents a LHS [ NOT ] [ $ ] LIKE RHS expression.
027: * It should be noted that unlike "normal" SQL the "." character is not supported since in
028: * practice it tends to be redundant.
029: * <p>
030: * It is possible to use: $ in front of the "LIKE" to indicate that a case insensitive
031: * comparison should be performed, for example:
032: * <pre>
033: * SELECT *
034: * FROM java.lang.String
035: * WHERE toString $LIKE '%value'
036: * </pre>
037: * <p>
038: * Also, the LHS or RHS can be built up using the "+" operator to concatenate a string, thus:
039: * <pre>
040: * SELECT *
041: * FROM java.lang.String
042: * WHREE toString $LIKE '%' + :myValue
043: * </pre>
044: * <p>
045: * In this way (using the {@link BindVariable named bind variable}) you don't have to provide
046: * the wildcard.
047: * <p>
048: * It is also possible to specify your own "wildcard" character in the Query object using:
049: * {@link Query#setWildcardCharacter(char)}.
050: * <p>
051: * Note: the implementation is a modified version of that provided by: Kevin Stannard
052: * (http://www.jzoo.com/java/wildcardfilter/).
053: */
054: public class LikeExpression extends BinaryExpression {
055:
056: private boolean not = false;
057: private boolean ignoreCase = false;
058: private List pattern = null;
059:
060: public boolean isIgnoreCase() {
061:
062: return this .ignoreCase;
063:
064: }
065:
066: /**
067: * Init the expression, we over-ride here so that if the RHS is fixed we can
068: * init the pattern that will be used to match the expression.
069: *
070: * @param q The Query object.
071: * @throws QueryParseException If the LHS and/or RHS cannot be inited.
072: */
073: public void init(Query q) throws QueryParseException {
074:
075: // Call our parent first.
076: super .init(q);
077:
078: if (this .right.hasFixedResult(q)) {
079:
080: // It does have a fixed result so get the value and init the pattern.
081: Object r = null;
082:
083: try {
084:
085: r = this .right.getValue(null, q);
086:
087: } catch (Exception e) {
088:
089: throw new QueryParseException(
090: "Unable to get RHS value from: \""
091: + this .right
092: + "\", expected to RHS to have fixed result.",
093: e);
094:
095: }
096:
097: if (r == null) {
098:
099: // Return since we can't do anything useful now.
100: return;
101:
102: }
103:
104: String rs = r.toString();
105:
106: if (this .ignoreCase) {
107:
108: rs = rs.toLowerCase();
109:
110: }
111:
112: char wc = q.getWildcardCharacter();
113:
114: this .pattern = Utilities.getLikePattern(rs, String
115: .valueOf(wc));
116:
117: }
118:
119: }
120:
121: public void setIgnoreCase(boolean v) {
122:
123: this .ignoreCase = v;
124:
125: }
126:
127: public boolean isNot() {
128:
129: return this .not;
130:
131: }
132:
133: public void setNot(boolean v) {
134:
135: this .not = v;
136:
137: }
138:
139: /**
140: * Returns whether the LHS is "LIKE" the RHS.
141: * A special case here is that if the LHS and RHS are both <code>null</code> then <code>true</code>
142: * is returned. Also, if either the LHS or RHS is not null and one is null then <code>false</code>
143: * is returned.
144: *
145: * @param o The object to evaluate the expression on.
146: * @param q The Query object.
147: * @return <code>true</code> if the LHS is "LIKE" the RHS, <code>false</code> if not. And using
148: * "NOT" will invert the result.
149: * @throws QueryExecutionException If the expression cannot be evaluated.
150: */
151: public boolean isTrue(Object o, Query q)
152: throws QueryExecutionException {
153:
154: // Get the left...
155: Object l = this .left.getValue(o, q);
156:
157: if (this .pattern != null) {
158:
159: return Utilities.matchLikePattern(this .pattern, l,
160: this .not, this .ignoreCase);
161:
162: }
163:
164: Object r = this .right.getValue(o, q);
165:
166: if ((l == null) && (r == null)) {
167:
168: // Special case...
169: if (this .not) {
170:
171: return false;
172:
173: }
174:
175: return true;
176:
177: }
178:
179: if ((l == null) || (r == null)) {
180:
181: if (this .not) {
182:
183: return true;
184:
185: }
186:
187: return false;
188:
189: }
190:
191: // Convert RHS to a string.
192: String rs = r.toString();
193:
194: if (this .ignoreCase) {
195:
196: rs = rs.toLowerCase();
197:
198: }
199:
200: // Now see if rs contains the wildcard character.
201: char wc = q.getWildcardCharacter();
202:
203: List pat = Utilities.getLikePattern(rs, String.valueOf(wc));
204:
205: return Utilities.matchLikePattern(pat, l, this .not,
206: this .ignoreCase);
207:
208: }
209:
210: /**
211: * Returns a string version of the expression.
212: * Returns in the form:
213: * <pre>
214: * {@link Expression#toString() Expression} [ NOT ] [ $ ]LIKE {@link Expression#toString() Expression}
215: * </pre>
216: *
217: * @return A string representation of the expression.
218: */
219: public String toString() {
220:
221: StringBuffer buf = new StringBuffer(this .left.toString());
222:
223: if (this .isNot()) {
224:
225: buf.append(" NOT");
226:
227: }
228:
229: buf.append(" ");
230:
231: if (this .ignoreCase) {
232:
233: buf.append("$");
234:
235: }
236:
237: buf.append("LIKE ");
238:
239: buf.append(this .right);
240:
241: if (this .isBracketed()) {
242:
243: buf.insert(0, "(");
244: buf.append(")");
245:
246: }
247:
248: return buf.toString();
249:
250: }
251:
252: }
|