001: package org.python.core;
002:
003: import java.io.File;
004: import java.io.IOException;
005: import java.io.InputStream;
006: import java.util.zip.ZipEntry;
007:
008: /**
009: * Load python source from jar or zip files.
010: */
011: public class ZipFileImporter extends PyObject {
012:
013: private SyspathArchive archive;
014: private String pathToArchive;
015:
016: /**
017: * If this path is not an archive (.zip or .jar) then raise an ImportError,
018: * otherwise this instance will handle this path.
019: *
020: * @param path the path to check for modules
021: */
022: public ZipFileImporter(PyObject path) {
023: if (!(path instanceof SyspathArchive)) {
024: throw Py.ImportError(path.toString());
025: }
026: this .archive = (SyspathArchive) path;
027: String archiveName = SyspathArchive
028: .getArchiveName(archive.string);
029: this .pathToArchive = new File(archiveName).getAbsolutePath()
030: + File.separatorChar;
031: }
032:
033: /**
034: * Find the module for the fully qualified name.
035: *
036: * @param name the fully qualified name of the module
037: * @return a loader instance if this importer can load the module, None
038: * otherwise
039: */
040: public PyObject find_module(String name) {
041: return find_module(name, Py.None);
042: }
043:
044: /**
045: * Find the module for the fully qualified name.
046: *
047: * @param name the fully qualified name of the module
048: * @param path if installed on the meta-path None or a module path
049: * @return a loader instance if this importer can load the module, None
050: * otherwise
051: */
052: public PyObject find_module(String name, PyObject path) {
053: ZipModuleInfo zip = getModuleInfo(name, this .archive);
054: return (zip == null) ? Py.None : new ZipFileLoader(zip);
055: }
056:
057: /**
058: * Returns a string representation of the object.
059: *
060: * @return a string representation of the object.
061: */
062: public String toString() {
063: return this .getType().toString();
064: }
065:
066: /**
067: * Returns the last part of a fully qualified name. For example, the name
068: * <p>
069: * <code>
070: * a.b.c
071: * </code>
072: * </p>
073: * would return <code>c</code>.
074: *
075: * @param name a fully qualified name
076: * @return the last part of a fully qualified name
077: */
078: private String getSubName(String name) {
079: int x = name.lastIndexOf(".");
080: if (x >= 0) {
081: return name.substring(x + 1);
082: }
083: return name;
084: }
085:
086: /**
087: * Find the module name starting at the zipArchive root. This method will
088: * look for both package and non-package names in the archive. If the name
089: * is not found null will be returned.
090: *
091: * @param name the fully qualified module name
092: * @param zipArchive the root of the path to begin looking
093: * @return null if the module is not found, a ZipModuleInfo instance
094: * otherwise
095: */
096: private ZipModuleInfo getModuleInfo(String name,
097: SyspathArchive zipArchive) {
098: String entryName = getSubName(name);
099:
100: String sourceName = entryName + "/__init__.py";
101: String compiledName = entryName + "/__init__$py.class";
102: ZipEntry sourceEntry = zipArchive.getEntry(sourceName);
103: ZipEntry compiledEntry = zipArchive.getEntry(compiledName);
104:
105: boolean pkg = (sourceEntry != null || compiledEntry != null);
106: if (!pkg) {
107: sourceName = entryName + ".py";
108: compiledName = entryName + "$py.class";
109: sourceEntry = zipArchive.getEntry(sourceName);
110: compiledEntry = zipArchive.getEntry(compiledName);
111: } else {
112: zipArchive = zipArchive.makeSubfolder(entryName);
113: }
114:
115: ZipModuleInfo info = null;
116: if (sourceEntry != null) {
117: Py.writeDebug("import", "trying source entry: "
118: + sourceName + " from jar/zip file " + zipArchive);
119: if (compiledEntry != null) {
120: Py.writeDebug("import", "trying precompiled entry "
121: + compiledName + " from jar/zip file "
122: + zipArchive);
123: long pyTime = sourceEntry.getTime();
124: long classTime = compiledEntry.getTime();
125: if (classTime >= pyTime) {
126: info = new ZipModuleInfo(zipArchive, compiledEntry,
127: true);
128: }
129: }
130: if (info == null) {
131: info = new ZipModuleInfo(zipArchive, sourceEntry, false);
132: }
133: }
134:
135: if (pkg && info != null) {
136: info.path = new PyList(new PyObject[] { zipArchive });
137: }
138:
139: return info;
140: }
141:
142: /**
143: * Loader for zipfile python sources.
144: */
145: public class ZipFileLoader extends PyObject {
146:
147: private ZipModuleInfo _info;
148:
149: public ZipFileLoader(ZipModuleInfo info) {
150: this ._info = info;
151: }
152:
153: /**
154: * A loaded module for the fully qualified name.
155: *
156: * @param moduleName the fully qualified name
157: * @return a loaded module (added to sys.path)
158: */
159: public PyObject load_module(String moduleName) {
160: PyModule m = null;
161: if (this ._info.path != null) {
162: m = imp.addModule(moduleName);
163: m.__dict__.__setitem__("__path__", this ._info.path);
164: m.__dict__.__setitem__("__loader__", this );
165: }
166:
167: InputStream is = null; // should this be closed?
168: ZipEntry entry = this ._info.zipEntry;
169: try {
170: is = this ._info.archive.getInputStream(entry);
171: } catch (IOException e) {
172: Py.writeDebug("import", "loadFromZipFile exception: "
173: + e.toString());
174: throw Py.ImportError("error loading from zipfile");
175: }
176: String pathToEntry = pathToArchive + entry.getName();
177: PyObject o;
178: if (this ._info.compiled) {
179: o = imp.createFromPyClass(moduleName, is, true,
180: pathToEntry);
181: } else {
182: o = imp.createFromSource(moduleName, is, pathToEntry,
183: null);
184: }
185: return (m == null) ? o : m;
186: }
187:
188: /**
189: * Returns a string representation of the object.
190: *
191: * @return a string representation of the object.
192: */
193: public String toString() {
194: return this .getType().toString();
195: }
196: }
197:
198: private class ZipModuleInfo {
199: /** The path of the package if it is a package. */
200: public PyObject path;
201:
202: /** Whether the code is already compiled. */
203: public boolean compiled;
204:
205: /** The zip entry for the file to load. */
206: public ZipEntry zipEntry;
207:
208: /** The archive in which the zip entry resides. */
209: public SyspathArchive archive;
210:
211: public ZipModuleInfo(SyspathArchive archive, ZipEntry zipEntry,
212: boolean compiled) {
213: this (archive, zipEntry, compiled, null);
214: }
215:
216: public ZipModuleInfo(SyspathArchive archive, ZipEntry zipEntry,
217: boolean compiled, PyObject path) {
218: this.path = path;
219: this.archive = archive;
220: this.zipEntry = zipEntry;
221: this.compiled = compiled;
222: }
223: }
224: }
|