001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.jdbc.kernel.exps;
020:
021: import java.util.Map;
022:
023: import org.apache.openjpa.jdbc.schema.Column;
024: import org.apache.openjpa.jdbc.sql.SQLBuffer;
025: import org.apache.openjpa.jdbc.sql.Select;
026: import org.apache.openjpa.kernel.exps.ExpressionVisitor;
027: import serp.util.Strings;
028:
029: /**
030: * Test if a string matches a regexp.
031: *
032: * @author Abe White
033: */
034: class MatchesExpression implements Exp {
035:
036: private final Val _val;
037: private final Const _const;
038: private final String _single;
039: private final String _multi;
040: private final String _escape;
041:
042: /**
043: * Constructor. Supply values.
044: */
045: public MatchesExpression(Val val, Const con, String single,
046: String multi, String escape) {
047: _val = val;
048: _const = con;
049: _single = single;
050: _multi = multi;
051: _escape = escape;
052: }
053:
054: public ExpState initialize(Select sel, ExpContext ctx, Map contains) {
055: ExpState s1 = _val.initialize(sel, ctx, 0);
056: ExpState s2 = _const.initialize(sel, ctx, 0);
057: return new BinaryOpExpState(sel.and(s1.joins, s2.joins), s1, s2);
058: }
059:
060: public void appendTo(Select sel, ExpContext ctx, ExpState state,
061: SQLBuffer buf) {
062: BinaryOpExpState bstate = (BinaryOpExpState) state;
063: _val.calculateValue(sel, ctx, bstate.state1, _const,
064: bstate.state2);
065: _const.calculateValue(sel, ctx, bstate.state2, _val,
066: bstate.state1);
067:
068: Column col = null;
069: if (_val instanceof PCPath) {
070: Column[] cols = ((PCPath) _val).getColumns(bstate.state1);
071: if (cols.length == 1)
072: col = cols[0];
073: }
074:
075: Object o = _const.getValue(ctx, bstate.state2);
076: if (o == null)
077: buf.append("1 <> 1");
078: else {
079: // look for ignore case flag and strip it out if present
080: boolean ignoreCase = false;
081: String str = o.toString();
082: int idx = str.indexOf("(?i)");
083: if (idx != -1) {
084: ignoreCase = true;
085: if (idx + 4 < str.length())
086: str = str.substring(0, idx)
087: + str.substring(idx + 4);
088: else
089: str = str.substring(0, idx);
090: str = str.toLowerCase();
091: }
092:
093: // append target
094: if (ignoreCase)
095: buf.append("LOWER(");
096: _val.appendTo(sel, ctx, bstate.state1, buf, 0);
097: if (ignoreCase)
098: buf.append(")");
099:
100: // create a DB wildcard string by replacing the
101: // multi token (e.g., '.*') and the single token (e.g., ".")
102: // with '%' and '.' with '_'
103: str = replaceEscape(str, _multi, "%", _escape);
104: str = replaceEscape(str, _single, "_", _escape);
105: buf.append(" LIKE ").appendValue(str, col);
106:
107: // escape out characters by using the database's escape sequence
108: if (_escape != null)
109: buf.append(" ESCAPE '").append(_escape).append("'");
110: }
111: sel.append(buf, state.joins);
112: }
113:
114: /**
115: * Perform a string replacement with simplistic escape handing.
116: *
117: * @param str the source string
118: * @param from the string to find
119: * @param to the string to replace
120: * @param escape the string to use to escape replacement
121: * @return the replaced string
122: */
123: private static String replaceEscape(String str, String from,
124: String to, String escape) {
125: String[] parts = Strings.split(str, from, Integer.MAX_VALUE);
126: StringBuffer repbuf = new StringBuffer();
127: for (int i = 0; i < parts.length; i++) {
128: if (i > 0) {
129: // if the previous part ended with an escape character, then
130: // escape the character and remove the previous escape;
131: // this doesn't support any double-escaping or other more
132: // sophisticated features
133: if (!from.equals(to) && parts[i - 1].endsWith(escape)) {
134: repbuf.setLength(repbuf.length() - 1);
135: repbuf.append(from);
136: } else
137: repbuf.append(to);
138: }
139: repbuf.append(parts[i]);
140: }
141: return repbuf.toString();
142: }
143:
144: public void selectColumns(Select sel, ExpContext ctx,
145: ExpState state, boolean pks) {
146: BinaryOpExpState bstate = (BinaryOpExpState) state;
147: _val.selectColumns(sel, ctx, bstate.state1, true);
148: _const.selectColumns(sel, ctx, bstate.state2, true);
149: }
150:
151: public void acceptVisit(ExpressionVisitor visitor) {
152: visitor.enter(this);
153: _val.acceptVisit(visitor);
154: _const.acceptVisit(visitor);
155: visitor.exit(this);
156: }
157: }
|