blob: 47dd39415d5da5b151be2b323ee85f4248f43557 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1994-2003 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.tools.java;
27
28import java.util.Hashtable;
29import java.util.Vector;
30import java.util.Enumeration;
31import java.util.List;
32import java.util.Collections;
33import java.io.IOException;
34
35/**
36 * This class describes the classes and packages imported
37 * from a source file. A Hashtable called bindings is maintained
38 * to quickly map symbol names to classes. This table is flushed
39 * everytime a new import is added.
40 *
41 * A class name is resolved as follows:
42 * - if it is a qualified name then return the corresponding class
43 * - if the name corresponds to an individually imported class then return that class
44 * - check if the class is defined in any of the imported packages,
45 * if it is then return it, make sure it is defined in only one package
46 * - assume that the class is defined in the current package
47 *
48 * WARNING: The contents of this source file are not part of any
49 * supported API. Code that depends on them does so at its own risk:
50 * they are subject to change or removal without notice.
51 */
52
53public
54class Imports implements Constants {
55 /**
56 * The current package, which is implicitly imported,
57 * and has precedence over other imported packages.
58 */
59 Identifier currentPackage = idNull;
60
61 /**
62 * A location for the current package declaration. Used to
63 * report errors against the current package.
64 */
65 long currentPackageWhere = 0;
66
67 /**
68 * The imported classes, including memoized imports from packages.
69 */
70 Hashtable classes = new Hashtable();
71
72 /**
73 * The imported package identifiers. This will not contain duplicate
74 * imports for the same package. It will also not contain the
75 * current package.
76 */
77 Vector packages = new Vector();
78
79 /**
80 * The (originally) imported classes.
81 * A vector of IdentifierToken.
82 */
83 Vector singles = new Vector();
84
85 /**
86 * Are the import names checked yet?
87 */
88 protected int checked;
89
90 /**
91 * Constructor, always import java.lang.
92 */
93 public Imports(Environment env) {
94 addPackage(idJavaLang);
95 }
96
97 /**
98 * Check the names of the imports.
99 */
100 public synchronized void resolve(Environment env) {
101 if (checked != 0) {
102 return;
103 }
104 checked = -1;
105
106 // After all class information has been read, now we can
107 // safely inspect import information for errors.
108 // If we did this before all parsing was finished,
109 // we could get vicious circularities, since files can
110 // import each others' classes.
111
112 // A note: the resolution of the package java.lang takes place
113 // in the sun.tools.javac.BatchEnvironment#setExemptPackages().
114
115 // Make sure that the current package's name does not collide
116 // with the name of an existing class. (bug 4101529)
117 //
118 // This change has been backed out because, on WIN32, it
119 // failed to distinguish between java.awt.event and
120 // java.awt.Event when looking for a directory. We will
121 // add this back in later.
122 //
123 // if (currentPackage != idNull) {
124 // Identifier resolvedName =
125 // env.resolvePackageQualifiedName(currentPackage);
126 //
127 // Identifier className = resolvedName.getTopName();
128 //
129 // if (importable(className, env)) {
130 // // The name of the current package is also the name
131 // // of a class.
132 // env.error(currentPackageWhere, "package.class.conflict",
133 // currentPackage, className);
134 // }
135 // }
136
137 Vector resolvedPackages = new Vector();
138 for (Enumeration e = packages.elements() ; e.hasMoreElements() ;) {
139 IdentifierToken t = (IdentifierToken)e.nextElement();
140 Identifier nm = t.getName();
141 long where = t.getWhere();
142
143 // Check to see if this package is exempt from the "exists"
144 // check. See the note in
145 // sun.tools.javac.BatchEnvironment#setExemptPackages()
146 // for more information.
147 if (env.isExemptPackage(nm)) {
148 resolvedPackages.addElement(t);
149 continue;
150 }
151
152 // (Note: This code is moved from BatchParser.importPackage().)
153 try {
154 Identifier rnm = env.resolvePackageQualifiedName(nm);
155 if (importable(rnm, env)) {
156 // This name is a real class; better not be a package too.
157 if (env.getPackage(rnm.getTopName()).exists()) {
158 env.error(where, "class.and.package",
159 rnm.getTopName());
160 }
161 // Pass an "inner" name to the imports.
162 if (!rnm.isInner())
163 rnm = Identifier.lookupInner(rnm, idNull);
164 nm = rnm;
165 } else if (!env.getPackage(nm).exists()) {
166 env.error(where, "package.not.found", nm, "import");
167 } else if (rnm.isInner()) {
168 // nm exists, and rnm.getTopName() is a parent package
169 env.error(where, "class.and.package", rnm.getTopName());
170 }
171 resolvedPackages.addElement(new IdentifierToken(where, nm));
172 } catch (IOException ee) {
173 env.error(where, "io.exception", "import");
174 }
175 }
176 packages = resolvedPackages;
177
178 for (Enumeration e = singles.elements() ; e.hasMoreElements() ;) {
179 IdentifierToken t = (IdentifierToken)e.nextElement();
180 Identifier nm = t.getName();
181 long where = t.getWhere();
182 Identifier pkg = nm.getQualifier();
183
184 // (Note: This code is moved from BatchParser.importClass().)
185 nm = env.resolvePackageQualifiedName(nm);
186 if (!env.classExists(nm.getTopName())) {
187 env.error(where, "class.not.found", nm, "import");
188 }
189
190 // (Note: This code is moved from Imports.addClass().)
191 Identifier snm = nm.getFlatName().getName();
192
193 // make sure it isn't already imported explicitly
194 Identifier className = (Identifier)classes.get(snm);
195 if (className != null) {
196 Identifier f1 = Identifier.lookup(className.getQualifier(),
197 className.getFlatName());
198 Identifier f2 = Identifier.lookup(nm.getQualifier(),
199 nm.getFlatName());
200 if (!f1.equals(f2)) {
201 env.error(where, "ambig.class", nm, className);
202 }
203 }
204 classes.put(snm, nm);
205
206
207 // The code here needs to check to see, if we
208 // are importing an inner class, that all of its
209 // enclosing classes are visible to us. To check this,
210 // we need to construct a definition for the class.
211 // The code here used to call...
212 //
213 // ClassDefinition def = env.getClassDefinition(nm);
214 //
215 // ...but that interfered with the basicCheck()'ing of
216 // interfaces in certain cases (bug no. 4086139). Never
217 // fear. Instead we load the class with a call to the
218 // new getClassDefinitionNoCheck() which does no basicCheck() and
219 // lets us answer the questions we are interested in w/o
220 // interfering with the demand-driven nature of basicCheck().
221
222 try {
223 // Get a declaration
224 ClassDeclaration decl = env.getClassDeclaration(nm);
225
226 // Get the definition (no env argument)
227 ClassDefinition def = decl.getClassDefinitionNoCheck(env);
228
229 // Get the true name of the package containing this class.
230 // `pkg' from above is insufficient. It includes the
231 // names of our enclosing classes. Fix for 4086815.
232 Identifier importedPackage = def.getName().getQualifier();
233
234 // Walk out the outerClass chain, ensuring that each level
235 // is visible from our perspective.
236 for (; def != null; def = def.getOuterClass()) {
237 if (def.isPrivate()
238 || !(def.isPublic()
239 || importedPackage.equals(currentPackage))) {
240 env.error(where, "cant.access.class", def);
241 break;
242 }
243 }
244 } catch (AmbiguousClass ee) {
245 env.error(where, "ambig.class", ee.name1, ee.name2);
246 } catch (ClassNotFound ee) {
247 env.error(where, "class.not.found", ee.name, "import");
248 }
249 }
250 checked = 1;
251 }
252
253 /**
254 * Lookup a class, given the current set of imports,
255 * AmbiguousClass exception is thrown if the name can be
256 * resolved in more than one way. A ClassNotFound exception
257 * is thrown if the class is not found in the imported classes
258 * and packages.
259 */
260 public synchronized Identifier resolve(Environment env, Identifier nm) throws ClassNotFound {
261 if (tracing) env.dtEnter("Imports.resolve: " + nm);
262
263 // If the class has the special ambiguous prefix, then we will
264 // get the original AmbiguousClass exception by removing the
265 // prefix and proceeding in the normal fashion.
266 // (part of solution for 4059855)
267 if (nm.hasAmbigPrefix()) {
268 nm = nm.removeAmbigPrefix();
269 }
270
271 if (nm.isQualified()) {
272 // Don't bother it is already qualified
273 if (tracing) env.dtExit("Imports.resolve: QUALIFIED " + nm);
274 return nm;
275 }
276
277 if (checked <= 0) {
278 checked = 0;
279 resolve(env);
280 }
281
282 // Check if it was imported before
283 Identifier className = (Identifier)classes.get(nm);
284 if (className != null) {
285 if (tracing) env.dtExit("Imports.resolve: PREVIOUSLY IMPORTED " + nm);
286 return className;
287 }
288
289 // Note: the section below has changed a bit during the fix
290 // for bug 4093217. The current package is no longer grouped
291 // with the rest of the import-on-demands; it is now checked
292 // separately. Also, the list of import-on-demands is now
293 // guarranteed to be duplicate-free, so the code below can afford
294 // to be a bit simpler.
295
296 // First we look in the current package. The current package
297 // is given precedence over the rest of the import-on-demands,
298 // which means, among other things, that a class in the current
299 // package cannot be ambiguous.
300 Identifier id = Identifier.lookup(currentPackage, nm);
301 if (importable(id, env)) {
302 className = id;
303 } else {
304 // If it isn't in the current package, try to find it in
305 // our import-on-demands.
306 Enumeration e = packages.elements();
307 while (e.hasMoreElements()) {
308 IdentifierToken t = (IdentifierToken)e.nextElement();
309 id = Identifier.lookup(t.getName(), nm);
310
311 if (importable(id, env)) {
312 if (className == null) {
313 // We haven't found any other matching classes yet.
314 // Set className to what we've found and continue
315 // looking for an ambiguity.
316 className = id;
317 } else {
318 if (tracing)
319 env.dtExit("Imports.resolve: AMBIGUOUS " + nm);
320
321 // We've found an ambiguity.
322 throw new AmbiguousClass(className, id);
323 }
324 }
325 }
326 }
327
328 // Make sure a class was found
329 if (className == null) {
330 if (tracing) env.dtExit("Imports.resolve: NOT FOUND " + nm);
331 throw new ClassNotFound(nm);
332 }
333
334 // Remember the binding
335 classes.put(nm, className);
336 if (tracing) env.dtExit("Imports.resolve: FIRST IMPORT " + nm);
337 return className;
338 }
339
340 /**
341 * Check to see if 'id' names an importable class in `env'.
342 * This method was made public and static for utility.
343 */
344 static public boolean importable(Identifier id, Environment env) {
345 if (!id.isInner()) {
346 return env.classExists(id);
347 } else if (!env.classExists(id.getTopName())) {
348 return false;
349 } else {
350 // load the top class and look inside it
351 try {
352 // There used to be a call to...
353 // env.getClassDeclaration(id.getTopName());
354 // ...here. It has been replaced with the
355 // two statements below. These should be functionally
356 // the same except for the fact that
357 // getClassDefinitionNoCheck() does not call
358 // basicCheck(). This allows us to avoid a circular
359 // need to do basicChecking that can arise with
360 // certain patterns of importing and inheritance.
361 // This is a fix for a variant of bug 4086139.
362 //
363 // Note: the special case code in env.getClassDefinition()
364 // which handles inner class names is not replicated below.
365 // This should be okay, as we are looking up id.getTopName(),
366 // not id.
367 ClassDeclaration decl =
368 env.getClassDeclaration(id.getTopName());
369 ClassDefinition c =
370 decl.getClassDefinitionNoCheck(env);
371
372 return c.innerClassExists(id.getFlatName().getTail());
373 } catch (ClassNotFound ee) {
374 return false;
375 }
376 }
377 }
378
379 /**
380 * Suppose a resolve() call has failed.
381 * This routine can be used silently to give a reasonable
382 * default qualification (the current package) to the identifier.
383 * This decision is recorded for future reference.
384 */
385 public synchronized Identifier forceResolve(Environment env, Identifier nm) {
386 if (nm.isQualified())
387 return nm;
388
389 Identifier className = (Identifier)classes.get(nm);
390 if (className != null) {
391 return className;
392 }
393
394 className = Identifier.lookup(currentPackage, nm);
395
396 classes.put(nm, className);
397 return className;
398 }
399
400 /**
401 * Add a class import
402 */
403 public synchronized void addClass(IdentifierToken t) {
404 singles.addElement(t);
405 }
406 // for compatibility
407 public void addClass(Identifier nm) throws AmbiguousClass {
408 addClass(new IdentifierToken(nm));
409 }
410
411 /**
412 * Add a package import, or perhaps an inner class scope.
413 * Ignore any duplicate imports.
414 */
415 public synchronized void addPackage(IdentifierToken t) {
416 final Identifier name = t.getName();
417
418 // If this is a duplicate import for the current package,
419 // ignore it.
420 if (name == currentPackage) {
421 return;
422 }
423
424 // If this is a duplicate of a package which has already been
425 // added to the list, ignore it.
426 final int size = packages.size();
427 for (int i = 0; i < size; i++) {
428 if (name == ((IdentifierToken)packages.elementAt(i)).getName()) {
429 return;
430 }
431 }
432
433 // Add the package to the list.
434 packages.addElement(t);
435 }
436 // for compatibility
437 public void addPackage(Identifier id) {
438 addPackage(new IdentifierToken(id));
439 }
440
441 /**
442 * Specify the current package with an IdentifierToken.
443 */
444 public synchronized void setCurrentPackage(IdentifierToken t) {
445 currentPackage = t.getName();
446 currentPackageWhere = t.getWhere();
447 }
448
449 /**
450 * Specify the current package
451 */
452 public synchronized void setCurrentPackage(Identifier id) {
453 currentPackage = id;
454 }
455
456 /**
457 * Report the current package
458 */
459 public Identifier getCurrentPackage() {
460 return currentPackage;
461 }
462
463 /**
464 * Return an unmodifiable list of IdentifierToken representing
465 * packages specified as imports.
466 */
467 public List getImportedPackages() {
468 return Collections.unmodifiableList(packages);
469 }
470
471 /**
472 * Return an unmodifiable list of IdentifierToken representing
473 * classes specified as imports.
474 */
475 public List getImportedClasses() {
476 return Collections.unmodifiableList(singles);
477 }
478
479 /**
480 * Extend an environment with my resolve() method.
481 */
482 public Environment newEnvironment(Environment env) {
483 return new ImportEnvironment(env, this);
484 }
485}
486
487final
488class ImportEnvironment extends Environment {
489 Imports imports;
490
491 ImportEnvironment(Environment env, Imports imports) {
492 super(env, env.getSource());
493 this.imports = imports;
494 }
495
496 public Identifier resolve(Identifier nm) throws ClassNotFound {
497 return imports.resolve(this, nm);
498 }
499
500 public Imports getImports() {
501 return imports;
502 }
503}