org.python.core.JavaImportHelper Maven / Gradle / Ivy
Show all versions of jython-slim Show documentation
package org.python.core;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* Helper class handling the VM specific java package detection.
*/
public class JavaImportHelper {
private static final String DOT = ".";
/**
* Try to add the java package.
*
* This is handy in cases where the package scan cannot run, or when the initial classpath does
* not contain all .jar files (such as in J2EE containers).
*
* There is some self-healing in the sense that a correct, explicit import of a java class will
* succeed even if sys.modules already contains a Py.None entry for the corresponding java
* package.
*
* @param packageName The dotted name of the java package
* @param fromlist A tuple with the from names to import. Can be null or empty.
*
* @return true
if a java package was doubtlessly identified and added,
* false
otherwise.
*/
protected static boolean tryAddPackage(final String packageName, PyObject fromlist) {
// make sure we do not turn off the added flag, once it is set
boolean packageAdded = false;
if (packageName != null) {
// check explicit imports first (performance optimization)
// handle 'from java.net import URL' like explicit imports
List stringFromlist = getFromListAsStrings(fromlist);
for (String fromName : stringFromlist) {
if (isJavaClass(packageName, fromName)) {
packageAdded = addPackage(packageName, packageAdded);
}
}
// handle 'import java.net.URL' style explicit imports
int dotPos = packageName.lastIndexOf(DOT);
if (dotPos > 0) {
String lastDottedName = packageName.substring(dotPos + 1);
String packageCand = packageName.substring(0, dotPos);
if (isJavaClass(packageCand, lastDottedName)) {
packageAdded = addPackage(packageCand, packageAdded);
}
}
// if all else fails, check already loaded packages
if (!packageAdded) {
// build the actual map with the packages known to the VM
Map packages = buildLoadedPackages();
// add known packages
String parentPackageName = packageName;
if (isLoadedPackage(packageName, packages)) {
packageAdded = addPackage(packageName, packageAdded);
}
dotPos = 0;
do {
dotPos = parentPackageName.lastIndexOf(DOT);
if (dotPos > 0) {
parentPackageName = parentPackageName.substring(0, dotPos);
if (isLoadedPackage(parentPackageName, packages)) {
packageAdded = addPackage(parentPackageName, packageAdded);
}
}
} while (dotPos > 0);
// handle package imports like 'from java import math'
for (String fromName : stringFromlist) {
String fromPackageName = packageName + DOT + fromName;
if (isLoadedPackage(fromPackageName, packages)) {
packageAdded = addPackage(fromPackageName, packageAdded);
}
}
}
}
return packageAdded;
}
/**
* Check if a java package is already known to the VM.
*
* May return false
even if the given package name is a valid java package !
*
* @param packageName
*
* @return true
if the package with the given name is already loaded by the VM,
* false
otherwise.
*/
protected static boolean isLoadedPackage(String packageName) {
return isLoadedPackage(packageName, buildLoadedPackages());
}
/**
* Convert the fromlist into a java.lang.String based list.
*
* Do some sanity checks: filter out '*' and empty tuples, as well as non tuples.
*
* @param fromlist
* @return a list containing java.lang.String entries
*/
private static final List getFromListAsStrings(PyObject fromlist) {
List stringFromlist = new ArrayList();
if (fromlist != null && fromlist != Py.EmptyTuple && fromlist instanceof PyTuple) {
Iterator iterator = ((PyTuple) fromlist).iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
if (obj instanceof PyString) {
obj = ((PyString) obj).getString();
}
if (obj instanceof String) {
String fromName = (String) obj;
if (!"*".equals(fromName)) {
stringFromlist.add(fromName);
}
}
}
}
return stringFromlist;
}
/**
* Faster way to check if a java package is already known to the VM.
*
* May return false
even if the given package name is a valid java package !
*
* @param packageName
* @param packages A Map containing all packages actually known to the VM. Such a Map can be
* obtained using {@link JavaImportHelper.buildLoadedPackagesTree()}
*
* @return true
if the package with the given name is already loaded by the VM,
* false
otherwise.
*/
private static boolean isLoadedPackage(String javaPackageName, Map packages) {
boolean isLoaded = false;
if (javaPackageName != null) {
isLoaded = packages.containsKey(javaPackageName);
}
return isLoaded;
}
/**
* Build a Map
of the currently known packages to the VM.
*
* All parent packages appear as single entries like python modules, e.g. java
,
* java.lang
, java.lang.reflect
,
*/
private static Map buildLoadedPackages() {
TreeMap packageMap = new TreeMap();
Package[] packages = Package.getPackages();
for (int i = 0; i < packages.length; i++) {
String packageName = packages[i].getName();
packageMap.put(packageName, "");
int dotPos = 0;
do {
dotPos = packageName.lastIndexOf(DOT);
if (dotPos > 0) {
packageName = packageName.substring(0, dotPos);
packageMap.put(packageName, "");
}
} while (dotPos > 0);
}
return packageMap;
}
/**
* Try to load packageName.className and return {@code true} if successful.
*
* @return true
if the java class can be found by the current Py classloader setup
*/
private static boolean isJavaClass(String packageName, String className) {
return className != null && className.length() > 0
&& Py.findClass(packageName + "." + className) != null;
}
/**
* Add a java package to sys.modules, if not already done.
*
* @return true
if something was really added, false
otherwise
*/
private static boolean addPackage(String packageName, boolean packageAdded) {
PyObject modules = Py.getSystemState().modules;
String internedPackageName = packageName.intern();
PyObject module = modules.__finditem__(internedPackageName);
// a previously failed import could have created a Py.None entry in sys.modules
if (module == null || module == Py.None) {
int dotPos = 0;
do {
PyJavaPackage p = PySystemState.add_package(packageName);
if(dotPos == 0) {
modules.__setitem__(internedPackageName, p);
} else {
module = modules.__finditem__(internedPackageName);
if (module == null || module == Py.None) {
modules.__setitem__(internedPackageName, p);
}
}
dotPos = packageName.lastIndexOf(DOT);
if (dotPos > 0) {
packageName = packageName.substring(0, dotPos);
internedPackageName = packageName.intern();
}
} while(dotPos > 0);
// make sure not to turn off the packageAdded flag
packageAdded = true;
}
return packageAdded;
}
}