dc4f59f5cd7a7444d77576105fe1a356a77e1e9f
[phpeclipse.git] /
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.lookup;
12
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;
23
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;
30         
31         public SourceTypeBinding[] topLevelTypes;
32
33         private CompoundNameVector qualifiedReferences;
34         private SimpleNameVector simpleNameReferences;
35         private ObjectVector referencedTypes;
36
37 public CompilationUnitScope(CompilationUnitDeclaration unit, LookupEnvironment environment) {
38         super(COMPILATION_UNIT_SCOPE, null);
39         this.environment = environment;
40         this.referenceContext = unit;
41         unit.scope = this;
42         this.currentPackageName = unit.currentPackage == null ? NoCharChar : unit.currentPackage.tokens;
43
44         if (environment.options.produceReferenceInfo) {
45                 this.qualifiedReferences = new CompoundNameVector();
46                 this.simpleNameReferences = new SimpleNameVector();
47                 this.referencedTypes = new ObjectVector();
48         } else {
49                 this.qualifiedReferences = null; // used to test if dependencies should be recorded
50                 this.simpleNameReferences = null;
51                 this.referencedTypes = null;
52         }
53 }
54 void buildFieldsAndMethods() {
55         for (int i = 0, length = topLevelTypes.length; i < length; i++)
56                 topLevelTypes[i].scope.buildFieldsAndMethods();
57 }
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;
65                 }
66         }
67         if (currentPackageName == NoCharChar) {
68                 if ((fPackage = environment.defaultPackage) == null) {
69                         problemReporter().mustSpecifyPackage(referenceContext);
70                         return;
71                 }
72         } else {
73                 if ((fPackage = environment.createPackage(currentPackageName)) == null) {
74                         problemReporter().packageCollidesWithType(referenceContext);
75                         return;
76                 }
77                 recordQualifiedReference(currentPackageName); // always dependent on your own package
78         }
79
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];
84         int count = 0;
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);
93                         continue nextType;
94                 }
95                 boolean packageExists = currentPackageName == NoCharChar
96                         ? environment.getTopLevelPackage(typeDecl.name) != null
97                         : (fPackage.getPackage(typeDecl.name)) != null;
98                 if (packageExists) {
99                         // if a package exists, it must be a valid package - cannot be a NotFound problem package
100                         problemReporter().typeCollidesWithPackage(referenceContext, typeDecl);
101                         continue nextType;
102                 }
103
104                 if ((typeDecl.modifiers & AccPublic) != 0) {
105                         char[] mainTypeName;
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);
109                                 continue nextType;
110                         }
111                 }
112
113                 ClassScope child = new ClassScope(this, typeDecl);
114                 topLevelTypes[count++] = child.buildType(null, fPackage);
115         }
116
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);
120 }
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]);
127
128                 // abort if java.lang cannot be found...
129                 if (importBinding == null || !importBinding.isValidBinding())
130                         problemReporter().isClassPathCorrect(JAVA_LANG_OBJECT, referenceCompilationUnit());
131
132                 environment.defaultImports = new ImportBinding[] {new ImportBinding(JAVA_LANG, true, importBinding, null)};
133         }
134         if (referenceContext.imports == null) {
135                 imports = environment.defaultImports;
136                 return;
137         }
138
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)) {
145                         numberOfImports--;
146                         break;
147                 }
148         }
149         ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
150         resolvedImports[0] = environment.defaultImports[0];
151         int index = 1;
152
153         nextImport : for (int i = 0; i < numberOfStatements; i++) {
154                 ImportReference importReference = referenceContext.imports[i];
155                 char[][] compoundName = importReference.tokens;
156
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))
161                                         continue nextImport;
162                 if (importReference.onDemand == true)
163                         if (CharOperation.equals(compoundName, currentPackageName))
164                                 continue nextImport;
165
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);
171                 } else {
172                         resolvedImports[index++] = new ImportBinding(compoundName, false, null, importReference);
173                 }
174         }
175
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;
180 }
181 void connectTypeHierarchy() {
182         for (int i = 0, length = topLevelTypes.length; i < length; i++)
183                 topLevelTypes[i].scope.connectTypeHierarchy();
184 }
185 void faultInImports() {
186         if (referenceContext.imports == null)
187                 return;
188
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]);
197                         break;
198                 }
199         }
200
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)) {
206                         numberOfImports--;
207                         break;
208                 }
209         }
210         ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
211         resolvedImports[0] = environment.defaultImports[0];
212         int index = 1;
213
214         nextImport : for (int i = 0; i < numberOfStatements; i++) {
215                 ImportReference importReference = referenceContext.imports[i];
216                 char[][] compoundName = importReference.tokens;
217
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)) {
222                                         continue nextImport;
223                                 }
224                 if (importReference.onDemand == true)
225                         if (CharOperation.equals(compoundName, currentPackageName)) {
226                                 continue nextImport;
227                         }
228                 if (importReference.onDemand) {
229                         Binding importBinding = findOnDemandImport(compoundName);
230                         if (!importBinding.isValidBinding()) {
231                                 problemReporter().importProblem(importReference, importBinding);
232                                 continue nextImport;
233                         }
234                         resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding, importReference);
235                 } else {
236                         Binding typeBinding = findSingleTypeImport(compoundName);
237                         if (!typeBinding.isValidBinding()) {
238                                 problemReporter().importProblem(importReference, typeBinding);
239                                 continue nextImport;
240                         }
241                         if (typeBinding instanceof PackageBinding) {
242                                 problemReporter().cannotImportPackage(importReference);
243                                 continue nextImport;
244                         }
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) {
249                                         continue nextImport;
250                                 }
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);
255                                                 continue nextImport;
256                                         }
257                                 }
258                                 problemReporter().duplicateImport(importReference);
259                                 continue nextImport;
260                         }
261                         resolvedImports[index++] = new ImportBinding(compoundName, false, typeBinding, importReference);
262                         typesBySimpleNames.put(compoundName[compoundName.length - 1], (ReferenceBinding) typeBinding);
263                 }
264         }
265
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;
270 }
271 public void faultInTypes() {
272         faultInImports();
273
274         for (int i = 0, length = topLevelTypes.length; i < length; i++)
275                 topLevelTypes[i].faultInTypesForFieldsAndMethods();
276 }
277 private Binding findOnDemandImport(char[][] compoundName) {
278         recordQualifiedReference(compoundName);
279
280         Binding binding = environment.getTopLevelPackage(compoundName[0]);
281         int i = 1;
282         int length = compoundName.length;
283         foundNothingOrType: if (binding != null) {
284                 PackageBinding packageBinding = (PackageBinding) binding;
285                 while (i < length) {
286                         binding = packageBinding.getTypeOrPackage(compoundName[i++]);
287                         if (binding == null || !binding.isValidBinding()) {
288                                 binding = null;
289                                 break foundNothingOrType;
290                         }
291                         if (!(binding instanceof PackageBinding))
292                                 break foundNothingOrType;
293
294                         packageBinding = (PackageBinding) binding;
295                 }
296                 return packageBinding;
297         }
298
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),
305                                 NotFound);
306                 }
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),
311                                 NotFound);
312                 i = 1; // reset to look for member types inside the default package type
313         } else {
314                 type = (ReferenceBinding) binding;
315         }
316
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),
322                                 NotFound);
323         }
324         if (!type.canBeSeenBy(fPackage))
325                 return new ProblemReferenceBinding(compoundName, type, NotVisible);
326         return type;
327 }
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);
338                 else
339                         return typeBinding;
340         }
341         return findOnDemandImport(compoundName);
342 }
343 /* Answer the problem reporter to use for raising new problems.
344 *
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
347 * to abort.
348 */
349
350 public ProblemReporter problemReporter() {
351         ProblemReporter problemReporter = referenceContext.problemReporter;
352         problemReporter.referenceContext = referenceContext;
353         return problemReporter;
354 }
355
356 /*
357 What do we hold onto:
358
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.
365
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.
373
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'
379 OR 'a' -> '' & 'a'
380 -> As long as each single char[] is interned, we should not have a space problem
381  and can handle collision cases.
382
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.
391 */
392 void recordQualifiedReference(char[][] qualifiedName) {
393         if (qualifiedReferences == null) return; // not recording dependencies
394
395         int length = qualifiedName.length;
396         if (length > 1) {
397                 while (!qualifiedReferences.contains(qualifiedName)) {
398                         qualifiedReferences.add(qualifiedName);
399                         if (length == 2) {
400                                 recordSimpleReference(qualifiedName[0]);
401                                 recordSimpleReference(qualifiedName[1]);
402                                 return;
403                         }
404                         length--;
405                         recordSimpleReference(qualifiedName[length]);
406                         System.arraycopy(qualifiedName, 0, qualifiedName = new char[length][], 0, length);
407                 }
408         } else if (length == 1) {
409                 recordSimpleReference(qualifiedName[0]);
410         }
411 }
412 void recordReference(char[][] qualifiedEnclosingName, char[] simpleName) {
413         recordQualifiedReference(qualifiedEnclosingName);
414         recordSimpleReference(simpleName);
415 }
416 void recordSimpleReference(char[] simpleName) {
417         if (simpleNameReferences == null) return; // not recording dependencies
418
419         if (!simpleNameReferences.contains(simpleName))
420                 simpleNameReferences.add(simpleName);
421 }
422 void recordTypeReference(TypeBinding type) {
423         if (referencedTypes == null) return; // not recording dependencies
424
425         if (type.isArrayType())
426                 type = ((ArrayBinding) type).leafComponentType;
427         if (!type.isBaseType() && !referencedTypes.containsIdentical(type))
428                 referencedTypes.add(type);
429 }
430 void recordTypeReferences(TypeBinding[] types) {
431         if (qualifiedReferences == null) return; // not recording dependencies
432         if (types == null || types.length == 0) return;
433
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);
446                 }
447         }
448 }
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];
458                                         }
459                                 this.imports = newImports;
460                         }
461                         return null;
462                 }
463         }
464         return importBinding.resolvedImport;
465 }
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
478                 }
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
487         }
488
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;
494
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;
500 }
501 public String toString() {
502         return "--- CompilationUnit Scope : " + new String(referenceContext.getFileName()); //$NON-NLS-1$
503 }
504 public void verifyMethods(MethodVerifier verifier) {
505         for (int i = 0, length = topLevelTypes.length; i < length; i++)
506                 topLevelTypes[i].verifyMethods(verifier);
507 }
508 }