JavaImportCmd.java :  » Natural-Language-Processing » Ellogon » tcl » lang » Java Open Source

Java Open Source » Natural Language Processing » Ellogon 
Ellogon » tcl » lang » JavaImportCmd.java
/*
 * JavaImportCmd.java --
 *
 *  This class implements the java::import command which is used
 *  to indicate which classes will be imported. An imported class
 *  simply means that we can use the class name insteead of the
 *  full pkg.class name.
 *
 * Copyright (c) 1999 Mo DeJong.
 *
 * See the file "license.terms" for information on usage and
 * redistribution of this file, and for a DISCLAIMER OF ALL
 * WARRANTIES.
 *
 * RCS: @(#) $Id: JavaImportCmd.java,v 1.7 2006/04/13 07:36:50 mdejong Exp $
 *
 */

package tcl.lang;

import tcl.lang.reflect.PkgInvoker;

import java.util.*;


public class JavaImportCmd implements Command {

    /*
     *----------------------------------------------------------------------
     *
     * cmdProc --
     *
     *  This procedure is invoked to process the "java::import" Tcl
     *  comamnd.  See the user documentation for details on what
     *  it does.
     *
     * Results:
     *  None.
     *
     * Side effects:
     *  A standard Tcl result is stored in the interpreter.
     *
     *----------------------------------------------------------------------
     */

