KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > polyglot > types > ImportTable


1 package polyglot.types;
2
3 import polyglot.util.*;
4 import polyglot.main.Report;
5
6 import java.util.*;
7
8
9 /**
10  * An <code>ImportTable</code> is a type of <code>ClassResolver</code> that
11  * corresponds to a particular source file.
12  * <p>
13  * It has a set of package and class imports, which caches the results of
14  * lookups for future reference.
15  */

16 public class ImportTable extends ClassResolver
17 {
18     protected TypeSystem ts;
19
20     /** The underlying resolver. */
21     protected Resolver resolver;
22     /** A list of all package imports. */
23     protected List packageImports;
24     /** Map from names to classes found, or to the NOT_FOUND object. */
25     protected Map map;
26     /** List of class imports which will be lazily added to the table at the
27      * next lookup. */

28     protected List lazyImports;
29     /** List of explicitly imported classes added to the table or pending in
30      * the lazyImports list. */

31     protected List classImports;
32     /** Source name to use for debugging and error reporting */
33     protected String JavaDoc sourceName;
34     /** Position to use for error reporting */
35     protected Position sourcePos;
36     /** Our package */
37     protected Package JavaDoc pkg;
38
39     private static final Object JavaDoc NOT_FOUND = "NOT FOUND";
40     
41     /**
42      * Create an import table.
43      * @param ts The type system
44      * @param base The outermost resolver to use for looking up types.
45      * @param pkg The package of the source we are importing types into.
46      */

47     public ImportTable(TypeSystem ts, Resolver base, Package JavaDoc pkg) {
48         this(ts, base, pkg, null);
49     }
50
51     /**
52      * Create an import table.
53      * @param ts The type system
54      * @param base The outermost resolver to use for looking up types.
55      * @param pkg The package of the source we are importing types into.
56      * @param src The name of the source file we are importing into.
57      */

58     public ImportTable(TypeSystem ts, Resolver base, Package JavaDoc pkg, String JavaDoc src) {
59         this.resolver = base;
60         this.ts = ts;
61         this.sourceName = src;
62         this.sourcePos = src != null ? new Position(src) : null;
63         this.pkg = pkg;
64
65     this.map = new HashMap();
66     this.packageImports = new ArrayList();
67     this.lazyImports = new ArrayList();
68     this.classImports = new ArrayList();
69     }
70
71     /**
72      * The package of the source we are importing types into.
73      */

74     public Package JavaDoc package_() {
75         return pkg;
76     }
77
78     /**
79      * Add a class import.
80      */

81     public void addClassImport(String JavaDoc className) {
82         if (Report.should_report(TOPICS, 2))
83             Report.report(2, this + ": lazy import " + className);
84
85     lazyImports.add(className);
86         classImports.add(className);
87     }
88
89     /**
90      * Add a package import.
91      */

92     public void addPackageImport(String JavaDoc pkgName) {
93         // don't add the import if it is the same as the current package,
94
// the same as a default import, or has already been imported
95
if ((pkg != null && pkg.fullName().equals(pkgName)) ||
96                 ts.defaultPackageImports().contains(pkgName) ||
97                 packageImports.contains(pkgName)) {
98             return;
99         }
100         
101         packageImports.add(pkgName);
102     }
103
104     /**
105      * List the packages we import from.
106      */

107     public List packageImports() {
108         return packageImports;
109     }
110
111     /**
112      * List the classes explicitly imported.
113      */

114     public List classImports() {
115         return classImports;
116     }
117
118     /**
119      * The name of the source file we are importing into.
120      */

121     public String JavaDoc sourceName() {
122         return sourceName;
123     }
124
125     /**
126      * Find a type by name, using the cache and the outer resolver,
127      * but not the import table.
128      */

129     protected Named cachedFind(String JavaDoc name) throws SemanticException {
130         Object JavaDoc res = map.get(name);
131
132         if (res != null) {
133             return (Named) res;
134         }
135
136         Named t = resolver.find(name);
137         map.put(name, t);
138         return t;
139     }
140
141     /**
142      * Find a type by name, searching the import table.
143      */

144     public Named find(String JavaDoc name) throws SemanticException {
145         if (Report.should_report(TOPICS, 2))
146            Report.report(2, this + ".find(" + name + ")");
147
148         /* First add any lazy imports. */
149         lazyImport();
150
151         if (!StringUtil.isNameShort(name)) {
152             // The name was long.
153
return resolver.find(name);
154         }
155         
156         // The class name is short.
157
// First see if we have a mapping already.
158
Object JavaDoc res = map.get(name);
159
160         if (res != null) {
161             if (res == NOT_FOUND) {
162                 throw new NoClassException(name, sourcePos);
163             }
164             return (Named) res;
165         }
166
167         try {
168             if (pkg != null) {
169                 // check if the current package defines it.
170
// If so, this takes priority over the package imports (or
171
// "type-import-on-demand" declarations as they are called in
172
// the JLS), so even if another package defines the same name,
173
// there is no conflict. See Section 6.5.2 of JLS, 2nd Ed.
174
Named n = findInPkg(name, pkg.fullName());
175                 if (n != null) {
176                     if (Report.should_report(TOPICS, 3))
177                        Report.report(3, this + ".find(" + name + "): found in current package");
178
179                     // Memoize the result.
180
map.put(name, n);
181                     return n;
182                 }
183             }
184             
185             List imports = new ArrayList(packageImports.size() + 5);
186             
187             imports.addAll(ts.defaultPackageImports());
188             imports.addAll(packageImports);
189             
190             // It wasn't a ClassImport. Maybe it was a PackageImport?
191
Named resolved = null;
192             for (Iterator iter = imports.iterator(); iter.hasNext(); ) {
193                 String JavaDoc pkgName = (String JavaDoc) iter.next();
194                 Named n = findInPkg(name, pkgName);
195                 if (n != null) {
196                     if (resolved == null) {
197                         // this is the first occurance of name we've found
198
// in a package import.
199
// Record it, and keep going, to see if there
200
// are any conflicts.
201
resolved = n;
202                     }
203                     else {
204                         // this is the 2nd occurance of name we've found
205
// in an imported package.
206
// That's bad.
207
throw new SemanticException("Reference to \"" +
208                                 name + "\" is ambiguous; both " +
209                                 resolved.fullName() + " and " + n.fullName() +
210                                 " match.");
211                     }
212                 }
213             }
214             
215             if (resolved == null) {
216                 // The name was short, but not in any imported class or package.
217
// Check the null package.
218
resolved = resolver.find(name); // may throw exception
219

220                 if (!isVisibleFrom(resolved, "")) {
221                     // Not visible.
222
throw new NoClassException(name, sourcePos);
223                 }
224             }
225             
226             // Memoize the result.
227
if (Report.should_report(TOPICS, 3))
228                Report.report(3, this + ".find(" + name + "): found as " + resolved.fullName());
229             map.put(name, resolved);
230             return resolved;
231         }
232         catch (NoClassException e) {
233             // memoize the no class exception
234
if (Report.should_report(TOPICS, 3))
235                Report.report(3, this + ".find(" + name + "): didn't find it");
236             map.put(name, NOT_FOUND);
237             throw e;
238         }
239     }
240     
241     protected Named findInPkg(String JavaDoc name, String JavaDoc pkgName) throws SemanticException {
242         String JavaDoc fullName = pkgName + "." + name;
243
244         try {
245             Named n = resolver.find(pkgName);
246
247             if (n instanceof ClassType) {
248                 n = ts.classContextResolver((ClassType) n).find(name);
249                 return n;
250             }
251         }
252         catch (NoClassException ex) {
253             // Do nothing.
254
}
255
256         try {
257             Named n = resolver.find(fullName);
258
259             // Check if the type is visible in this package.
260
if (isVisibleFrom(n, pkgName)) {
261                 return n;
262             }
263         }
264         catch (NoClassException ex) {
265             // Do nothing.
266
}
267
268         return null;
269     }
270
271     /**
272      * Return whether <code>n</code> in package <code>pkgName</code> is visible from within
273      * package <code>pkg</code>. The empty string may
274      * be passed in to represent the default package.
275      */

276     protected boolean isVisibleFrom(Named n, String JavaDoc pkgName) {
277         boolean isVisible = false;
278         boolean inSamePackage = this.pkg != null
279                 && this.pkg.fullName().equals(pkgName)
280             || this.pkg == null
281                 && pkgName.equals("");
282         if (n instanceof Type) {
283             Type t = (Type) n;
284             //FIXME: Assume non-class types are always visible.
285
isVisible = !t.isClass()
286                 || t.toClass().flags().isPublic()
287                 || inSamePackage;
288         } else {
289             //FIXME: Assume non-types are always visible.
290
isVisible = true;
291         }
292         return isVisible;
293     }
294     
295     /**
296      * Load the class imports, lazily.
297      */

298     protected void lazyImport() throws SemanticException {
299     if (lazyImports.isEmpty()) {
300             return;
301     }
302
303     for (int i = 0; i < lazyImports.size(); i++) {
304         String JavaDoc longName = (String JavaDoc) lazyImports.get(i);
305
306             if (Report.should_report(TOPICS, 2))
307         Report.report(2, this + ": import " + longName);
308
309         try {
310                 // Try to find a class named longName.
311
// The class maybe a static member class of another, so we'll
312
// make several attempts.
313
StringTokenizer st = new StringTokenizer(longName, ".");
314                 StringBuffer JavaDoc name = new StringBuffer JavaDoc();
315                 Named t = null;
316
317                 while (st.hasMoreTokens()) {
318                     String JavaDoc s = st.nextToken();
319                     name.append(s);
320
321                     try {
322                         t = cachedFind(name.toString());
323
324                         if (! st.hasMoreTokens()) {
325                             // found it
326
break;
327                         }
328
329                         if (t instanceof ClassType) {
330                             // If we find a class that is further qualfied,
331
// search for member classes of that class.
332
ClassType ct = (ClassType) t;
333
334                             while (st.hasMoreTokens()) {
335                                 String JavaDoc n = st.nextToken();
336                                 t = ct = ts.findMemberClass(ct, n);
337
338                                 // cache the result
339
map.put(n, ct);
340                             }
341                         }
342                         else {
343                             // t, whatever it is, is further qualified, but
344
// should be, at least in Java, a ClassType.
345
throw new InternalCompilerError("Qualified type \"" + t + "\" is not a class type.", sourcePos);
346                         }
347                     }
348                     catch (SemanticException e) {
349                         if (! st.hasMoreTokens()) {
350                             throw e;
351                         }
352
353                         // try again with the next level of type qualification
354
name.append(".");
355                     }
356                 }
357
358                 String JavaDoc shortName = StringUtil.getShortNameComponent(longName);
359
360                 if (Report.should_report(TOPICS, 2))
361             Report.report(2, this + ": import " + shortName + " as " + t);
362
363         if (map.containsKey(shortName)) {
364             Named s = (Named) map.get(shortName);
365
366             if (! ts.equals(s, t)) {
367             throw new SemanticException("Class " + shortName +
368                 " already defined as " + map.get(shortName),
369                             sourcePos);
370             }
371         }
372
373                 // map.put(longName, t); // should already be in the cache
374
map.put(shortName, t);
375         }
376         catch (SemanticException e) {
377                 if (e.position == null) {
378                     e.position = sourcePos;
379                 }
380
381                 throw e;
382         }
383     }
384
385     lazyImports = new ArrayList();
386     }
387
388     public String JavaDoc toString() {
389         if (sourceName != null) {
390             return "(import " + sourceName + ")";
391         }
392         else {
393             return "(import)";
394         }
395     }
396
397     private static final Collection TOPICS =
398         CollectionUtil.list(Report.types, Report.resolver, Report.imports);
399
400 }
401
Popular Tags