001: /*
002: * Copyright (C) 1999-2004 <a href="mailto:mandarax@jbdietrich.com">Jens Dietrich</a>
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package org.mandarax.jdbc.server.sql;
020:
021: import java.util.*;
022: import org.mandarax.kernel.InferenceException;
023: import org.mandarax.util.resultsetfilters.ResultSetCondition;
024:
025: /**
026: * Represents a compound condition (conditions connected by AND or OR).
027: * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
028: * @version 3.3.2 <29 December 2004>
029: * @since 3.0
030: */
031:
032: public class CompoundCondition extends Condition implements
033: ConditionContext {
034: public static final int AND = 0;
035: public static final int OR = 1;
036: protected List conditions = new ArrayList();
037: protected int connective = -1;
038: private boolean negateNext = false;
039:
040: /**
041: * Constructor.
042: * @param connective the connective (see the respective class constants)
043: * @param condition1 the first sub condition
044: * @param condition2 the second subcondition
045: */
046: public CompoundCondition(int connective, Condition condition1,
047: Condition condition2) {
048: super ();
049: add(connective);
050: add(condition1);
051: add(condition2);
052: }
053:
054: /**
055: * Constructor.
056: * @param connective the connective (see the respective class constants)
057:
058: */
059: public CompoundCondition(int connective) {
060: super ();
061: add(connective);
062: }
063:
064: /**
065: * Constructor.
066:
067: */
068: public CompoundCondition() {
069: super ();
070: }
071:
072: /**
073: * Compares objects.
074: * @param obj another object.
075: * @return a boolean
076: */
077: public boolean sameAs(Object obj) {
078: if (obj != null && obj instanceof CompoundCondition) {
079: CompoundCondition s = (CompoundCondition) obj;
080: boolean result = connective == s.connective;
081: result = result
082: && conditions.size() == ((s.conditions.size()));
083: if (result) {
084: for (int i = 0; i < conditions.size(); i++) {
085: result = result
086: && ((Condition) conditions.get(i))
087: .sameAs((Condition) s.conditions
088: .get(i));
089: }
090: }
091: return result;
092: }
093: return false;
094: }
095:
096: /**
097: * Gather the host variables.
098: * @param variables the list used to collect the variables
099: */
100: public void prepare(java.util.List variables) {
101: for (int i = 0; i < conditions.size(); i++)
102: ((Condition) conditions.get(i)).prepare(variables);
103: }
104:
105: /**
106: * Add a condition to this context.
107: * @param condition a condition
108: */
109: public void add(Condition condition) {
110: conditions.add(condition);
111: if (negateNext)
112: condition.setNegated(true);
113: negateNext = false;
114: condition.setOwner(this );
115:
116: }
117:
118: /**
119: * Add a connective to this context.
120: * @param connective a connective
121: */
122: public void add(int connective) {
123: if (this .connective != -1 && this .connective != connective)
124: throw new IllegalArgumentException(
125: "Use brackets when mixing AND and OR");
126: this .connective = connective;
127:
128: }
129:
130: /**
131: * Set the conditions (and connectives)
132: * @param newConditions a list of conditions
133: * @param newConnective a new connective
134: */
135: public void setConditions(List newConditions, int newConnective) {
136: conditions = newConditions;
137: for (int i = 0; i < conditions.size(); i++) {
138: ((Condition) conditions.get(i)).setOwner(this );
139: }
140: connective = newConnective;
141: }
142:
143: /**
144: * Normalize the object.
145: * TODO full normalization using deMorgan's rules
146: * @param typesByColumn associations between column names and (SQL) types
147: */
148: public void normalize(Map typesByColumn) {
149: if (conditions.size() == 1) {
150: Condition c = (Condition) conditions.get(0);
151: // synchronize negation status - this is a hack - TODO improve
152: // if (!(this instanceof WhereClause)) c.setNegated(this.isNegated());
153: if (context == null) {
154: // root !!
155: if (c instanceof CompoundCondition) {
156: setConditions(((CompoundCondition) c).conditions,
157: ((CompoundCondition) c).connective);
158: this .setNegated(c.isNegated());
159: }
160: } else {
161: // replace this object by the only child maintain "negated" state
162: c.setNegated(this .isNegated());
163: context.replace(this , c);
164: }
165: }
166:
167: for (int i = 0; i < conditions.size(); i++) {
168: ((Condition) conditions.get(i)).normalize(typesByColumn);
169: }
170:
171: }
172:
173: /**
174: * Replace a child condition.
175: * @param oldCondition the old condition
176: * @param newCondition the new condition
177: */
178: public void replace(Condition oldCondition, Condition newCondition) {
179: for (int i = 0; i < conditions.size(); i++) {
180: Condition c = (Condition) conditions.get(i);
181: if (c == oldCondition) {
182: conditions.remove(i);
183: conditions.add(i, newCondition);
184: }
185: }
186: }
187:
188: /**
189: * Negate the condition that will be added next.
190: */
191: public void negateNext() {
192: negateNext = true;
193: }
194:
195: /**
196: * Set variable names / variable associations.
197: * @param variablesByNames a map containing variable name -> variable term associations
198: */
199: void setVariablesByName(Map variablesByName) {
200: for (int i = 0; i < conditions.size(); i++) {
201: ((Condition) conditions.get(i))
202: .setVariablesByName(variablesByName);
203: }
204: }
205:
206: /**
207: * Indicates whether the condition is true for an mandarax result set.
208: * @param rs a mandarax result set
209: * @return a boolean
210: */
211: public boolean isSatisfiedBy(org.mandarax.kernel.ResultSet rs)
212: throws InferenceException {
213: if (conditions.size() == 1)
214: return negated != ((Condition) conditions.get(0))
215: .isSatisfiedBy(rs);
216: if (connective == AND) {
217: boolean result = true;
218: for (int i = 0; i < conditions.size(); i++) {
219: result = result
220: && ((ResultSetCondition) conditions.get(i))
221: .isSatisfiedBy(rs);
222: }
223: return negated != result;
224: } else if (connective == OR) {
225: boolean result = false;
226: for (int i = 0; i < conditions.size(); i++) {
227: result = result
228: || ((ResultSetCondition) conditions.get(i))
229: .isSatisfiedBy(rs);
230: }
231: return negated != result;
232: }
233: throw new InferenceException(
234: "Cannot evaluate compound condition with unknown connective "
235: + connective);
236: }
237:
238: /**
239: * Indicates whether this condition is empty (=does not have children).
240: * @return a boolean
241: */
242: boolean isEmpty() {
243: return conditions == null || conditions.size() == 0;
244: }
245:
246: /**
247: * Print the object on a buffer in order to display the parsed SQL.
248: * @param out a string bufer to print on
249: */
250: public void print(StringBuffer out) {
251: if (this .isNegated())
252: out.append("NOT (");
253: for (int i = 0; i < conditions.size(); i++) {
254: if (i > 0)
255: out.append(connective == AND ? " AND " : " OR ");
256: Condition condition = (Condition) conditions.get(i);
257: if (condition instanceof CompoundCondition)
258: out.append('(');
259: condition.print(out);
260: if (condition instanceof CompoundCondition)
261: out.append(')');
262: }
263: if (this .isNegated())
264: out.append(')');
265: }
266:
267: }
|