    public void
  cmdProc(
    Interp interp,      // Current interpreter.
    TclObject[] objv)    // Argument list.
  throws
      TclException      // A standard Tcl exception.
    {
  final boolean debug = false;

  final String usage = "java::import ?-forget? ?-package pkg? ?class ...?";

  HashMap classTable = interp.importTable[0];
  HashMap packageTable = interp.importTable[1];

  boolean forget = false;
  String pkg = null;
  TclObject import_list;
  String elem, elem2;
  int startIdx, i;

  // If there are no args simply return all the imported classes
  if (objv.length == 1) {
      import_list = TclList.newInstance();

      for ( Iterator iter = classTable.entrySet().iterator(); iter.hasNext() ; ) {
    Map.Entry entry = (Map.Entry) iter.next();
                String key = (String) entry.getKey();
                String value = (String) entry.getValue();
    TclList.append(interp, import_list,
        TclString.newInstance(value));
    if (debug) {
        System.out.println("added " + key + " : " + value);
    }
      }
      interp.setResult(import_list);
      return;
  }
  
  // See if there is a -forget argument
  startIdx = 1;
  elem = objv[startIdx].toString();
  if (elem.equals("-forget")) {
      forget = true;
      startIdx++;
  }

  // When -forget is given with no arguments, we simply
  // return. This is needed to support the following usage.
  //
  // eval {java::import -forget} [java::import]
  //
  // This happens when java::import returns the empty list

  if (startIdx >= objv.length) {
      interp.resetResult();
      return;
  }


  // Figure out if the "-package pkg" arguments are given
  elem = objv[startIdx].toString();
  if (elem.equals("-package")) {
      startIdx++;

      // "java::import -package" is not a valid usage
      // "java::import -forget -package" is not a valid usage
      if (startIdx >= objv.length) {
    throw new TclException(interp, usage);
      }
      
      pkg = objv[startIdx].toString();
      if (pkg.length() == 0) {
    throw new TclException(interp, usage);
      }
      startIdx++;
  }


  // No additional arguments means we have hit one of 
  // two conditions.
  //
  // "java::import -forget -package pkg"
  // "java::import -package pkg"

  if (startIdx >= objv.length) {
      if (forget) {
    // We must have "java::import -forget -package pkg"
    if (debug) {
        System.out.println("forget of whole package " + pkg);
    }

    // Ceck that it is not "java::import -forget" which is invalid!
    if (pkg == null) {
        throw new TclException(interp, usage);
    }

    // forget each member of the given package

    boolean found = false;

    for (Iterator iter = packageTable.entrySet().iterator(); iter.hasNext() ;) {
        Map.Entry entry = (Map.Entry) iter.next();
        elem = (String) entry.getKey();
        if (debug) {
            System.out.println("packageTable key " + elem);
        }

        if (elem.equals(pkg)) {
      // This is the package we are looking for, remove it!
      if (found) {
          throw new TclRuntimeError("unexpected : found == true");
      }
      found = true;

      // Loop over each class imported from this package
      // and remove the class and package entry.
      ArrayList alist = (ArrayList) entry.getValue();
            if (debug) {
                System.out.println("matched package to remove " + pkg);
                System.out.println("will remove each of {" + alist + "}");
            }

      for (ListIterator iter2 = alist.listIterator(); iter2.hasNext() ;) {
          // Remove imported class from the classTable
          elem2 = (String) iter2.next();
                if (debug) {
                    System.out.println("remove " + elem2 + " entry from classTable");
                }
          if (classTable.remove(elem2) == null) {
              throw new TclRuntimeError("key " + elem2 + " not in classTable");
          }
      }

      // Remove the package entry
      if (packageTable.remove(elem) == null) {
          throw new TclRuntimeError("key " + elem + " not in packageTable");
      }
        }
    }

    if (!found) {
        // It is an error to forget a package that
        // does not have any classes imported from it

        throw new TclException(interp,
            "cannot forget package \"" + pkg +
      "\", no classes were imported from it");
    }
    
    interp.resetResult();
    return;
      } else {
    if (pkg == null) {
        throw new TclRuntimeError(
            "unexpected : pkg == null");
    }

    // "java::import -package pkg" should return each imported
    // class in the given package

    for (Iterator iter = packageTable.entrySet().iterator(); iter.hasNext() ; ) {
        Map.Entry entry = (Map.Entry) iter.next();
        elem = (String) entry.getKey();

        if (elem.equals(pkg)) {
      // This is the package we are looking for.

      import_list = TclList.newInstance();

      // Loop over each class imported from this package
      ArrayList alist = (ArrayList) entry.getValue();
      for (ListIterator iter2 = alist.listIterator(); iter2.hasNext() ;) {
          // Remove imported class from the classTable
          elem2 = (String) iter2.next();

          TclList.append(interp, import_list,
              TclString.newInstance(
            (String) classTable.get(elem2)));
      }

      interp.setResult(import_list);
      return;
        }
    }

    // If we got this far then we have not imported
    // any classes from the given package, just return

    interp.resetResult();
    return;
      }
  }


  // Keep track of the classes we will import and forget about

  ArrayList importClasses = new ArrayList();
  ArrayList forgetClasses = new ArrayList();

  // Get the operation type string to be used in error messages
  String operation = "import";
  if (forget) {
      operation = "forget";
  }

  // Use TclClassLoader defined on a per-interp basis
  TclClassLoader tclClassLoader = (TclClassLoader) interp.getClassLoader();

  // Start processing class arguments begining at startIdx.

  for (i = startIdx; i < objv.length ; i++) {
      elem = objv[i].toString();

      // Check that the class name is not "" or "-forget" or "-package"
      if ((elem.length() == 0) || elem.equals("-forget")
    || elem.equals("-package")) {
    throw new TclException(interp, usage);
      }

      // Check that the class name does not have the '.'
      // char in it if the package argument was given

      if (pkg != null) {
    if (elem.indexOf('.') != -1) {
        throw new TclException(interp,
            "class argument must not contain a package specifier" +
            " when the -package pkg arguments are given");
    }
      }

      // Make sure the class is not a primitive type
      if (elem.equals("int") ||
    elem.equals("boolean") ||
    elem.equals("long") ||
    elem.equals("float") ||
    elem.equals("double") ||
    elem.equals("byte") ||
    elem.equals("short") ||
    elem.equals("char")) {
    throw new TclException(interp,
        "cannot " + operation +
        " primitive type \"" + elem + "\"");
      }

      // Create fully qualified name by combining -package argument
      // (if it was given) and the class argument

      String fullyqualified;

      if (pkg == null) {
    fullyqualified = elem;
      } else {
    fullyqualified = pkg + "." + elem;
      }
  
      // split the fullyqualified name into a package and class
      // by looking for the last '.' character in the string.
      // If there is no '.' in the string the class is in the
      // global package which is not valid.
      
      int ind = fullyqualified.lastIndexOf('.');
      if (ind == -1) {
    throw new TclException(interp,
        "cannot " + operation +
                    " from global package");
      }

      String class_package = fullyqualified.substring(0, ind);
      String class_name    = fullyqualified.substring(ind+1,
               fullyqualified.length());
      
      // Make sure the class is not in the java.lang package
      if (class_package.equals("java.lang")) {
    throw new TclException(interp,
        "cannot " + operation + " class \"" +
        fullyqualified + "\", it is in the java.lang package");
      }

      if (!forget) {
    // Make sure class is not in the global package
    // We need to test to see if the class exists only
    // when doing an import because doing a -forget will
    // only work if the class had been imported already

    boolean inGlobal = true;

    try {
        tclClassLoader.loadClass(class_name);
    } catch (ClassNotFoundException e) {
        inGlobal = false;
    } catch (PackageNameException e) {
        throw e;
    }

    if (inGlobal) {
        tclClassLoader.removeCache(class_name);

        throw new TclException(interp,
            "cannot import \"" + fullyqualified +
            "\" it conflicts with a class with the same name" +
      " in the global package");
    }

    // Make sure the class can be loaded (using the fully qualified name)

    Class c = null;
    try {
        c = tclClassLoader.loadClass(fullyqualified);

        if (!PkgInvoker.isAccessible(c)) {
            JavaInvoke.notAccessibleError(interp, c);
        }
        if (JavaInvoke.isInnerClass(c)) {
            throw new TclException(interp, "can't import an inner class");
        }
    } catch (ClassNotFoundException e) {
    } catch (PackageNameException e) {
    }

    if (c == null) {
        // Generate a specific error message for an inner class.
        // An inner class would not have been loaded by loadClass()
        // above, we need to invoke getClassByName() to find it.

        Class inner = null;
        try {
            inner = JavaInvoke.getClassByName(interp, fullyqualified);
        } catch (TclException e2) {
            // No-op
        }

        if (inner != null && JavaInvoke.isInnerClass(inner)) {
            throw new TclException(interp,
                "can't import an inner class");
        } else {
            throw new TclException(interp,
                "cannot import class \"" +
                fullyqualified +
                "\", it does not exist");
        }
    }
      }

      // When processing a -forget argument, make sure the class
      // was already imported.

      if (forget) {
    if (classTable.get(class_name) == null) {
        throw new TclException(interp,
            "cannot forget class \"" + fullyqualified +
                        "\", it was never imported");
    }
      }

      // We now know that everything was ok. Add this class
      // to the import or export list for later processing

      if (forget) {
    forgetClasses.add(fullyqualified);
      } else {
    importClasses.add(fullyqualified);
      }
  }


  // We now process the forgetClasses or the importClasses.
  // Only one of these can contain elements.

  if (forgetClasses.size() != 0 &&
      importClasses.size() != 0) {
      throw new TclRuntimeError(
                "unexpected : forgetClasses and importClasses are both nonempty");
  }

  if (forgetClasses.size() != 0) {
      if (debug) {
    System.out.print("now to forget { ");

    for (ListIterator iter = forgetClasses.listIterator(); iter.hasNext() ; ) {
        System.out.print((String) iter.next());
        System.out.print(" ");
    }

    System.out.println("}");
      }

      // Loop through each class we want to forget

      for (ListIterator iter = forgetClasses.listIterator(); iter.hasNext() ; ) {
    String fullyqualified = (String) iter.next();
    int ind = fullyqualified.lastIndexOf('.');
    if (ind == -1) {
        throw new TclRuntimeError("unexpected : no package in forget class");
    }

    String class_package = fullyqualified.substring(0, ind);
    String class_name    = fullyqualified.substring(ind+1,
                   fullyqualified.length());

    // Hash the class package key to the package list
    ArrayList class_list = (ArrayList) packageTable.get(class_package);

    // Remove the current class from the list

    int cindex = class_list.indexOf(class_name);
    if (cindex == -1) {
        throw new TclRuntimeError(
            "unexpected : class not found in package list");
    }
    if (class_list.remove(cindex) == null) {
                    throw new TclRuntimeError("could not remove element at index "
                        + cindex + " from {" + class_list + "}, class_name is " +
                        class_name);
    }

    // If there are no more classes in the list, remove the package
    if (class_list.size() == 0) {
        if (packageTable.remove(class_package) == null) {
            throw new TclRuntimeError("could not remove " + class_package +
                " from packageTable");
        }
    }

    // Remove the name -> fullyqualified entry
    if (classTable.remove(class_name) == null) {
        throw new TclRuntimeError("could not remove " + class_name +
                        " from classTable");
    }
      }
  }

  if (importClasses.size() != 0) {
      if (debug) {
    System.out.print("now to import { ");

    for (ListIterator iter = importClasses.listIterator(); iter.hasNext() ; ) {
        System.out.print((String) iter.next());
        System.out.print(" ");
    }

    System.out.println("}");
      }

      // Loop through each class we want to import

      for (ListIterator iter = importClasses.listIterator(); iter.hasNext() ; ) {
    String fullyqualified = (String) iter.next();
    int ind = fullyqualified.lastIndexOf('.');
    if (ind == -1) {
        throw new TclRuntimeError("unexpected : no package in import class");
    }

    String class_package = fullyqualified.substring(0, ind);
    String class_name    = fullyqualified.substring(ind+1,
                   fullyqualified.length());

    // If this import already exists, just continue on to the next class
    
    if (classTable.get(class_name) != null) {
        continue;
    } else {
        // We are adding a new class import

        classTable.put(class_name, fullyqualified);

        // Hash the class package key to the package list
        ArrayList class_list = (ArrayList) packageTable.get(class_package);

        if (class_list == null) {
      // A new package is being added
      class_list = new ArrayList();
      packageTable.put(class_package, class_list);
        }

        // Add the name of the class (not fully qualified) to the list
        class_list.add(class_name);
    }
      }
  }

  interp.resetResult();

  if (debug) {
      System.out.println("finished with method");
  }

  return;
    }

