001: /*
002: * PkgInvoker --
003: *
004: * This class is used for the java::* commands to gain access to
005: * package protected and protected members of a Java package.
006: *
007: * Copyright (c) 1997 Sun Microsystems, Inc.
008: *
009: * See the file "license.terms" for information on usage and
010: * redistribution of this file, and for a DISCLAIMER OF ALL
011: * WARRANTIES.
012: *
013: * RCS: @(#) $Id: PkgInvoker.java,v 1.9 2006/06/30 00:30:52 mdejong Exp $
014: *
015: */
016:
017: package tcl.lang.reflect;
018:
019: import java.lang.reflect.*;
020: import java.util.*;
021:
022: /*
023: * This class is used for the java::* commands to gain access to
024: * package protected and protected members of a Java package.
025: *
026: * Normally, the java::* command can only access public members of
027: * public classes inside any package. With the help of the PkgInvoker
028: * class, the java::* command can access any member (constructor,
029: * method, field or property) of any class in a package as long as the
030: * member is not explicitly declared "private"
031: *
032: * The ability for Tcl to access protected members is desirable when
033: * we use Tcl to perform "white-box" testing on the methods of a Java
034: * package.
035: *
036: * To grant Tcl access to protected members of a package, do the
037: * following:
038: *
039: * + Create a subclass of PkgInvoker, give it the name
040: * "TclPkgInvoker", declare it public and place it inside the
041: * said package.
042: *
043: * + Give your TclPkgInvoker class a public constructor with
044: * no arguments.
045: *
046: * + Cut-and-paste the definitions of the following functions into
047: * TclPkgInvoker class: invokeMethod, invokeConstructor,
048: * getField and setField.
049: *
050: * An example of using PkgInvoker can be found in the directory
051: * src/tests/pkg1 and the file tests/common/PkgInvoker.test
052: *
053: */
054:
055: public class PkgInvoker {
056:
057: // PkgInvokers of the packages that we have already visited are stored
058: // in this hashtable. They key is the String name of a package.
059:
060: // FIXME: There is a problem here when mutliple interps could be making
061: // use of the same cachedInvokers table. If a name conflict were
062: // encountered, incorrect result would be the result.
063: static Hashtable cachedInvokers = new Hashtable();
064:
065: // This is the default invoker to use if a package doesn't include a
066: // proper TclPkgInvoker class. This means only the public members
067: // of the public classes of that package can be accessed diretly
068: // from Tcl.
069:
070: static PkgInvoker defaultInvoker = new PkgInvoker();
071:
072: /*
073: *----------------------------------------------------------------------
074: *
075: * invokeConstructor --
076: *
077: * Invoke the given constructor with the arguments.
078: *
079: * Results:
080: * The new object instance returned by the constructor.
081: *
082: * Side effects:
083: * The constructor may have arbitraty side effects.
084: *
085: *----------------------------------------------------------------------
086: */
087:
088: public Object invokeConstructor(Constructor constructor, // The constructor to invoke.
089: Object args[]) // Arguments for the constructor.
090: throws InstantiationException, // Standard exceptions thrown by
091: IllegalAccessException, // Constructor.newInstance.
092: IllegalArgumentException, InvocationTargetException {
093: return constructor.newInstance(args);
094: }
095:
096: /*
097: *----------------------------------------------------------------------
098: *
099: * invokeMethod --
100: *
101: * Invoke the given method of the obj with the arguments.
102: *
103: * Results:
104: * The value returned by the method.
105: *
106: * Side effects:
107: * The method may have arbitraty side effects.
108: *
109: *----------------------------------------------------------------------
110: */
111:
112: public Object invokeMethod(Method method, // The method to invoke.
113: Object obj, // The object associated with the method.
114: // May be null if the method is static.
115: Object args[]) // The arguments for the method.
116: throws IllegalAccessException, // Standard exceptions throw by Method.Invoke.
117: IllegalArgumentException, InvocationTargetException {
118: return method.invoke(obj, args);
119: }
120:
121: /*
122: *----------------------------------------------------------------------
123: *
124: * getField --
125: *
126: * Query the value of the given field.
127: *
128: * Results:
129: * The value of the field.
130: *
131: * Side effects:
132: * None.
133: *
134: *----------------------------------------------------------------------
135: */
136:
137: public Object getField(Field field, // The field to query.
138: Object obj) // The object that owns the field. May be
139: // null for static fields.
140: throws IllegalArgumentException, // Standard exceptions thrown by Field.get().
141: IllegalAccessException {
142: return field.get(obj);
143: }
144:
145: /*
146: *----------------------------------------------------------------------
147: *
148: * setField --
149: *
150: * Modify the value of the given field.
151: *
152: * Results:
153: * None.
154: *
155: * Side effects:
156: * When successful, the field is modified to be the new value.
157: *
158: *----------------------------------------------------------------------
159: */
160:
161: public void setField(Field field, // The field to modify.
162: Object obj, // The object that owns the field. May be
163: // null for static fields.
164: Object value) // New value for the field.
165: throws IllegalArgumentException, // Standard exceptions thrown by Field.set().
166: IllegalAccessException {
167: field.set(obj, value);
168: }
169:
170: /*
171: *----------------------------------------------------------------------
172: *
173: * getPkgInvoker --
174: *
175: * Returns the PkgInvoker for the package that includes the given
176: * class.
177: *
178: * Results:
179: * An instance of the PkgInvoker which is included in the package,
180: * or defaultInvoker if the package doesn't include a proper
181: * PkgInvoker.
182: *
183: * Side effects:
184: * The returned value is also stored in a hashtable for faster
185: * access in the future.
186: *
187: *----------------------------------------------------------------------
188: */
189:
190: public static final PkgInvoker getPkgInvoker(Class cls) // Query the PkgInvoker of the package
191: // that owns this class.
192: {
193: String clsName = cls.getName();
194: int index = clsName.lastIndexOf('.');
195: String pkg;
196:
197: if (index == -1) {
198: pkg = "";
199: } else {
200: pkg = clsName.substring(0, index);
201: }
202:
203: PkgInvoker invoker = (PkgInvoker) cachedInvokers.get(pkg);
204: if (invoker == null) {
205: // Use the class loader that loaded the class
206: // in question. The Class.getClassLoader()
207: // API can return null to indicate the
208: // bootstrap or system loader.
209:
210: ClassLoader cloader = cls.getClassLoader();
211:
212: try {
213: if (cloader != null) {
214: Class invCls = cloader.loadClass(pkg
215: + ".TclPkgInvoker");
216: invoker = (PkgInvoker) invCls.newInstance();
217: }
218: } catch (Exception e) {
219: // The package doesn't include a PkgInvoker class. We use
220: // the default invoker, which means we can't invoke
221: // any of the protected members inside this package.
222:
223: invoker = defaultInvoker;
224: }
225:
226: if (invoker == null) {
227: invoker = defaultInvoker;
228: }
229:
230: // FIXME: should we store default pkg invoker in invoker table.
231: // Should we store all the possible package that do not have
232: // an invoker. How will we know if an earlier check failed ???
233: // This also needs to be tied into a common "cache" system that can
234: // be controlled by the user with some tcl commands.
235:
236: cachedInvokers.put(pkg, invoker);
237: }
238:
239: return invoker;
240: }
241:
242: // Return true if the passed in class uses the default invoker,
243: // meaning there is no custom invoker for the package.
244:
245: public static boolean usesDefaultInvoker(Class cls) {
246: PkgInvoker invoker = getPkgInvoker(cls);
247: return (invoker == defaultInvoker);
248: }
249:
250: // Return true if the given class is accessible,
251: // meaning it is public or it is not private
252: // and we have an invoker for the package.
253:
254: public static boolean isAccessible(Class cls) {
255: int mod = cls.getModifiers();
256: if (Modifier.isPublic(mod))
257: return true;
258: if (Modifier.isPrivate(mod))
259: return false;
260: if (usesDefaultInvoker(cls))
261: return false;
262: return true;
263: }
264:
265: // Return true if the given Method is accessible,
266: // meaning it is public or it is not private
267: // and we have an invoker for the package.
268:
269: public static boolean isAccessible(Method meth) {
270: int mod = meth.getModifiers();
271: if (Modifier.isPublic(mod))
272: return true;
273: if (Modifier.isPrivate(mod))
274: return false;
275: if (usesDefaultInvoker(meth.getDeclaringClass()))
276: return false;
277: return true;
278: }
279:
280: // Return true if the given Constructor is accessible,
281: // meaning it is public or it is not private
282: // and we have an invoker for the package.
283:
284: public static boolean isAccessible(Constructor cons) {
285: int mod = cons.getModifiers();
286: if (Modifier.isPublic(mod))
287: return true;
288: if (Modifier.isPrivate(mod))
289: return false;
290: if (usesDefaultInvoker(cons.getDeclaringClass()))
291: return false;
292: return true;
293: }
294:
295: // Return true if the given Field is accessible,
296: // meaning it is public or it is not private
297: // and we have an invoker for the package.
298:
299: public static boolean isAccessible(Field fld) {
300: int mod = fld.getModifiers();
301: if (Modifier.isPublic(mod))
302: return true;
303: if (Modifier.isPrivate(mod))
304: return false;
305: if (usesDefaultInvoker(fld.getDeclaringClass()))
306: return false;
307: return true;
308: }
309:
310: } // end PkgInvoker
|