001: /*
002: * JavaImportCmd.java --
003: *
004: * This class implements the java::import command which is used
005: * to indicate which classes will be imported. An imported class
006: * simply means that we can use the class name insteead of the
007: * full pkg.class name.
008: *
009: * Copyright (c) 1999 Mo DeJong.
010: *
011: * See the file "license.terms" for information on usage and
012: * redistribution of this file, and for a DISCLAIMER OF ALL
013: * WARRANTIES.
014: *
015: * RCS: @(#) $Id: JavaImportCmd.java,v 1.7 2006/04/13 07:36:50 mdejong Exp $
016: *
017: */
018:
019: package tcl.lang;
020:
021: import tcl.lang.reflect.PkgInvoker;
022:
023: import java.util.*;
024:
025: public class JavaImportCmd implements Command {
026:
027: /*
028: *----------------------------------------------------------------------
029: *
030: * cmdProc --
031: *
032: * This procedure is invoked to process the "java::import" Tcl
033: * comamnd. See the user documentation for details on what
034: * it does.
035: *
036: * Results:
037: * None.
038: *
039: * Side effects:
040: * A standard Tcl result is stored in the interpreter.
041: *
042: *----------------------------------------------------------------------
043: */
044:
045: public void cmdProc(Interp interp, // Current interpreter.
046: TclObject[] objv) // Argument list.
047: throws TclException // A standard Tcl exception.
048: {
049: final boolean debug = false;
050:
051: final String usage = "java::import ?-forget? ?-package pkg? ?class ...?";
052:
053: HashMap classTable = interp.importTable[0];
054: HashMap packageTable = interp.importTable[1];
055:
056: boolean forget = false;
057: String pkg = null;
058: TclObject import_list;
059: String elem, elem2;
060: int startIdx, i;
061:
062: // If there are no args simply return all the imported classes
063: if (objv.length == 1) {
064: import_list = TclList.newInstance();
065:
066: for (Iterator iter = classTable.entrySet().iterator(); iter
067: .hasNext();) {
068: Map.Entry entry = (Map.Entry) iter.next();
069: String key = (String) entry.getKey();
070: String value = (String) entry.getValue();
071: TclList.append(interp, import_list, TclString
072: .newInstance(value));
073: if (debug) {
074: System.out.println("added " + key + " : " + value);
075: }
076: }
077: interp.setResult(import_list);
078: return;
079: }
080:
081: // See if there is a -forget argument
082: startIdx = 1;
083: elem = objv[startIdx].toString();
084: if (elem.equals("-forget")) {
085: forget = true;
086: startIdx++;
087: }
088:
089: // When -forget is given with no arguments, we simply
090: // return. This is needed to support the following usage.
091: //
092: // eval {java::import -forget} [java::import]
093: //
094: // This happens when java::import returns the empty list
095:
096: if (startIdx >= objv.length) {
097: interp.resetResult();
098: return;
099: }
100:
101: // Figure out if the "-package pkg" arguments are given
102: elem = objv[startIdx].toString();
103: if (elem.equals("-package")) {
104: startIdx++;
105:
106: // "java::import -package" is not a valid usage
107: // "java::import -forget -package" is not a valid usage
108: if (startIdx >= objv.length) {
109: throw new TclException(interp, usage);
110: }
111:
112: pkg = objv[startIdx].toString();
113: if (pkg.length() == 0) {
114: throw new TclException(interp, usage);
115: }
116: startIdx++;
117: }
118:
119: // No additional arguments means we have hit one of
120: // two conditions.
121: //
122: // "java::import -forget -package pkg"
123: // "java::import -package pkg"
124:
125: if (startIdx >= objv.length) {
126: if (forget) {
127: // We must have "java::import -forget -package pkg"
128: if (debug) {
129: System.out
130: .println("forget of whole package " + pkg);
131: }
132:
133: // Ceck that it is not "java::import -forget" which is invalid!
134: if (pkg == null) {
135: throw new TclException(interp, usage);
136: }
137:
138: // forget each member of the given package
139:
140: boolean found = false;
141:
142: for (Iterator iter = packageTable.entrySet().iterator(); iter
143: .hasNext();) {
144: Map.Entry entry = (Map.Entry) iter.next();
145: elem = (String) entry.getKey();
146: if (debug) {
147: System.out.println("packageTable key " + elem);
148: }
149:
150: if (elem.equals(pkg)) {
151: // This is the package we are looking for, remove it!
152: if (found) {
153: throw new TclRuntimeError(
154: "unexpected : found == true");
155: }
156: found = true;
157:
158: // Loop over each class imported from this package
159: // and remove the class and package entry.
160: ArrayList alist = (ArrayList) entry.getValue();
161: if (debug) {
162: System.out
163: .println("matched package to remove "
164: + pkg);
165: System.out.println("will remove each of {"
166: + alist + "}");
167: }
168:
169: for (ListIterator iter2 = alist.listIterator(); iter2
170: .hasNext();) {
171: // Remove imported class from the classTable
172: elem2 = (String) iter2.next();
173: if (debug) {
174: System.out.println("remove " + elem2
175: + " entry from classTable");
176: }
177: if (classTable.remove(elem2) == null) {
178: throw new TclRuntimeError("key "
179: + elem2 + " not in classTable");
180: }
181: }
182:
183: // Remove the package entry
184: if (packageTable.remove(elem) == null) {
185: throw new TclRuntimeError("key " + elem
186: + " not in packageTable");
187: }
188: }
189: }
190:
191: if (!found) {
192: // It is an error to forget a package that
193: // does not have any classes imported from it
194:
195: throw new TclException(
196: interp,
197: "cannot forget package \""
198: + pkg
199: + "\", no classes were imported from it");
200: }
201:
202: interp.resetResult();
203: return;
204: } else {
205: if (pkg == null) {
206: throw new TclRuntimeError(
207: "unexpected : pkg == null");
208: }
209:
210: // "java::import -package pkg" should return each imported
211: // class in the given package
212:
213: for (Iterator iter = packageTable.entrySet().iterator(); iter
214: .hasNext();) {
215: Map.Entry entry = (Map.Entry) iter.next();
216: elem = (String) entry.getKey();
217:
218: if (elem.equals(pkg)) {
219: // This is the package we are looking for.
220:
221: import_list = TclList.newInstance();
222:
223: // Loop over each class imported from this package
224: ArrayList alist = (ArrayList) entry.getValue();
225: for (ListIterator iter2 = alist.listIterator(); iter2
226: .hasNext();) {
227: // Remove imported class from the classTable
228: elem2 = (String) iter2.next();
229:
230: TclList
231: .append(
232: interp,
233: import_list,
234: TclString
235: .newInstance((String) classTable
236: .get(elem2)));
237: }
238:
239: interp.setResult(import_list);
240: return;
241: }
242: }
243:
244: // If we got this far then we have not imported
245: // any classes from the given package, just return
246:
247: interp.resetResult();
248: return;
249: }
250: }
251:
252: // Keep track of the classes we will import and forget about
253:
254: ArrayList importClasses = new ArrayList();
255: ArrayList forgetClasses = new ArrayList();
256:
257: // Get the operation type string to be used in error messages
258: String operation = "import";
259: if (forget) {
260: operation = "forget";
261: }
262:
263: // Use TclClassLoader defined on a per-interp basis
264: TclClassLoader tclClassLoader = (TclClassLoader) interp
265: .getClassLoader();
266:
267: // Start processing class arguments begining at startIdx.
268:
269: for (i = startIdx; i < objv.length; i++) {
270: elem = objv[i].toString();
271:
272: // Check that the class name is not "" or "-forget" or "-package"
273: if ((elem.length() == 0) || elem.equals("-forget")
274: || elem.equals("-package")) {
275: throw new TclException(interp, usage);
276: }
277:
278: // Check that the class name does not have the '.'
279: // char in it if the package argument was given
280:
281: if (pkg != null) {
282: if (elem.indexOf('.') != -1) {
283: throw new TclException(
284: interp,
285: "class argument must not contain a package specifier"
286: + " when the -package pkg arguments are given");
287: }
288: }
289:
290: // Make sure the class is not a primitive type
291: if (elem.equals("int") || elem.equals("boolean")
292: || elem.equals("long") || elem.equals("float")
293: || elem.equals("double") || elem.equals("byte")
294: || elem.equals("short") || elem.equals("char")) {
295: throw new TclException(interp, "cannot " + operation
296: + " primitive type \"" + elem + "\"");
297: }
298:
299: // Create fully qualified name by combining -package argument
300: // (if it was given) and the class argument
301:
302: String fullyqualified;
303:
304: if (pkg == null) {
305: fullyqualified = elem;
306: } else {
307: fullyqualified = pkg + "." + elem;
308: }
309:
310: // split the fullyqualified name into a package and class
311: // by looking for the last '.' character in the string.
312: // If there is no '.' in the string the class is in the
313: // global package which is not valid.
314:
315: int ind = fullyqualified.lastIndexOf('.');
316: if (ind == -1) {
317: throw new TclException(interp, "cannot " + operation
318: + " from global package");
319: }
320:
321: String class_package = fullyqualified.substring(0, ind);
322: String class_name = fullyqualified.substring(ind + 1,
323: fullyqualified.length());
324:
325: // Make sure the class is not in the java.lang package
326: if (class_package.equals("java.lang")) {
327: throw new TclException(interp, "cannot " + operation
328: + " class \"" + fullyqualified
329: + "\", it is in the java.lang package");
330: }
331:
332: if (!forget) {
333: // Make sure class is not in the global package
334: // We need to test to see if the class exists only
335: // when doing an import because doing a -forget will
336: // only work if the class had been imported already
337:
338: boolean inGlobal = true;
339:
340: try {
341: tclClassLoader.loadClass(class_name);
342: } catch (ClassNotFoundException e) {
343: inGlobal = false;
344: } catch (PackageNameException e) {
345: throw e;
346: }
347:
348: if (inGlobal) {
349: tclClassLoader.removeCache(class_name);
350:
351: throw new TclException(
352: interp,
353: "cannot import \""
354: + fullyqualified
355: + "\" it conflicts with a class with the same name"
356: + " in the global package");
357: }
358:
359: // Make sure the class can be loaded (using the fully qualified name)
360:
361: Class c = null;
362: try {
363: c = tclClassLoader.loadClass(fullyqualified);
364:
365: if (!PkgInvoker.isAccessible(c)) {
366: JavaInvoke.notAccessibleError(interp, c);
367: }
368: if (JavaInvoke.isInnerClass(c)) {
369: throw new TclException(interp,
370: "can't import an inner class");
371: }
372: } catch (ClassNotFoundException e) {
373: } catch (PackageNameException e) {
374: }
375:
376: if (c == null) {
377: // Generate a specific error message for an inner class.
378: // An inner class would not have been loaded by loadClass()
379: // above, we need to invoke getClassByName() to find it.
380:
381: Class inner = null;
382: try {
383: inner = JavaInvoke.getClassByName(interp,
384: fullyqualified);
385: } catch (TclException e2) {
386: // No-op
387: }
388:
389: if (inner != null && JavaInvoke.isInnerClass(inner)) {
390: throw new TclException(interp,
391: "can't import an inner class");
392: } else {
393: throw new TclException(interp,
394: "cannot import class \""
395: + fullyqualified
396: + "\", it does not exist");
397: }
398: }
399: }
400:
401: // When processing a -forget argument, make sure the class
402: // was already imported.
403:
404: if (forget) {
405: if (classTable.get(class_name) == null) {
406: throw new TclException(interp,
407: "cannot forget class \"" + fullyqualified
408: + "\", it was never imported");
409: }
410: }
411:
412: // We now know that everything was ok. Add this class
413: // to the import or export list for later processing
414:
415: if (forget) {
416: forgetClasses.add(fullyqualified);
417: } else {
418: importClasses.add(fullyqualified);
419: }
420: }
421:
422: // We now process the forgetClasses or the importClasses.
423: // Only one of these can contain elements.
424:
425: if (forgetClasses.size() != 0 && importClasses.size() != 0) {
426: throw new TclRuntimeError(
427: "unexpected : forgetClasses and importClasses are both nonempty");
428: }
429:
430: if (forgetClasses.size() != 0) {
431: if (debug) {
432: System.out.print("now to forget { ");
433:
434: for (ListIterator iter = forgetClasses.listIterator(); iter
435: .hasNext();) {
436: System.out.print((String) iter.next());
437: System.out.print(" ");
438: }
439:
440: System.out.println("}");
441: }
442:
443: // Loop through each class we want to forget
444:
445: for (ListIterator iter = forgetClasses.listIterator(); iter
446: .hasNext();) {
447: String fullyqualified = (String) iter.next();
448: int ind = fullyqualified.lastIndexOf('.');
449: if (ind == -1) {
450: throw new TclRuntimeError(
451: "unexpected : no package in forget class");
452: }
453:
454: String class_package = fullyqualified.substring(0, ind);
455: String class_name = fullyqualified.substring(ind + 1,
456: fullyqualified.length());
457:
458: // Hash the class package key to the package list
459: ArrayList class_list = (ArrayList) packageTable
460: .get(class_package);
461:
462: // Remove the current class from the list
463:
464: int cindex = class_list.indexOf(class_name);
465: if (cindex == -1) {
466: throw new TclRuntimeError(
467: "unexpected : class not found in package list");
468: }
469: if (class_list.remove(cindex) == null) {
470: throw new TclRuntimeError(
471: "could not remove element at index "
472: + cindex + " from {" + class_list
473: + "}, class_name is " + class_name);
474: }
475:
476: // If there are no more classes in the list, remove the package
477: if (class_list.size() == 0) {
478: if (packageTable.remove(class_package) == null) {
479: throw new TclRuntimeError("could not remove "
480: + class_package + " from packageTable");
481: }
482: }
483:
484: // Remove the name -> fullyqualified entry
485: if (classTable.remove(class_name) == null) {
486: throw new TclRuntimeError("could not remove "
487: + class_name + " from classTable");
488: }
489: }
490: }
491:
492: if (importClasses.size() != 0) {
493: if (debug) {
494: System.out.print("now to import { ");
495:
496: for (ListIterator iter = importClasses.listIterator(); iter
497: .hasNext();) {
498: System.out.print((String) iter.next());
499: System.out.print(" ");
500: }
501:
502: System.out.println("}");
503: }
504:
505: // Loop through each class we want to import
506:
507: for (ListIterator iter = importClasses.listIterator(); iter
508: .hasNext();) {
509: String fullyqualified = (String) iter.next();
510: int ind = fullyqualified.lastIndexOf('.');
511: if (ind == -1) {
512: throw new TclRuntimeError(
513: "unexpected : no package in import class");
514: }
515:
516: String class_package = fullyqualified.substring(0, ind);
517: String class_name = fullyqualified.substring(ind + 1,
518: fullyqualified.length());
519:
520: // If this import already exists, just continue on to the next class
521:
522: if (classTable.get(class_name) != null) {
523: continue;
524: } else {
525: // We are adding a new class import
526:
527: classTable.put(class_name, fullyqualified);
528:
529: // Hash the class package key to the package list
530: ArrayList class_list = (ArrayList) packageTable
531: .get(class_package);
532:
533: if (class_list == null) {
534: // A new package is being added
535: class_list = new ArrayList();
536: packageTable.put(class_package, class_list);
537: }
538:
539: // Add the name of the class (not fully qualified) to the list
540: class_list.add(class_name);
541: }
542: }
543: }
544:
545: interp.resetResult();
546:
547: if (debug) {
548: System.out.println("finished with method");
549: }
550:
551: return;
552: }
553:
554: // This method can be uncommented and used for debugging via:
555: // java::call tcl.lang.JavaImportCmd debugPrint [java::getinterp]
556:
557: /*
558: public static void debugPrint(Interp interp) {
559: HashMap classTable = interp.importTable[0];
560: HashMap packageTable = interp.importTable[1];
561:
562: for (Iterator iter = classTable.entrySet().iterator(); iter.hasNext() ;) {
563: Map.Entry entry = (Map.Entry) iter.next();
564: String elem = (String) entry.getKey();
565: String value = (String) entry.getValue();
566: String value2 = (String) classTable.get(elem);
567: if (value != value2) {
568: throw new TclRuntimeError("unexpected mismatch: \"" + value +
569: "\" != \"" + value2 + "\"");
570: }
571:
572: System.out.println("classTable \"" + elem + "\" -> \"" + value + "\"");
573: }
574:
575: for (Iterator iter = packageTable.entrySet().iterator(); iter.hasNext() ;) {
576: Map.Entry entry = (Map.Entry) iter.next();
577: String elem = (String) entry.getKey();
578: ArrayList alist = (ArrayList) entry.getValue();
579: ArrayList alist2 = (ArrayList) packageTable.get(elem);
580: if (alist != alist2) {
581: throw new TclRuntimeError("unexpected mismatch: \"" + alist +
582: "\" != \"" + alist2 + "\"");
583: }
584:
585: System.out.println("packageTable " + elem + "\" -> \"" + alist);
586: }
587:
588: System.out.println("Done with debug print");
589: }
590: */
591:
592: /*
593: *----------------------------------------------------------------------
594: *
595: * getImport --
596: *
597: * This method is invoked can be invoked to query the import
598: * system to find the fully qualified name of a class.
599: * See the user documentation for details on what it does.
600: *
601: * Results:
602: * Returns the fully qualified name if it was imported. If the
603: * class was not imported then null will be returned.
604: *
605: * Side effects:
606: * None.
607: *
608: *----------------------------------------------------------------------
609: */
610:
611: public static String getImport(Interp interp, // Current interpreter.
612: String name) // Class name to qualify
613: {
614: HashMap classTable = interp.importTable[0];
615: return (String) classTable.get(name);
616: }
617:
618: }
|