001: /*
002: * Copyright 1994-2003 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.tools.java;
027:
028: import java.util.Hashtable;
029: import java.util.Vector;
030: import java.util.Enumeration;
031: import java.util.List;
032: import java.util.Collections;
033: import java.io.IOException;
034:
035: /**
036: * This class describes the classes and packages imported
037: * from a source file. A Hashtable called bindings is maintained
038: * to quickly map symbol names to classes. This table is flushed
039: * everytime a new import is added.
040: *
041: * A class name is resolved as follows:
042: * - if it is a qualified name then return the corresponding class
043: * - if the name corresponds to an individually imported class then return that class
044: * - check if the class is defined in any of the imported packages,
045: * if it is then return it, make sure it is defined in only one package
046: * - assume that the class is defined in the current package
047: *
048: * WARNING: The contents of this source file are not part of any
049: * supported API. Code that depends on them does so at its own risk:
050: * they are subject to change or removal without notice.
051: */
052:
053: public class Imports implements Constants {
054: /**
055: * The current package, which is implicitly imported,
056: * and has precedence over other imported packages.
057: */
058: Identifier currentPackage = idNull;
059:
060: /**
061: * A location for the current package declaration. Used to
062: * report errors against the current package.
063: */
064: long currentPackageWhere = 0;
065:
066: /**
067: * The imported classes, including memoized imports from packages.
068: */
069: Hashtable classes = new Hashtable();
070:
071: /**
072: * The imported package identifiers. This will not contain duplicate
073: * imports for the same package. It will also not contain the
074: * current package.
075: */
076: Vector packages = new Vector();
077:
078: /**
079: * The (originally) imported classes.
080: * A vector of IdentifierToken.
081: */
082: Vector singles = new Vector();
083:
084: /**
085: * Are the import names checked yet?
086: */
087: protected int checked;
088:
089: /**
090: * Constructor, always import java.lang.
091: */
092: public Imports(Environment env) {
093: addPackage(idJavaLang);
094: }
095:
096: /**
097: * Check the names of the imports.
098: */
099: public synchronized void resolve(Environment env) {
100: if (checked != 0) {
101: return;
102: }
103: checked = -1;
104:
105: // After all class information has been read, now we can
106: // safely inspect import information for errors.
107: // If we did this before all parsing was finished,
108: // we could get vicious circularities, since files can
109: // import each others' classes.
110:
111: // A note: the resolution of the package java.lang takes place
112: // in the sun.tools.javac.BatchEnvironment#setExemptPackages().
113:
114: // Make sure that the current package's name does not collide
115: // with the name of an existing class. (bug 4101529)
116: //
117: // This change has been backed out because, on WIN32, it
118: // failed to distinguish between java.awt.event and
119: // java.awt.Event when looking for a directory. We will
120: // add this back in later.
121: //
122: // if (currentPackage != idNull) {
123: // Identifier resolvedName =
124: // env.resolvePackageQualifiedName(currentPackage);
125: //
126: // Identifier className = resolvedName.getTopName();
127: //
128: // if (importable(className, env)) {
129: // // The name of the current package is also the name
130: // // of a class.
131: // env.error(currentPackageWhere, "package.class.conflict",
132: // currentPackage, className);
133: // }
134: // }
135:
136: Vector resolvedPackages = new Vector();
137: for (Enumeration e = packages.elements(); e.hasMoreElements();) {
138: IdentifierToken t = (IdentifierToken) e.nextElement();
139: Identifier nm = t.getName();
140: long where = t.getWhere();
141:
142: // Check to see if this package is exempt from the "exists"
143: // check. See the note in
144: // sun.tools.javac.BatchEnvironment#setExemptPackages()
145: // for more information.
146: if (env.isExemptPackage(nm)) {
147: resolvedPackages.addElement(t);
148: continue;
149: }
150:
151: // (Note: This code is moved from BatchParser.importPackage().)
152: try {
153: Identifier rnm = env.resolvePackageQualifiedName(nm);
154: if (importable(rnm, env)) {
155: // This name is a real class; better not be a package too.
156: if (env.getPackage(rnm.getTopName()).exists()) {
157: env.error(where, "class.and.package", rnm
158: .getTopName());
159: }
160: // Pass an "inner" name to the imports.
161: if (!rnm.isInner())
162: rnm = Identifier.lookupInner(rnm, idNull);
163: nm = rnm;
164: } else if (!env.getPackage(nm).exists()) {
165: env.error(where, "package.not.found", nm, "import");
166: } else if (rnm.isInner()) {
167: // nm exists, and rnm.getTopName() is a parent package
168: env.error(where, "class.and.package", rnm
169: .getTopName());
170: }
171: resolvedPackages.addElement(new IdentifierToken(where,
172: nm));
173: } catch (IOException ee) {
174: env.error(where, "io.exception", "import");
175: }
176: }
177: packages = resolvedPackages;
178:
179: for (Enumeration e = singles.elements(); e.hasMoreElements();) {
180: IdentifierToken t = (IdentifierToken) e.nextElement();
181: Identifier nm = t.getName();
182: long where = t.getWhere();
183: Identifier pkg = nm.getQualifier();
184:
185: // (Note: This code is moved from BatchParser.importClass().)
186: nm = env.resolvePackageQualifiedName(nm);
187: if (!env.classExists(nm.getTopName())) {
188: env.error(where, "class.not.found", nm, "import");
189: }
190:
191: // (Note: This code is moved from Imports.addClass().)
192: Identifier snm = nm.getFlatName().getName();
193:
194: // make sure it isn't already imported explicitly
195: Identifier className = (Identifier) classes.get(snm);
196: if (className != null) {
197: Identifier f1 = Identifier.lookup(className
198: .getQualifier(), className.getFlatName());
199: Identifier f2 = Identifier.lookup(nm.getQualifier(), nm
200: .getFlatName());
201: if (!f1.equals(f2)) {
202: env.error(where, "ambig.class", nm, className);
203: }
204: }
205: classes.put(snm, nm);
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
228: .getClassDefinitionNoCheck(env);
229:
230: // Get the true name of the package containing this class.
231: // `pkg' from above is insufficient. It includes the
232: // names of our enclosing classes. Fix for 4086815.
233: Identifier importedPackage = def.getName()
234: .getQualifier();
235:
236: // Walk out the outerClass chain, ensuring that each level
237: // is visible from our perspective.
238: for (; def != null; def = def.getOuterClass()) {
239: if (def.isPrivate()
240: || !(def.isPublic() || importedPackage
241: .equals(currentPackage))) {
242: env.error(where, "cant.access.class", def);
243: break;
244: }
245: }
246: } catch (AmbiguousClass ee) {
247: env.error(where, "ambig.class", ee.name1, ee.name2);
248: } catch (ClassNotFound ee) {
249: env.error(where, "class.not.found", ee.name, "import");
250: }
251: }
252: checked = 1;
253: }
254:
255: /**
256: * Lookup a class, given the current set of imports,
257: * AmbiguousClass exception is thrown if the name can be
258: * resolved in more than one way. A ClassNotFound exception
259: * is thrown if the class is not found in the imported classes
260: * and packages.
261: */
262: public synchronized Identifier resolve(Environment env,
263: Identifier nm) throws ClassNotFound {
264: if (tracing)
265: env.dtEnter("Imports.resolve: " + nm);
266:
267: // If the class has the special ambiguous prefix, then we will
268: // get the original AmbiguousClass exception by removing the
269: // prefix and proceeding in the normal fashion.
270: // (part of solution for 4059855)
271: if (nm.hasAmbigPrefix()) {
272: nm = nm.removeAmbigPrefix();
273: }
274:
275: if (nm.isQualified()) {
276: // Don't bother it is already qualified
277: if (tracing)
278: env.dtExit("Imports.resolve: QUALIFIED " + nm);
279: return nm;
280: }
281:
282: if (checked <= 0) {
283: checked = 0;
284: resolve(env);
285: }
286:
287: // Check if it was imported before
288: Identifier className = (Identifier) classes.get(nm);
289: if (className != null) {
290: if (tracing)
291: env
292: .dtExit("Imports.resolve: PREVIOUSLY IMPORTED "
293: + nm);
294: return className;
295: }
296:
297: // Note: the section below has changed a bit during the fix
298: // for bug 4093217. The current package is no longer grouped
299: // with the rest of the import-on-demands; it is now checked
300: // separately. Also, the list of import-on-demands is now
301: // guarranteed to be duplicate-free, so the code below can afford
302: // to be a bit simpler.
303:
304: // First we look in the current package. The current package
305: // is given precedence over the rest of the import-on-demands,
306: // which means, among other things, that a class in the current
307: // package cannot be ambiguous.
308: Identifier id = Identifier.lookup(currentPackage, nm);
309: if (importable(id, env)) {
310: className = id;
311: } else {
312: // If it isn't in the current package, try to find it in
313: // our import-on-demands.
314: Enumeration e = packages.elements();
315: while (e.hasMoreElements()) {
316: IdentifierToken t = (IdentifierToken) e.nextElement();
317: id = Identifier.lookup(t.getName(), nm);
318:
319: if (importable(id, env)) {
320: if (className == null) {
321: // We haven't found any other matching classes yet.
322: // Set className to what we've found and continue
323: // looking for an ambiguity.
324: className = id;
325: } else {
326: if (tracing)
327: env.dtExit("Imports.resolve: AMBIGUOUS "
328: + nm);
329:
330: // We've found an ambiguity.
331: throw new AmbiguousClass(className, id);
332: }
333: }
334: }
335: }
336:
337: // Make sure a class was found
338: if (className == null) {
339: if (tracing)
340: env.dtExit("Imports.resolve: NOT FOUND " + nm);
341: throw new ClassNotFound(nm);
342: }
343:
344: // Remember the binding
345: classes.put(nm, className);
346: if (tracing)
347: env.dtExit("Imports.resolve: FIRST IMPORT " + nm);
348: return className;
349: }
350:
351: /**
352: * Check to see if 'id' names an importable class in `env'.
353: * This method was made public and static for utility.
354: */
355: static public boolean importable(Identifier id, Environment env) {
356: if (!id.isInner()) {
357: return env.classExists(id);
358: } else if (!env.classExists(id.getTopName())) {
359: return false;
360: } else {
361: // load the top class and look inside it
362: try {
363: // There used to be a call to...
364: // env.getClassDeclaration(id.getTopName());
365: // ...here. It has been replaced with the
366: // two statements below. These should be functionally
367: // the same except for the fact that
368: // getClassDefinitionNoCheck() does not call
369: // basicCheck(). This allows us to avoid a circular
370: // need to do basicChecking that can arise with
371: // certain patterns of importing and inheritance.
372: // This is a fix for a variant of bug 4086139.
373: //
374: // Note: the special case code in env.getClassDefinition()
375: // which handles inner class names is not replicated below.
376: // This should be okay, as we are looking up id.getTopName(),
377: // not id.
378: ClassDeclaration decl = env.getClassDeclaration(id
379: .getTopName());
380: ClassDefinition c = decl.getClassDefinitionNoCheck(env);
381:
382: return c.innerClassExists(id.getFlatName().getTail());
383: } catch (ClassNotFound ee) {
384: return false;
385: }
386: }
387: }
388:
389: /**
390: * Suppose a resolve() call has failed.
391: * This routine can be used silently to give a reasonable
392: * default qualification (the current package) to the identifier.
393: * This decision is recorded for future reference.
394: */
395: public synchronized Identifier forceResolve(Environment env,
396: Identifier nm) {
397: if (nm.isQualified())
398: return nm;
399:
400: Identifier className = (Identifier) classes.get(nm);
401: if (className != null) {
402: return className;
403: }
404:
405: className = Identifier.lookup(currentPackage, nm);
406:
407: classes.put(nm, className);
408: return className;
409: }
410:
411: /**
412: * Add a class import
413: */
414: public synchronized void addClass(IdentifierToken t) {
415: singles.addElement(t);
416: }
417:
418: // for compatibility
419: public void addClass(Identifier nm) throws AmbiguousClass {
420: addClass(new IdentifierToken(nm));
421: }
422:
423: /**
424: * Add a package import, or perhaps an inner class scope.
425: * Ignore any duplicate imports.
426: */
427: public synchronized void addPackage(IdentifierToken t) {
428: final Identifier name = t.getName();
429:
430: // If this is a duplicate import for the current package,
431: // ignore it.
432: if (name == currentPackage) {
433: return;
434: }
435:
436: // If this is a duplicate of a package which has already been
437: // added to the list, ignore it.
438: final int size = packages.size();
439: for (int i = 0; i < size; i++) {
440: if (name == ((IdentifierToken) packages.elementAt(i))
441: .getName()) {
442: return;
443: }
444: }
445:
446: // Add the package to the list.
447: packages.addElement(t);
448: }
449:
450: // for compatibility
451: public void addPackage(Identifier id) {
452: addPackage(new IdentifierToken(id));
453: }
454:
455: /**
456: * Specify the current package with an IdentifierToken.
457: */
458: public synchronized void setCurrentPackage(IdentifierToken t) {
459: currentPackage = t.getName();
460: currentPackageWhere = t.getWhere();
461: }
462:
463: /**
464: * Specify the current package
465: */
466: public synchronized void setCurrentPackage(Identifier id) {
467: currentPackage = id;
468: }
469:
470: /**
471: * Report the current package
472: */
473: public Identifier getCurrentPackage() {
474: return currentPackage;
475: }
476:
477: /**
478: * Return an unmodifiable list of IdentifierToken representing
479: * packages specified as imports.
480: */
481: public List getImportedPackages() {
482: return Collections.unmodifiableList(packages);
483: }
484:
485: /**
486: * Return an unmodifiable list of IdentifierToken representing
487: * classes specified as imports.
488: */
489: public List getImportedClasses() {
490: return Collections.unmodifiableList(singles);
491: }
492:
493: /**
494: * Extend an environment with my resolve() method.
495: */
496: public Environment newEnvironment(Environment env) {
497: return new ImportEnvironment(env, this );
498: }
499: }
500:
501: final class ImportEnvironment extends Environment {
502: Imports imports;
503:
504: ImportEnvironment(Environment env, Imports imports) {
505: super (env, env.getSource());
506: this .imports = imports;
507: }
508:
509: public Identifier resolve(Identifier nm) throws ClassNotFound {
510: return imports.resolve(this , nm);
511: }
512:
513: public Imports getImports() {
514: return imports;
515: }
516: }
|