-
Notifications
You must be signed in to change notification settings - Fork 227
Expand file tree
/
Copy pathPackageManager.java
More file actions
238 lines (207 loc) · 7.93 KB
/
PackageManager.java
File metadata and controls
238 lines (207 loc) · 7.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
// Copyright (c) Corporation for National Research Initiatives
// Copyright 2000 Samuele Pedroni
package org.python.core.packagecache;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;
import org.python.core.Py;
import org.python.core.PyJavaPackage;
import org.python.core.PyList;
import org.python.core.PyObject;
import org.python.core.PyStringMap;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.StringTokenizer;
/**
* Abstract package manager.
*/
public abstract class PackageManager extends Object {
/** Nominal top-level package of all (Java) packages, containing "java", "com", "org", etc.. */
public PyJavaPackage topLevelPackage;
public PackageManager() {
this.topLevelPackage = new PyJavaPackage("", this);
}
abstract public Class findClass(String pkg, String name, String reason);
public Class findClass(String pkg, String name) {
return findClass(pkg, name, "java class");
}
public void notifyPackageImport(String pkg, String name) {}
/**
* Dynamically check if pkg.name exists as java pkg in the controlled hierarchy. Should be
* overridden.
*
* @param pkg parent pkg name
* @param name candidate name
* @return true if pkg exists
*/
public abstract boolean packageExists(String pkg, String name);
/**
* Reports the specified package content names. Should be overridden. Used by
* {@link PyJavaPackage#__dir__} and {@link PyJavaPackage#fillDir}.
*
* @return resulting list of names (PyList of PyString)
* @param jpkg queried package
* @param instantiate if true then instatiate reported names in package dict
* @param exclpkgs exclude packages (just when instantiate is false)
*/
public abstract PyList doDir(PyJavaPackage jpkg, boolean instantiate, boolean exclpkgs);
/**
* Append a directory to the list of directories searched for java packages and java classes.
*
* @param dir A directory.
*/
public abstract void addDirectory(java.io.File dir);
/**
* Append a directory to the list of directories searched for java packages and java classes.
*
* @param dir A directory name.
*/
public abstract void addJarDir(String dir, boolean cache);
/**
* Append a jar file to the list of locations searched for java packages and java classes.
*
* @param jarfile A directory name.
*/
public abstract void addJar(String jarfile, boolean cache);
/**
* Basic helper implementation of {@link #doDir}. It merges information from jpkg
* {@link PyJavaPackage#clsSet} and {@link PyJavaPackage#__dict__}.
*/
protected PyList basicDoDir(PyJavaPackage jpkg, boolean instantiate, boolean exclpkgs) {
PyStringMap dict = jpkg.__dict__;
PyStringMap cls = jpkg.clsSet;
if (!instantiate) {
PyList ret = cls.keys();
PyList dictKeys = dict.keys();
for (PyObject name : dictKeys.asIterable()) {
if (!cls.has_key(name)) {
if (exclpkgs && dict.get(name) instanceof PyJavaPackage) {
continue;
}
ret.append(name);
}
}
return ret;
}
for (PyObject pyname : cls.keys().asIterable()) {
if (!dict.has_key(pyname)) {
String name = pyname.toString();
jpkg.addClass(name, Py.findClass(jpkg.__name__ + "." + name));
}
}
return dict.keys();
}
/** Helper merging list2 into list1. Returns list1. */
protected PyList merge(PyList list1, PyList list2) {
for (PyObject name : list2.asIterable()) {
list1.append(name);
}
return list1;
}
/**
* Given the (dotted) name of a package, find the {@link PyJavaPackage} corresponding, by
* navigating from the {@link #topLevelPackage}, successively applying
* {@link PyObject#__findattr__(String)}. This in fact drives the creation of
* {@link PyJavaPackage} objects since it indirectly calls
* {@link #packageExists(String, String)}.
*
* @param name (dotted) package name
* @return the package named
*/
public PyObject lookupName(String name) {
PyObject top = this.topLevelPackage;
do {
int dot = name.indexOf('.');
String firstName = name;
String lastName = null;
if (dot != -1) {
firstName = name.substring(0, dot);
lastName = name.substring(dot + 1, name.length());
}
firstName = firstName.intern();
top = top.__findattr__(firstName);
if (top == null) {
return null;
}
// ??pending: test for jpkg/jclass?
name = lastName;
} while (name != null);
return top;
}
/**
* Create (or ensure we have) a {@link PyJavaPackage} for the named package and add to it the
* names of classes mentioned here. These classes are added as "place holders" only, so they
* become members of it, without being instantiated. This method relies on
* {@link PyJavaPackage#addPackage(java.lang.String, java.lang.String)} and
* {@link PyJavaPackage#addPlaceholders}.
*
* @param name package name
* @param classes comma or @-sign separated string
* @param jarfile involved; can be null
* @return created/updated package
*/
public PyJavaPackage makeJavaPackage(String name, String classes, String jarfile) {
PyJavaPackage p = this.topLevelPackage;
if (name.length() != 0) {
p = p.addPackage(name, jarfile);
}
p.addPlaceholders(split(classes, ",@"));
return p;
}
/** ASM visitor class supporting {@link #checkAccess(InputStream)}. */
private static class AccessVisitor extends ClassVisitor {
private int class_access;
public AccessVisitor() throws IOException {
super(Opcodes.ASM7);
}
@Override
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
class_access = access;
}
public int getClassAccess() {
return class_access;
}
}
/**
* Check that a given stream is a valid Java .class file, and return its access permissions as
* an int.
*/
static protected int checkAccess(InputStream cstream) throws IOException {
try {
ClassReader reader = new ClassReader(cstream);
AccessVisitor visitor = new AccessVisitor();
reader.accept(visitor, 0);
return visitor.getClassAccess();
} catch (RuntimeException e) {
return -1;
}
}
/**
* Helper to split a textual list into a list. The semantics are basically those of
* {@code StringTokenizer}, followed by {@code String.trim()} and duplicate removal.
*
* @param target compound string to split into tokens ({@code null} treated as "".
* @param sep characters, any of which will be treated as a token separator
* @return set of tokens trimmed of white space
*/
protected static Set<String> split(String target, String sep) {
Set<String> result = new LinkedHashSet<>();
if (target != null) {
StringTokenizer tok = new StringTokenizer(target, sep);
while (tok.hasMoreTokens()) {
String entry = tok.nextToken().trim();
if (entry.length() > 0) {
result.add(entry);
}
}
}
return result;
}
/** Equivalent to {@code split(target, ",")}. See {@link #split(String, String)}. */
protected static Set<String> split(String target) {
return split(target, ",");
}
}