001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.lib.contract.lang.compare;
028:
029: import java.lang.reflect.Method;
030: import java.lang.reflect.Modifier;
031: import java.util.*;
032:
033: import org.cougaar.lib.contract.lang.*;
034: import org.cougaar.lib.contract.lang.op.OpCodes;
035: import org.cougaar.lib.contract.lang.op.constant.*;
036: import org.cougaar.lib.contract.lang.op.list.*;
037: import org.cougaar.lib.contract.lang.op.logical.*;
038: import org.cougaar.lib.contract.lang.op.reflect.*;
039:
040: /**
041: * Computes <tt>true</tt> if one <code>Op</code> "allows" another
042: * <code>Op</code>.
043: * <p>
044: * The following notes describe the meaning of "allows":
045: * <p>
046: * <pre>
047: * Given two Operators, A and B.
048: *
049: * Let "allows" be defined as:
050: * (A allows B) :
051: * there exists an Object <i>o</i> such that
052: * <tt>((A(<i>o</i>) == true) and (B(<i>o</i>) == true))</tt>
053: *
054: *
055: * For example:
056: *
057: * A(Object o) = { return (o instanceof String); }
058: * B(Object o) = { return ((o instanceof String) || (o instanceof List)); }
059: *
060: * then, by the definition of "allows":
061: *
062: * A implies B. (If o is a (String) then it must be a (String or List))
063: * B doesn't imply A. (Object o could be a non-String List, in which case
064: * ((B(o) == true) AND (A(o) == false)))
065: *
066: *
067: * The AND/OR relations and a small number of other Operators (NOT,
068: * INSTANCEOF) will provide most of the logic -- anything else will be
069: * compared using "equals".
070: * </pre>
071: *
072: * @see Imply which is closely related to <code>Allow</code>
073: */
074: public final class Allow implements OpCodes {
075:
076: private Allow() {
077: }
078:
079: public static final boolean compute(final Object o1, final Object o2) {
080: if (o1 instanceof Op) {
081: return compute((Op) o1, o2);
082: }
083: throw new IllegalArgumentException("Not an Op: " + o1);
084: }
085:
086: public static final boolean compute(final Op o1, final Object o2) {
087: if (o2 instanceof Op) {
088: return compute(o1, (Op) o2);
089: }
090: throw new IllegalArgumentException("Not an Op: " + o2);
091: }
092:
093: public static final boolean compute(final Op o1, final Op o2) {
094: switch (o1.getID()) {
095: // constant
096: case CONSTANT_ID:
097: return compute((ConstantOp) o1, o2);
098: case GET_ID:
099: return compute((GetOp) o1, o2);
100: // list
101: case ALL_ID:
102: return compute((AllOp) o1, o2);
103: case EMPTY_ID:
104: return compute((EmptyOp) o1, o2);
105: case EXISTS_ID:
106: return compute((ExistsOp) o1, o2);
107: // logical
108: case AND_ID:
109: return compute((AndOp) o1, o2);
110: case FALSE_ID:
111: return compute((FalseOp) o1, o2);
112: case NOT_ID:
113: return compute((NotOp) o1, o2);
114: case OR_ID:
115: return compute((OrOp) o1, o2);
116: case TRUE_ID:
117: return compute((TrueOp) o1, o2);
118: // reflect
119: case APPLY_ID:
120: return compute((ApplyOp) o1, o2);
121: case FIELD_ID:
122: return compute((FieldOp) o1, o2);
123: case INSTANCEOF_ID:
124: return compute((InstanceOfOp) o1, o2);
125: case METHOD_ID:
126: return compute((MethodOp) o1, o2);
127: case REFLECT_ID:
128: throw new UnsupportedOperationException(
129: "ReflectOps should be parsed to MethodOps/FieldOps!");
130: case THIS_ID:
131: return compute((ThisOp) o1, o2);
132: default:
133: throw new RuntimeException("Unknown Op: " + o1);
134: }
135: }
136:
137: protected static final boolean compute(final ConstantOp o1,
138: final Op o2) {
139: return (o1.equals(o2));
140: }
141:
142: protected static final boolean compute(final GetOp o1, final Op o2) {
143: return (o1.equals(o2));
144: }
145:
146: protected static final boolean compute(final AllOp o1, final Op o2) {
147: int o2ID = o2.getID();
148: if (o2ID == ALL_ID) {
149: // compare the operators of the alls
150: return compute((o1.u), (((AllOp) o2).u));
151: } else if (o2ID == INSTANCEOF_ID) {
152: // allOps only accept collections
153: return (((Type) o2).impliedBy(false, Collection.class));
154: } else {
155: return false;
156: }
157: }
158:
159: protected static final boolean compute(final EmptyOp o1, final Op o2) {
160: if (o1 == o2) {
161: // single EmptyOP
162: return true;
163: } else if (o2.getID() == INSTANCEOF_ID) {
164: // emptyOps only accept collections
165: return (((Type) o2).impliedBy(false, Collection.class));
166: } else {
167: return false;
168: }
169: }
170:
171: protected static final boolean compute(final ExistsOp o1,
172: final Op o2) {
173: int o2ID = o2.getID();
174: if (o2ID == EXISTS_ID) {
175: // compare the operators of the exists
176: return compute((o1.u), (((ExistsOp) o2).u));
177: } else if (o2ID == INSTANCEOF_ID) {
178: // existsOps only accept collections
179: return (((Type) o2).impliedBy(false, Collection.class));
180: } else {
181: return false;
182: }
183: }
184:
185: protected static final boolean compute(final AndOp o1,
186: final AndOp o2) {
187: // and1 implies and2 if all the elements of and2 are implied by some
188: // element of and1
189: Op[] ops = o1.ops;
190: Op[] xops = o2.ops;
191: int nops = ops.length;
192: int nxops = xops.length;
193: // for all and2 elements
194: for (int j = 0; j < nxops; j++) {
195: Op uj = xops[j];
196: // for all and1 elements
197: for (int i = 0;; i++) {
198: if (i >= nops) {
199: // and2[j] isn't implied by any element of and1[]
200: return false;
201: }
202: Op ui = ops[i];
203: if (compute(ui, uj)) {
204: // and1[i] implies and2[j]
205: break;
206: }
207: }
208: }
209: // all elements of and2 are implied by some and2 element,
210: // so and1 implies and2
211: return true;
212: }
213:
214: protected static final boolean compute(final AndOp o1,
215: final InstanceOfOp o2) {
216: // andOp implies typeOp if some element of andOp implies the typeOp
217: Op[] ops = o1.ops;
218: int nops = ops.length;
219: // for all and1 elements
220: System.out.println("compare and: " + o1 + " to type: " + o2);
221: for (int i = 0; i < nops; i++) {
222: Op ui = ops[i];
223: // compare andOp[i] with o2
224: if (compute(ui, o2)) {
225: System.out.println(" okay on and[" + i + "]");
226: // (andOp[i] implies typeOp), so (andOp implies typeOp)
227: return true;
228: }
229: }
230: // (!(andOp implies typeOp))
231: System.out.println(" fail");
232: return false;
233: }
234:
235: protected static final boolean compute(final AndOp o1, final Op o2) {
236: int o2ID = o2.getID();
237: if (o2ID == AND_ID) {
238: // compute two ands
239: return compute(o1, (AndOp) o2);
240: } else if (o2ID == INSTANCEOF_ID) {
241: // compute and with type
242: return compute(o1, (InstanceOfOp) o2);
243: }
244: // compare the andOp with a non-andOp/instanceOfOp
245: //
246: // see if all the elements of the andOp imply the given op
247: Op[] ops = o1.ops;
248: int nops = ops.length;
249: // for all andOp elements
250: for (int i = 0; i < nops; i++) {
251: Op ui = ops[i];
252: // compare andOp[i] with o2
253: if (!(compute(ui, o2))) {
254: // andOp[i] doesn't imply o2
255: if (ui.getID() == INSTANCEOF_ID) {
256: // see if the andOp is something like (and (isX) (X.method)),
257: // where "X.method" is non-static. The "isX" in that case
258: // is a redundant check.
259: //
260: //
261: for (int j = 0;; j++) {
262: if (j >= nops) {
263: // no matching "X.method", so (!(andOp implies o2))
264: return false;
265: }
266: Op uj = ops[j];
267: if ((uj.getID() != INSTANCEOF_ID)
268: && (compute(uj, ui))) {
269: // found matching "X.method", continue to andOp[i+1]
270: System.out.println("### ignore and[" + i
271: + "]: " + ui + " for and[" + j
272: + "]: " + ops[j]);
273: break;
274: }
275: }
276: } else {
277: // andOp doesn't imply o2
278: return false;
279: }
280: }
281: }
282: // andOp implies the given op
283: return true;
284: }
285:
286: protected static final boolean compute(final FalseOp o1, final Op o2) {
287: // single FalseOP
288: return (o1 == o2);
289: }
290:
291: protected static final boolean compute(final NotOp o1, final Op o2) {
292: if (o2.getID() == NOT_ID) {
293: // compare the operator of the nots
294: return compute((o1.u1), (((NotOp) o2).u1));
295: } else {
296: return false;
297: }
298: }
299:
300: protected static final boolean compute(final OrOp o1, final OrOp o2) {
301: // or1 implies or2 if any or1 element implies any or2 element
302: Op[] ops = o1.ops;
303: Op[] xops = o2.ops;
304: int nops = ops.length;
305: int nxops = xops.length;
306: // for all or1 elements
307: for (int i = 0; i < nops; i++) {
308: Op ui = ops[i];
309: // for all or2 elements
310: for (int j = 0; j < nxops; j++) {
311: Op uj = xops[j];
312: // compare the elements
313: if (compute(ui, uj)) {
314: // (or1[i] implies or2[j]), so (or1 implies or2)
315: return true;
316: }
317: }
318: }
319: // none of the or1 elements imply any or2 element
320: return false;
321: }
322:
323: protected static final boolean compute(final OrOp o1, final Op o2) {
324: if (o2.getID() == OR_ID) {
325: // compute two ors
326: return compute(o1, (OrOp) o2);
327: }
328: // compare the orOp with a non-orOp
329: //
330: // an orOp implies an op if all element of the orOp imply the op
331: Op[] ops = o1.ops;
332: int nops = ops.length;
333: // for all orOp elements
334: for (int i = 0; i < nops; i++) {
335: Op ui = ops[i];
336: // compare orOp[i] with op
337: if (!(compute(ui, o2))) {
338: System.out.println("Or failed on [" + i + "]: " + ui
339: + " to given " + o2);
340: // orOp[i] doesn't imply op, so (!(orOp implies op))
341: return false;
342: }
343: }
344: // orOp implies op
345: return true;
346: }
347:
348: protected static final boolean compute(final TrueOp o1, final Op o2) {
349: // single TrueOp
350: return (o1 == o2);
351: }
352:
353: protected static final boolean compute(final ApplyOp o1, final Op o2) {
354: if (o2.getID() == APPLY_ID) {
355: // apply1 is equal to apply2 if their arguments are equal
356: ApplyOp x = (ApplyOp) o2;
357: Op u1 = o1.u1;
358: Op xu1 = x.u1;
359: // test if the operators are the same
360: if (!(u1.equals(xu1))) {
361: return false;
362: }
363: Op u2 = o1.u2;
364: Op xu2 = x.u2;
365: // test if the operators imply
366: return (compute(u2, xu2));
367: } else {
368: return false;
369: }
370: }
371:
372: protected static final boolean compute(final FieldOp o1, final Op o2) {
373: return (o1.equals(o2));
374: }
375:
376: protected static final boolean compute(final InstanceOfOp o1,
377: final Op o2) {
378: int o2ID = o2.getID();
379: if (o2ID == INSTANCEOF_ID) {
380: // test the type implications of the two instanceOfOps
381: return (((Type) o1).implies((Type) o2));
382: } else if (o2ID == AND_ID) {
383: // see if this typeOp implies all the elements of the andOp
384: Op[] ops = ((AndOp) o2).ops;
385: int nops = ops.length;
386: // for all andOp elements
387: for (int i = 0; i < nops; i++) {
388: Op ui = ops[i];
389: if (!(compute(o1, ui))) {
390: return false;
391: }
392: }
393: return true;
394: } else if (o2ID == OR_ID) {
395: // see if this typeOp implies any element of the orOp
396: Op[] ops = ((OrOp) o2).ops;
397: int nops = ops.length;
398: // for all orOp elements
399: for (int i = 0; i < nops; i++) {
400: Op ui = ops[i];
401: if (compute(o1, ui)) {
402: return true;
403: }
404: }
405: return false;
406: } else {
407: return false;
408: }
409: }
410:
411: protected static final boolean compute(final MethodOp o1,
412: final Op o2) {
413: int o2ID = o2.getID();
414: if (o2ID == METHOD_ID) {
415: // see if the methods are equal
416: return (o1.equals(o2));
417: } else if (o2ID == INSTANCEOF_ID) {
418: // see if method is non-static, e.g. "String.equals", and
419: // if it implies the given type, e.g. "isString"
420: Method meth = o1.meth;
421: if ((meth.getModifiers() & Modifier.STATIC) == 0) {
422: Type t2 = (Type) o2;
423: Class mclass = meth.getDeclaringClass();
424: return (t2.impliedBy(false, mclass));
425: } else {
426: return false;
427: }
428: } else if (o2ID == AND_ID) {
429: // see if the andOp elements are all instance checks and/or
430: // this method
431: System.out.println("+compare method: " + o1 + " to and: "
432: + o2);
433: Op[] ops = ((AndOp) o2).ops;
434: int nops = ops.length;
435: // for all andOp elements
436: for (int i = 0; i < nops; i++) {
437: Op ui = ops[i];
438: if (!(compute(o1, ui))) {
439: System.out.println("+ failed on and[" + i + "]: "
440: + ui);
441: }
442: }
443: System.out.println("+ okay");
444: return true;
445: } else {
446: return false;
447: }
448: }
449:
450: protected static final boolean compute(final ThisOp o1, final Op o2) {
451: // type is context-sensitive, so just see if the
452: // other op is also a thisOp
453: return (o2.getID() == THIS_ID);
454: }
455: }
|