    // This method can be uncommented and used for debugging via:
    // java::call tcl.lang.JavaImportCmd debugPrint [java::getinterp]

/*
    public static void debugPrint(Interp interp) {
  HashMap classTable   = interp.importTable[0];
  HashMap packageTable = interp.importTable[1];

  for (Iterator iter = classTable.entrySet().iterator(); iter.hasNext() ;) {
      Map.Entry entry = (Map.Entry) iter.next();
      String elem = (String) entry.getKey();
      String value = (String) entry.getValue();
      String value2 = (String) classTable.get(elem);
      if (value != value2) {
                throw new TclRuntimeError("unexpected mismatch: \"" + value +
                    "\" != \"" + value2 + "\"");
            }

      System.out.println("classTable \"" + elem + "\" -> \"" + value + "\"");
  }

  for (Iterator iter = packageTable.entrySet().iterator(); iter.hasNext() ;) {
      Map.Entry entry = (Map.Entry) iter.next();
      String elem = (String) entry.getKey();
      ArrayList alist = (ArrayList) entry.getValue();
      ArrayList alist2 = (ArrayList) packageTable.get(elem);
      if (alist != alist2) {
                throw new TclRuntimeError("unexpected mismatch: \"" + alist +
                    "\" != \"" + alist2 + "\"");
            }

      System.out.println("packageTable " + elem + "\" -> \"" + alist);
  }

  System.out.println("Done with debug print");
    }
*/

    /*
     *----------------------------------------------------------------------
     *
     * getImport --
     *
     *  This method is invoked can be invoked to query the import
     *  system to find the fully qualified name of a class.
     *  See the user documentation for details on what it does.
     *
     * Results:
     *  Returns the fully qualified name if it was imported. If the
     *  class was not imported then null will be returned.
     *
     * Side effects:
     *  None.
     *
     *----------------------------------------------------------------------
     */

    public static String
  getImport(
    Interp interp,      // Current interpreter.
    String name)                    // Class name to qualify
    {
  HashMap classTable = interp.importTable[0];
  return (String) classTable.get(name);
    }


}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.