001: package org.python.core;
002:
003: import java.util.ArrayList;
004: import java.util.Iterator;
005: import java.util.List;
006: import java.util.Map;
007: import java.util.TreeMap;
008:
009: /**
010: * Helper class handling the VM specific java package detection.
011: */
012: public class JavaImportHelper {
013:
014: private static final String DOT = ".";
015:
016: /**
017: * Try to add the java package.
018: * <p>
019: * This is handy in cases where the package scan cannot run, or when the initial classpath does not contain all .jar
020: * files (such as in J2EE containers).
021: * <p>
022: * There is some self-healing in the sense that a correct, explicit import of a java class will succeed even if
023: * sys.modules already contains a Py.None entry for the corresponding java package.
024: *
025: * @param packageName The dotted name of the java package
026: * @param fromlist A tuple with the from names to import. Can be null or empty.
027: *
028: * @return <code>true</code> if a java package was doubtlessly identified and added, <code>false</code>
029: * otherwise.
030: */
031: protected static boolean tryAddPackage(final String packageName,
032: PyObject fromlist) {
033: // make sure we do not turn off the added flag, once it is set
034: boolean packageAdded = false;
035:
036: if (packageName != null) {
037: // check explicit imports first (performance optimization)
038:
039: // handle 'from java.net import URL' like explicit imports
040: List stringFromlist = getFromListAsStrings(fromlist);
041: Iterator fromlistIterator = stringFromlist.iterator();
042: while (fromlistIterator.hasNext()) {
043: String fromName = (String) fromlistIterator.next();
044: if (isJavaClass(packageName, fromName)) {
045: packageAdded = addPackage(packageName, packageAdded);
046:
047: }
048: }
049:
050: // handle 'import java.net.URL' style explicit imports
051: int dotPos = packageName.lastIndexOf(DOT);
052: if (dotPos > 0) {
053: String lastDottedName = packageName
054: .substring(dotPos + 1);
055: String packageCand = packageName.substring(0, dotPos);
056: if (isJavaClass(packageCand, lastDottedName)) {
057: packageAdded = addPackage(packageCand, packageAdded);
058: }
059: }
060:
061: // if all else fails, check already loaded packages
062: if (!packageAdded) {
063: // build the actual map with the packages known to the VM
064: Map packages = buildLoadedPackages();
065:
066: // add known packages
067: String parentPackageName = packageName;
068: if (isLoadedPackage(packageName, packages)) {
069: packageAdded = addPackage(packageName, packageAdded);
070: }
071: dotPos = 0;
072: do {
073: dotPos = parentPackageName.lastIndexOf(DOT);
074: if (dotPos > 0) {
075: parentPackageName = parentPackageName
076: .substring(0, dotPos);
077: if (isLoadedPackage(parentPackageName, packages)) {
078: packageAdded = addPackage(
079: parentPackageName, packageAdded);
080: }
081: }
082: } while (dotPos > 0);
083:
084: // handle package imports like 'from java import math'
085: fromlistIterator = stringFromlist.iterator();
086: while (fromlistIterator.hasNext()) {
087: String fromName = (String) fromlistIterator.next();
088: String fromPackageName = packageName + DOT
089: + fromName;
090: if (isLoadedPackage(fromPackageName, packages)) {
091: packageAdded = addPackage(fromPackageName,
092: packageAdded);
093: }
094: }
095: }
096: }
097: return packageAdded;
098: }
099:
100: /**
101: * Check if a java package is already known to the VM.
102: * <p>
103: * May return <code>false</code> even if the given package name is a valid java package !
104: *
105: * @param packageName
106: *
107: * @return <code>true</code> if the package with the given name is already loaded by the VM, <code>false</code>
108: * otherwise.
109: */
110: protected static boolean isLoadedPackage(String packageName) {
111: return isLoadedPackage(packageName, buildLoadedPackages());
112: }
113:
114: /**
115: * Convert the fromlist into a java.lang.String based list.
116: * <p>
117: * Do some sanity checks: filter out '*' and empty tuples, as well as non tuples.
118: *
119: * @param fromlist
120: * @return a list containing java.lang.String entries
121: */
122: private static final List getFromListAsStrings(PyObject fromlist) {
123: List stringFromlist = new ArrayList();
124:
125: if (fromlist != null && fromlist != Py.EmptyTuple
126: && fromlist instanceof PyTuple) {
127: Iterator iterator = ((PyTuple) fromlist).iterator();
128: while (iterator.hasNext()) {
129: Object obj = iterator.next();
130: if (obj instanceof String) {
131: String fromName = (String) obj;
132: if (!"*".equals(fromName)) {
133: stringFromlist.add(fromName);
134: }
135: }
136: }
137: }
138: return stringFromlist;
139: }
140:
141: /**
142: * Faster way to check if a java package is already known to the VM.
143: * <p>
144: * May return <code>false</code> even if the given package name is a valid java package !
145: *
146: * @param packageName
147: * @param packages A Map containing all packages actually known to the VM. Such a Map can be obtained using
148: * {@link JavaImportHelper.buildLoadedPackagesTree()}
149: *
150: * @return <code>true</code> if the package with the given name is already loaded by the VM, <code>false</code>
151: * otherwise.
152: */
153: private static boolean isLoadedPackage(String javaPackageName,
154: Map packages) {
155: boolean isLoaded = false;
156: if (javaPackageName != null) {
157: isLoaded = packages.containsKey(javaPackageName);
158: }
159: return isLoaded;
160: }
161:
162: /**
163: * Build a <code>Map</code> of the currently known packages to the VM.
164: * <p>
165: * All parent packages appear as single entries like python modules, e.g. <code>java</code>,
166: * <code>java.lang</code>, <code>java.lang.reflect</code>,
167: */
168: private static Map buildLoadedPackages() {
169: TreeMap packageMap = new TreeMap();
170: Package[] packages = Package.getPackages();
171: for (int i = 0; i < packages.length; i++) {
172: String packageName = packages[i].getName();
173: packageMap.put(packageName, "");
174: int dotPos = 0;
175: do {
176: dotPos = packageName.lastIndexOf(DOT);
177: if (dotPos > 0) {
178: packageName = packageName.substring(0, dotPos);
179: packageMap.put(packageName, "");
180: }
181: } while (dotPos > 0);
182: }
183: return packageMap;
184: }
185:
186: /**
187: * Check a java class on VM level.
188: *
189: * @param packageName
190: * @param className
191: *
192: * @return <code>true</code> if the java class can be doubtlessly identified, <code>false</code> otherwise.
193: */
194: private static boolean isJavaClass(String packageName,
195: String className) {
196: if (className != null && className.length() > 0) {
197: className = packageName.replace('.', '/') + "/" + className
198: + ".class";
199: return Thread.currentThread().getContextClassLoader()
200: .getResource(className) != null;
201: } else {
202: return false;
203: }
204: }
205:
206: /**
207: * Add a java package to sys.modules, if not already done
208: *
209: * @return <code>true</code> if something was really added, <code>false</code> otherwise
210: */
211: private static boolean addPackage(String packageName,
212: boolean packageAdded) {
213: boolean added = false;
214: PyObject module = Py.getSystemState().modules
215: .__finditem__(packageName.intern());
216: if (module == null || module == Py.None) {
217: PyObject modules = Py.getSystemState().modules;
218: int dotPos;
219: do {
220: String internedPackageName = packageName.intern();
221: if (modules.__finditem__(internedPackageName) == Py.None) {
222: // a previously failed import could have created a Py.None entry in sys.modules
223: modules.__delitem__(internedPackageName);
224: }
225: PyJavaPackage p = PySystemState
226: .add_package(packageName);
227: Py.getSystemState().modules.__setitem__(
228: internedPackageName, p);
229: added = true;
230: dotPos = packageName.lastIndexOf(DOT);
231: if (dotPos > 0) {
232: packageName = packageName.substring(0, dotPos);
233: }
234: } while (dotPos > 0);
235: }
236: // make sure not to turn off the packageAdded flag
237: if (added && !packageAdded) {
238: packageAdded = true;
239: }
240: return packageAdded;
241: }
242:
243: }
|