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.op.reflect;
028:
029: import java.util.*;
030: import java.lang.reflect.*;
031:
032: import org.cougaar.lib.contract.lang.*;
033: import org.cougaar.lib.contract.lang.cache.*;
034: import org.cougaar.lib.contract.lang.op.OpCodes;
035:
036: /**
037: * Reflect <code>Op</code>, which eventually becomes either a
038: * <code>MethodOp</code> or <code>FieldOp</code>.
039: **/
040:
041: public final class ReflectOp extends OpImpl {
042:
043: /** optional "Class" **/
044: private Class clazz;
045:
046: /** optional method Class args **/
047: private Class[] methArgs;
048:
049: /** non-optional reference name **/
050: private String refName;
051:
052: private ReflectOp() {
053: }
054:
055: public ReflectOp(String s) {
056: // get optional class
057: int iClassSep = s.indexOf(':');
058: while (iClassSep == 0) {
059: // s can (rarely) start with an optional ':', which is useful if
060: // the method/field name matches an Op keyword, e.g. "X.and()"
061: //
062: // also note the use of ':' as a class/ref separator is awkward
063: // and will likely be redone to use the last "." before the possible
064: // "-".
065: s = s.substring(1);
066: iClassSep = s.indexOf(':');
067: }
068: if (iClassSep < 0) {
069: // class not specified
070: this .clazz = null;
071: iClassSep = 0;
072: } else {
073: // get the class
074: String cname = s.substring(0, iClassSep);
075: iClassSep++;
076: this .clazz = ClassCache.lookup(cname);
077: if (this .clazz == null) {
078: throw new IllegalArgumentException(
079: "Unknown class name: " + cname + " given in: "
080: + s);
081: }
082: }
083:
084: int iMethSep = s.indexOf('-', iClassSep);
085: if (iMethSep < 0) {
086: // method arguments not specified -- may be field reference
087: this .refName = s.substring(iClassSep);
088: this .methArgs = null;
089: } else {
090: // fully-qualified method
091: this .refName = s.substring(iClassSep, iMethSep);
092: // count the args
093: int nargs = 1;
094: int j = iMethSep;
095: while (true) {
096: j = s.indexOf(':', j + 1);
097: if (j < 0) {
098: break;
099: }
100: nargs++;
101: }
102: // add the args
103: if ((nargs == 1) && (iMethSep == (s.length() - 1))) {
104: // zero args
105: this .methArgs = new Class[0];
106: } else {
107: this .methArgs = new Class[nargs];
108: j = iMethSep + 1;
109: for (int x = 0; x < nargs; x++) {
110: int k = s.indexOf(':', j);
111: if (k < 0) {
112: k = s.length();
113: }
114: String acname = s.substring(j, k);
115: Class ac = ClassCache.lookup(acname);
116: if (ac == null) {
117: // unknown class
118: throw new IllegalArgumentException(
119: "Unknown method argument class name["
120: + x + "]: " + acname
121: + " given in: " + s);
122: }
123: this .methArgs[x] = ac;
124: j = k + 1;
125: }
126: }
127: }
128: }
129:
130: public final int getID() {
131: return OpCodes.REFLECT_ID;
132: }
133:
134: public final Op parse(final OpParser p) throws ParseException {
135:
136: // FIXME taking first matching method, which might be overridden
137: // for a better match in some other TypeList entry!
138:
139: // set method using class in TypeList
140: TypeList sharedTypeList = p.getTypeList();
141:
142: Op op;
143:
144: // check known types
145: List knownTypes = sharedTypeList.getKnownTypes();
146: int nTypes = knownTypes.size();
147: for (int i = 0; i < nTypes; i++) {
148: org.cougaar.lib.contract.lang.Type ti = (org.cougaar.lib.contract.lang.Type) knownTypes
149: .get(i);
150: if (!(ti.isNot())) {
151: op = tryRef(ti.getClazz(), false, p);
152: if (op != null) {
153: return op;
154: }
155: }
156: }
157:
158: // check assumed class
159: Class assumedClass = sharedTypeList.getWanted().getClazz();
160: op = tryRef(assumedClass, false, p);
161: if (op != null) {
162: return op;
163: }
164:
165: // check for Object method/field common to all interfaces, e.g. equals
166: if (assumedClass.isInterface()) {
167: op = tryRef(Object.class, false, p);
168: if (op != null) {
169: return op;
170: }
171: }
172:
173: // check for static method/field
174: if (clazz != null) {
175: op = tryRef(clazz, true, p);
176: if (op != null) {
177: return op;
178: }
179: }
180:
181: // method/field not found
182: throw new ParseException("Unknown method/field \"" + refName
183: + "\"\nCurrent " + sharedTypeList);
184: }
185:
186: /**
187: * Look for <tt>refName</tt> in <tt>cl</tt>.
188: */
189: private final Op tryRef(Class cl, boolean isStatic, final OpParser p)
190: throws ParseException {
191: // check the class
192: if ((clazz == null) || (clazz == cl)) {
193: // keep cl
194: } else if (clazz.isAssignableFrom(cl)) {
195: // clazz is superclass of cl -- use superclass
196: cl = clazz;
197: } else {
198: // non-compatible classes
199: return null;
200: }
201: // look for matching method
202: Object kmi;
203: if (methArgs == null) {
204: kmi = MethodCache.lookup(cl, refName, isStatic);
205: } else {
206: kmi = MethodCache.lookup(cl, refName, methArgs, isStatic);
207: }
208: if (kmi != null) {
209: // create method op
210: MethodOp m = new MethodOp();
211: if (kmi instanceof Method) {
212: return m.parseMethod((Method) kmi, p);
213: } else {
214: return m.parseMethod((List) kmi, p);
215: }
216: }
217: if (methArgs == null) {
218: // look for matching field
219: Field kfi = FieldCache.lookup(cl, refName, isStatic);
220: if (kfi != null) {
221: // create field op
222: return (new FieldOp()).parseField(kfi, p);
223: }
224: }
225: // not listed
226: return null;
227: }
228:
229: /**
230: * Format <code>Method</code> to <code>ReflectOp</code> constructor-style.
231: */
232: public static final String toString(Method meth, boolean verbose) {
233: if (!verbose) {
234: return meth.getName();
235: } else {
236: StringBuffer sb = new StringBuffer();
237: Class declClass = meth.getDeclaringClass();
238:
239: // append package
240: String cname = ClassCache.toString(declClass);
241: sb.append(cname).append(":");
242:
243: // append method
244: String mname = meth.getName();
245: sb.append(mname);
246:
247: // check to see if arguments are needed
248: Method[] allMeths = declClass.getMethods();
249: boolean seen = false;
250: for (int i = allMeths.length - 1;; i--) {
251: if (i < 0) {
252: // don't need arguments
253: return sb.toString();
254: }
255: Method m = allMeths[i];
256: if (mname.equals(m.getName())) {
257: if (seen) {
258: // needs arguments
259: break;
260: } else {
261: // look for duplicate
262: seen = true;
263: }
264: }
265: }
266:
267: // append arguments
268: appendArgs(sb, meth.getParameterTypes());
269:
270: return sb.toString();
271: }
272: }
273:
274: /**
275: * Format <code>Field</code> to <code>ReflectOp</code> constructor-style.
276: */
277: public static final String toString(Field field, boolean verbose) {
278: // get field name
279: String fname = field.getName();
280: if (!verbose && ((field.getModifiers() & Modifier.STATIC) == 0)) {
281: return fname;
282: } else {
283: // get package
284: Class declClass = field.getDeclaringClass();
285: String cname = ClassCache.toString(declClass);
286: // return fully-specified field
287: return cname + ":" + fname;
288: }
289: }
290:
291: /**
292: * Format <code>this</code> to <code>ReflectOp</code> constructor-style.
293: */
294: public String getReflectString(boolean verbose) {
295: if (!verbose) {
296: return refName;
297: } else {
298: StringBuffer sb = new StringBuffer();
299: if (clazz != null) {
300: sb.append(clazz.getName()).append(":");
301: }
302: sb.append(refName);
303: if (methArgs != null) {
304: appendArgs(sb, methArgs);
305: }
306: return sb.toString();
307: }
308: }
309:
310: /**
311: * Format <code>Class[]</code> Method arguments to
312: * <code>ReflectOp</code> constructor-style.
313: */
314: private static final void appendArgs(StringBuffer sb, Class[] margs) {
315: sb.append("-");
316: if (margs.length > 0) {
317: int lastI = margs.length - 1;
318: for (int i = 0; i < lastI; i++) {
319: sb.append(margs[i].getName()).append(":");
320: }
321: sb.append(margs[lastI].getName());
322: }
323: }
324:
325: /**
326: * <tt>parse</tt> should return <code>MethodOp</code> or
327: * <code>FieldOp</code> -- all non-<tt>parse</tt> methods should
328: * not be called!
329: */
330: public final boolean isReturnBoolean() {
331: throw new UnsupportedOperationException();
332: }
333:
334: public final Class getReturnClass() {
335: throw new UnsupportedOperationException();
336: }
337:
338: public final boolean execute(final Object o) {
339: throw new UnsupportedOperationException();
340: }
341:
342: public final Object operate(final Object o) {
343: throw new UnsupportedOperationException();
344: }
345:
346: public final void setConst(final String key, final Object val) {
347: throw new UnsupportedOperationException();
348: }
349:
350: public final void accept(TreeVisitor visitor) {
351: // (reflectop)
352: visitor.visitWord(getReflectString(visitor.isVerbose()));
353: visitor.visitEnd();
354: }
355: }
|