1 /*******************************************************************************
2 * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.lookup;
13 import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration;
14 import net.sourceforge.phpdt.internal.compiler.ast.ImportReference;
15 import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
16 import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
17 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
18 import net.sourceforge.phpdt.internal.compiler.util.CharOperation;
19 import net.sourceforge.phpdt.internal.compiler.util.CompoundNameVector;
20 import net.sourceforge.phpdt.internal.compiler.util.HashtableOfType;
21 import net.sourceforge.phpdt.internal.compiler.util.ObjectVector;
22 import net.sourceforge.phpdt.internal.compiler.util.SimpleNameVector;
24 public class CompilationUnitScope extends Scope {
25 public LookupEnvironment environment;
26 public CompilationUnitDeclaration referenceContext;
27 public char[][] currentPackageName;
28 public PackageBinding fPackage;
29 public ImportBinding[] imports;
31 public SourceTypeBinding[] topLevelTypes;
33 private CompoundNameVector qualifiedReferences;
34 private SimpleNameVector simpleNameReferences;
35 private ObjectVector referencedTypes;
37 public CompilationUnitScope(CompilationUnitDeclaration unit, LookupEnvironment environment) {
38 super(COMPILATION_UNIT_SCOPE, null);
39 this.environment = environment;
40 this.referenceContext = unit;
42 this.currentPackageName = unit.currentPackage == null ? NoCharChar : unit.currentPackage.tokens;
44 if (environment.options.produceReferenceInfo) {
45 this.qualifiedReferences = new CompoundNameVector();
46 this.simpleNameReferences = new SimpleNameVector();
47 this.referencedTypes = new ObjectVector();
49 this.qualifiedReferences = null; // used to test if dependencies should be recorded
50 this.simpleNameReferences = null;
51 this.referencedTypes = null;
54 void buildFieldsAndMethods() {
55 for (int i = 0, length = topLevelTypes.length; i < length; i++)
56 topLevelTypes[i].scope.buildFieldsAndMethods();
58 void buildTypeBindings() {
59 topLevelTypes = new SourceTypeBinding[0]; // want it initialized if the package cannot be resolved
60 if (referenceContext.compilationResult.compilationUnit != null) {
61 char[][] expectedPackageName = referenceContext.compilationResult.compilationUnit.getPackageName();
62 if (expectedPackageName != null && !CharOperation.equals(currentPackageName, expectedPackageName)) {
63 problemReporter().packageIsNotExpectedPackage(referenceContext);
64 currentPackageName = expectedPackageName.length == 0 ? NoCharChar : expectedPackageName;
67 if (currentPackageName == NoCharChar) {
68 if ((fPackage = environment.defaultPackage) == null) {
69 problemReporter().mustSpecifyPackage(referenceContext);
73 if ((fPackage = environment.createPackage(currentPackageName)) == null) {
74 problemReporter().packageCollidesWithType(referenceContext);
77 recordQualifiedReference(currentPackageName); // always dependent on your own package
80 // Skip typeDeclarations which know of previously reported errors
81 TypeDeclaration[] types = referenceContext.types;
82 int typeLength = (types == null) ? 0 : types.length;
83 topLevelTypes = new SourceTypeBinding[typeLength];
85 nextType: for (int i = 0; i < typeLength; i++) {
86 TypeDeclaration typeDecl = types[i];
87 ReferenceBinding typeBinding = fPackage.getType0(typeDecl.name);
88 recordSimpleReference(typeDecl.name); // needed to detect collision cases
89 if (typeBinding != null && !(typeBinding instanceof UnresolvedReferenceBinding)) {
90 // if a type exists, it must be a valid type - cannot be a NotFound problem type
91 // unless its an unresolved type which is now being defined
92 problemReporter().duplicateTypes(referenceContext, typeDecl);
95 boolean packageExists = currentPackageName == NoCharChar
96 ? environment.getTopLevelPackage(typeDecl.name) != null
97 : (fPackage.getPackage(typeDecl.name)) != null;
99 // if a package exists, it must be a valid package - cannot be a NotFound problem package
100 problemReporter().typeCollidesWithPackage(referenceContext, typeDecl);
104 if ((typeDecl.modifiers & AccPublic) != 0) {
106 if ((mainTypeName = referenceContext.getMainTypeName()) != null // mainTypeName == null means that implementor of ICompilationUnit decided to return null
107 && !CharOperation.equals(mainTypeName, typeDecl.name)) {
108 problemReporter().publicClassMustMatchFileName(referenceContext, typeDecl);
113 ClassScope child = new ClassScope(this, typeDecl);
114 topLevelTypes[count++] = child.buildType(null, fPackage);
117 // shrink topLevelTypes... only happens if an error was reported
118 if (count != topLevelTypes.length)
119 System.arraycopy(topLevelTypes, 0, topLevelTypes = new SourceTypeBinding[count], 0, count);
121 void checkAndSetImports() {
122 // initialize the default imports if necessary... share the default java.lang.* import
123 if (environment.defaultImports == null) {
124 Binding importBinding = environment.getTopLevelPackage(JAVA);
125 if (importBinding != null)
126 importBinding = ((PackageBinding) importBinding).getTypeOrPackage(JAVA_LANG[1]);
128 // abort if java.lang cannot be found...
129 if (importBinding == null || !importBinding.isValidBinding())
130 problemReporter().isClassPathCorrect(JAVA_LANG_OBJECT, referenceCompilationUnit());
132 environment.defaultImports = new ImportBinding[] {new ImportBinding(JAVA_LANG, true, importBinding, null)};
134 if (referenceContext.imports == null) {
135 imports = environment.defaultImports;
139 // allocate the import array, add java.lang.* by default
140 int numberOfStatements = referenceContext.imports.length;
141 int numberOfImports = numberOfStatements + 1;
142 for (int i = 0; i < numberOfStatements; i++) {
143 ImportReference importReference = referenceContext.imports[i];
144 if (importReference.onDemand && CharOperation.equals(JAVA_LANG, importReference.tokens)) {
149 ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
150 resolvedImports[0] = environment.defaultImports[0];
153 nextImport : for (int i = 0; i < numberOfStatements; i++) {
154 ImportReference importReference = referenceContext.imports[i];
155 char[][] compoundName = importReference.tokens;
157 // skip duplicates or imports of the current package
158 for (int j = 0; j < index; j++)
159 if (resolvedImports[j].onDemand == importReference.onDemand)
160 if (CharOperation.equals(compoundName, resolvedImports[j].compoundName))
162 if (importReference.onDemand == true)
163 if (CharOperation.equals(compoundName, currentPackageName))
166 if (importReference.onDemand) {
167 Binding importBinding = findOnDemandImport(compoundName);
168 if (!importBinding.isValidBinding())
169 continue nextImport; // we report all problems in faultInImports()
170 resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding, importReference);
172 resolvedImports[index++] = new ImportBinding(compoundName, false, null, importReference);
176 // shrink resolvedImports... only happens if an error was reported
177 if (resolvedImports.length > index)
178 System.arraycopy(resolvedImports, 0, resolvedImports = new ImportBinding[index], 0, index);
179 imports = resolvedImports;
181 void connectTypeHierarchy() {
182 for (int i = 0, length = topLevelTypes.length; i < length; i++)
183 topLevelTypes[i].scope.connectTypeHierarchy();
185 void faultInImports() {
186 if (referenceContext.imports == null)
189 // collect the top level type names if a single type import exists
190 int numberOfStatements = referenceContext.imports.length;
191 HashtableOfType typesBySimpleNames = null;
192 for (int i = 0; i < numberOfStatements; i++) {
193 if (!referenceContext.imports[i].onDemand) {
194 typesBySimpleNames = new HashtableOfType(topLevelTypes.length + numberOfStatements);
195 for (int j = 0, length = topLevelTypes.length; j < length; j++)
196 typesBySimpleNames.put(topLevelTypes[j].sourceName, topLevelTypes[j]);
201 // allocate the import array, add java.lang.* by default
202 int numberOfImports = numberOfStatements + 1;
203 for (int i = 0; i < numberOfStatements; i++) {
204 ImportReference importReference = referenceContext.imports[i];
205 if (importReference.onDemand && CharOperation.equals(JAVA_LANG, importReference.tokens)) {
210 ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
211 resolvedImports[0] = environment.defaultImports[0];
214 nextImport : for (int i = 0; i < numberOfStatements; i++) {
215 ImportReference importReference = referenceContext.imports[i];
216 char[][] compoundName = importReference.tokens;
218 // skip duplicates or imports of the current package
219 for (int j = 0; j < index; j++)
220 if (resolvedImports[j].onDemand == importReference.onDemand)
221 if (CharOperation.equals(compoundName, resolvedImports[j].compoundName)) {
224 if (importReference.onDemand == true)
225 if (CharOperation.equals(compoundName, currentPackageName)) {
228 if (importReference.onDemand) {
229 Binding importBinding = findOnDemandImport(compoundName);
230 if (!importBinding.isValidBinding()) {
231 problemReporter().importProblem(importReference, importBinding);
234 resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding, importReference);
236 Binding typeBinding = findSingleTypeImport(compoundName);
237 if (!typeBinding.isValidBinding()) {
238 problemReporter().importProblem(importReference, typeBinding);
241 if (typeBinding instanceof PackageBinding) {
242 problemReporter().cannotImportPackage(importReference);
245 ReferenceBinding existingType = typesBySimpleNames.get(compoundName[compoundName.length - 1]);
246 if (existingType != null) {
247 // duplicate test above should have caught this case, but make sure
248 if (existingType == typeBinding) {
251 // either the type collides with a top level type or another imported type
252 for (int j = 0, length = topLevelTypes.length; j < length; j++) {
253 if (CharOperation.equals(topLevelTypes[j].sourceName, existingType.sourceName)) {
254 problemReporter().conflictingImport(importReference);
258 problemReporter().duplicateImport(importReference);
261 resolvedImports[index++] = new ImportBinding(compoundName, false, typeBinding, importReference);
262 typesBySimpleNames.put(compoundName[compoundName.length - 1], (ReferenceBinding) typeBinding);
266 // shrink resolvedImports... only happens if an error was reported
267 if (resolvedImports.length > index)
268 System.arraycopy(resolvedImports, 0, resolvedImports = new ImportBinding[index], 0, index);
269 imports = resolvedImports;
271 public void faultInTypes() {
274 for (int i = 0, length = topLevelTypes.length; i < length; i++)
275 topLevelTypes[i].faultInTypesForFieldsAndMethods();
277 private Binding findOnDemandImport(char[][] compoundName) {
278 recordQualifiedReference(compoundName);
280 Binding binding = environment.getTopLevelPackage(compoundName[0]);
282 int length = compoundName.length;
283 foundNothingOrType: if (binding != null) {
284 PackageBinding packageBinding = (PackageBinding) binding;
286 binding = packageBinding.getTypeOrPackage(compoundName[i++]);
287 if (binding == null || !binding.isValidBinding()) {
289 break foundNothingOrType;
291 if (!(binding instanceof PackageBinding))
292 break foundNothingOrType;
294 packageBinding = (PackageBinding) binding;
296 return packageBinding;
299 ReferenceBinding type;
300 if (binding == null) {
301 if (environment.defaultPackage == null
302 || environment.options.complianceLevel >= CompilerOptions.JDK1_4){
303 return new ProblemReferenceBinding(
304 CharOperation.subarray(compoundName, 0, i),
307 type = findType(compoundName[0], environment.defaultPackage, environment.defaultPackage);
308 if (type == null || !type.isValidBinding())
309 return new ProblemReferenceBinding(
310 CharOperation.subarray(compoundName, 0, i),
312 i = 1; // reset to look for member types inside the default package type
314 type = (ReferenceBinding) binding;
317 for (; i < length; i++) {
318 // does not look for inherited member types on purpose
319 if ((type = type.getMemberType(compoundName[i])) == null)
320 return new ProblemReferenceBinding(
321 CharOperation.subarray(compoundName, 0, i + 1),
324 if (!type.canBeSeenBy(fPackage))
325 return new ProblemReferenceBinding(compoundName, type, NotVisible);
328 private Binding findSingleTypeImport(char[][] compoundName) {
329 if (compoundName.length == 1) {
330 // findType records the reference
331 // the name cannot be a package
332 if (environment.defaultPackage == null
333 || environment.options.complianceLevel >= CompilerOptions.JDK1_4)
334 return new ProblemReferenceBinding(compoundName, NotFound);
335 ReferenceBinding typeBinding = findType(compoundName[0], environment.defaultPackage, fPackage);
336 if (typeBinding == null)
337 return new ProblemReferenceBinding(compoundName, NotFound);
341 return findOnDemandImport(compoundName);
343 /* Answer the problem reporter to use for raising new problems.
345 * Note that as a side-effect, this updates the current reference context
346 * (unit, type or method) in case the problem handler decides it is necessary
350 public ProblemReporter problemReporter() {
351 ProblemReporter problemReporter = referenceContext.problemReporter;
352 problemReporter.referenceContext = referenceContext;
353 return problemReporter;
357 What do we hold onto:
359 1. when we resolve 'a.b.c', say we keep only 'a.b.c'
360 & when we fail to resolve 'c' in 'a.b', lets keep 'a.b.c'
361 THEN when we come across a new/changed/removed item named 'a.b.c',
362 we would find all references to 'a.b.c'
363 -> This approach fails because every type is resolved in every onDemand import to
364 detect collision cases... so the references could be 10 times bigger than necessary.
366 2. when we resolve 'a.b.c', lets keep 'a.b' & 'c'
367 & when we fail to resolve 'c' in 'a.b', lets keep 'a.b' & 'c'
368 THEN when we come across a new/changed/removed item named 'a.b.c',
369 we would find all references to 'a.b' & 'c'
370 -> This approach does not have a space problem but fails to handle collision cases.
371 What happens if a type is added named 'a.b'? We would search for 'a' & 'b' but
372 would not find a match.
374 3. when we resolve 'a.b.c', lets keep 'a', 'a.b' & 'a', 'b', 'c'
375 & when we fail to resolve 'c' in 'a.b', lets keep 'a', 'a.b' & 'a', 'b', 'c'
376 THEN when we come across a new/changed/removed item named 'a.b.c',
377 we would find all references to 'a.b' & 'c'
378 OR 'a.b' -> 'a' & 'b'
380 -> As long as each single char[] is interned, we should not have a space problem
381 and can handle collision cases.
383 4. when we resolve 'a.b.c', lets keep 'a.b' & 'a', 'b', 'c'
384 & when we fail to resolve 'c' in 'a.b', lets keep 'a.b' & 'a', 'b', 'c'
385 THEN when we come across a new/changed/removed item named 'a.b.c',
386 we would find all references to 'a.b' & 'c'
387 OR 'a.b' -> 'a' & 'b' in the simple name collection
388 OR 'a' -> 'a' in the simple name collection
389 -> As long as each single char[] is interned, we should not have a space problem
390 and can handle collision cases.
392 void recordQualifiedReference(char[][] qualifiedName) {
393 if (qualifiedReferences == null) return; // not recording dependencies
395 int length = qualifiedName.length;
397 while (!qualifiedReferences.contains(qualifiedName)) {
398 qualifiedReferences.add(qualifiedName);
400 recordSimpleReference(qualifiedName[0]);
401 recordSimpleReference(qualifiedName[1]);
405 recordSimpleReference(qualifiedName[length]);
406 System.arraycopy(qualifiedName, 0, qualifiedName = new char[length][], 0, length);
408 } else if (length == 1) {
409 recordSimpleReference(qualifiedName[0]);
412 void recordReference(char[][] qualifiedEnclosingName, char[] simpleName) {
413 recordQualifiedReference(qualifiedEnclosingName);
414 recordSimpleReference(simpleName);
416 void recordSimpleReference(char[] simpleName) {
417 if (simpleNameReferences == null) return; // not recording dependencies
419 if (!simpleNameReferences.contains(simpleName))
420 simpleNameReferences.add(simpleName);
422 void recordTypeReference(TypeBinding type) {
423 if (referencedTypes == null) return; // not recording dependencies
425 if (type.isArrayType())
426 type = ((ArrayBinding) type).leafComponentType;
427 if (!type.isBaseType() && !referencedTypes.containsIdentical(type))
428 referencedTypes.add(type);
430 void recordTypeReferences(TypeBinding[] types) {
431 if (qualifiedReferences == null) return; // not recording dependencies
432 if (types == null || types.length == 0) return;
434 for (int i = 0, max = types.length; i < max; i++) {
435 // No need to record supertypes of method arguments & thrown exceptions, just the compoundName
436 // If a field/method is retrieved from such a type then a separate call does the job
437 TypeBinding type = types[i];
438 if (type.isArrayType())
439 type = ((ArrayBinding) type).leafComponentType;
440 if (!type.isBaseType()) {
441 ReferenceBinding actualType = (ReferenceBinding) type;
442 if (!actualType.isLocalType())
443 recordQualifiedReference(actualType.isMemberType()
444 ? CharOperation.splitOn('.', actualType.readableName())
445 : actualType.compoundName);
449 Binding resolveSingleTypeImport(ImportBinding importBinding) {
450 if (importBinding.resolvedImport == null) {
451 importBinding.resolvedImport = findSingleTypeImport(importBinding.compoundName);
452 if (!importBinding.resolvedImport.isValidBinding() || importBinding.resolvedImport instanceof PackageBinding) {
453 if (this.imports != null){
454 ImportBinding[] newImports = new ImportBinding[imports.length - 1];
455 for (int i = 0, n = 0, max = this.imports.length; i < max; i++)
456 if (this.imports[i] != importBinding){
457 newImports[n++] = this.imports[i];
459 this.imports = newImports;
464 return importBinding.resolvedImport;
466 public void storeDependencyInfo() {
467 // add the type hierarchy of each referenced type
468 // cannot do early since the hierarchy may not be fully resolved
469 for (int i = 0; i < referencedTypes.size; i++) { // grows as more types are added
470 ReferenceBinding type = (ReferenceBinding) referencedTypes.elementAt(i);
471 if (!type.isLocalType()) {
472 recordQualifiedReference(type.isMemberType()
473 ? CharOperation.splitOn('.', type.readableName())
474 : type.compoundName);
475 ReferenceBinding enclosing = type.enclosingType();
476 if (enclosing != null && !referencedTypes.containsIdentical(enclosing))
477 referencedTypes.add(enclosing); // to record its supertypes
479 ReferenceBinding superclass = type.superclass();
480 if (superclass != null && !referencedTypes.containsIdentical(superclass))
481 referencedTypes.add(superclass); // to record its supertypes
482 ReferenceBinding[] interfaces = type.superInterfaces();
483 if (interfaces != null && interfaces.length > 0)
484 for (int j = 0, length = interfaces.length; j < length; j++)
485 if (!referencedTypes.containsIdentical(interfaces[j]))
486 referencedTypes.add(interfaces[j]); // to record its supertypes
489 int size = qualifiedReferences.size;
490 char[][][] qualifiedRefs = new char[size][][];
491 for (int i = 0; i < size; i++)
492 qualifiedRefs[i] = qualifiedReferences.elementAt(i);
493 referenceContext.compilationResult.qualifiedReferences = qualifiedRefs;
495 size = simpleNameReferences.size;
496 char[][] simpleRefs = new char[size][];
497 for (int i = 0; i < size; i++)
498 simpleRefs[i] = simpleNameReferences.elementAt(i);
499 referenceContext.compilationResult.simpleNameReferences = simpleRefs;
501 public String toString() {
502 return "--- CompilationUnit Scope : " + new String(referenceContext.getFileName()); //$NON-NLS-1$
504 public void verifyMethods(MethodVerifier verifier) {
505 for (int i = 0, length = topLevelTypes.length; i < length; i++)
506 topLevelTypes[i].verifyMethods(verifier);