added -J option to preserve unmodified files in preexisting jarfile
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / lookup / ParameterizedTypeBinding.java
1 /*******************************************************************************
2  * Copyright (c) 2000-2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.lookup;
12
13 import java.util.Map;
14 import org.eclipse.jdt.core.compiler.CharOperation;
15 import org.eclipse.jdt.internal.compiler.ast.Wildcard;
16
17 /**
18  * A parameterized type encapsulates a type with type arguments,
19  */
20 public class ParameterizedTypeBinding extends ReferenceBinding implements Substitution {
21
22         public ReferenceBinding type; 
23         public TypeBinding[] arguments;
24         public LookupEnvironment environment; 
25         public char[] genericTypeSignature;
26         public ReferenceBinding superclass;
27         public ReferenceBinding[] superInterfaces;      
28         public FieldBinding[] fields;   
29         public ReferenceBinding[] memberTypes;
30         public MethodBinding[] methods;
31         private ReferenceBinding enclosingType;
32         
33         public ParameterizedTypeBinding(ReferenceBinding type, TypeBinding[] arguments,  ReferenceBinding enclosingType, LookupEnvironment environment){
34
35                 this.environment = environment;
36                 initialize(type, arguments);
37                 this.enclosingType = enclosingType; // never unresolved, never lazy per construction
38
39                 if (type instanceof UnresolvedReferenceBinding)
40                         ((UnresolvedReferenceBinding) type).addWrapper(this);
41                 if (arguments != null) {
42                         for (int i = 0, l = arguments.length; i < l; i++)
43                                 if (arguments[i] instanceof UnresolvedReferenceBinding)
44                                         ((UnresolvedReferenceBinding) arguments[i]).addWrapper(this);
45                 }
46         }
47
48         /**
49          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#canBeInstantiated()
50          */
51         public boolean canBeInstantiated() {
52                 return ((this.tagBits & HasDirectWildcard) == 0) // cannot instantiate param type with wildcard arguments
53                                                         && super.canBeInstantiated();
54         }
55         public int kind() {
56                 return PARAMETERIZED_TYPE;
57         }       
58         
59         /**
60          * Collect the substitutes into a map for certain type variables inside the receiver type
61          * e.g.   Collection<T>.findSubstitute(T, Collection<List<X>>):   T --> List<X>
62          */
63         public void collectSubstitutes(TypeBinding otherType, Map substitutes) {
64                 if (this.arguments == null) return;
65                 if (otherType instanceof ReferenceBinding) {
66                         // allow List<T> to match with LinkedList<String>
67                         ReferenceBinding equivalent = this;
68                 ReferenceBinding otherEquivalent = ((ReferenceBinding)otherType).findSuperTypeErasingTo((ReferenceBinding)this.type.erasure());
69                 if (otherEquivalent == null) {
70                         // allow LinkedList<String> to match List<T> (downcast scenario)
71                         equivalent = this.findSuperTypeErasingTo((ReferenceBinding)otherType.erasure());
72                         if (equivalent == null) return;
73                         otherEquivalent = (ReferenceBinding)otherType;
74                 }
75                 TypeBinding[] elements;
76                 switch (equivalent.kind()) {
77                         case Binding.GENERIC_TYPE :
78                                 elements = equivalent.typeVariables();
79                                 break;
80                         case Binding.PARAMETERIZED_TYPE :
81                                 elements = ((ParameterizedTypeBinding)equivalent).arguments;
82                                 break;
83                         default :
84                                 return;
85                 }
86                 TypeBinding[] otherElements;
87                 switch (otherEquivalent.kind()) {
88                         case Binding.GENERIC_TYPE :
89                                 otherElements = otherEquivalent.typeVariables();
90                                 break;
91                         case Binding.PARAMETERIZED_TYPE :
92                                 otherElements = ((ParameterizedTypeBinding)otherEquivalent).arguments;
93                                 break;
94                         case Binding.RAW_TYPE :
95                                 substitutes.clear(); // clear all variables to indicate raw generic method in the end
96                                 return;
97                         default :
98                                 return;
99                 }
100             for (int i = 0, length = elements.length; i < length; i++) {
101                 elements[i].collectSubstitutes(otherElements[i], substitutes);
102                 }
103             }
104         }
105         
106         /**
107          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#computeId()
108          */
109         public void computeId() {
110                 this.id = NoId;         
111         }
112
113         /**
114          * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#constantPoolName()
115          */
116         public char[] constantPoolName() {
117                 return this.type.constantPoolName(); // erasure
118         }
119
120         public ParameterizedMethodBinding createParameterizedMethod(MethodBinding originalMethod) {
121                 return new ParameterizedMethodBinding(this, originalMethod, originalMethod.isStatic());
122         }
123         
124         /**
125          * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#debugName()
126          */
127         public String debugName() {
128             StringBuffer nameBuffer = new StringBuffer(10);
129                 nameBuffer.append(this.type.sourceName());
130                 if (this.arguments != null) {
131                         nameBuffer.append('<');
132                     for (int i = 0, length = this.arguments.length; i < length; i++) {
133                         if (i > 0) nameBuffer.append(',');
134                         nameBuffer.append(this.arguments[i].debugName());
135                     }
136                     nameBuffer.append('>');
137                 }
138             return nameBuffer.toString();               
139         }
140
141         /**
142          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#enclosingType()
143          */
144         public ReferenceBinding enclosingType() {
145                 if (this.isMemberType() && this.enclosingType == null) {
146                         ReferenceBinding originalEnclosing = this.type.enclosingType();
147                         this.enclosingType = originalEnclosing.isGenericType()
148                                                                                                         ? this.environment.createRawType(originalEnclosing, originalEnclosing.enclosingType()) // TODO (need to propagate in depth on enclosing type)
149                                                                                                         : originalEnclosing;
150                 }
151             return this.enclosingType;
152         }
153
154         /**
155      * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#erasure()
156      */
157     public TypeBinding erasure() {
158         return this.type.erasure(); // erasure
159     }
160         /**
161          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#fieldCount()
162          */
163         public int fieldCount() {
164                 return this.type.fieldCount(); // same as erasure (lazy)
165         }
166
167         /**
168          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#fields()
169          */
170         public FieldBinding[] fields() {
171                 if (this.fields == null) {
172                         try {
173                                 FieldBinding[] originalFields = this.type.fields();
174                                 int length = originalFields.length;
175                                 FieldBinding[] parameterizedFields = new FieldBinding[length];
176                                 for (int i = 0; i < length; i++)
177                                         // substitute all fields, so as to get updated declaring class at least
178                                         parameterizedFields[i] = new ParameterizedFieldBinding(this, originalFields[i]);
179                                 this.fields = parameterizedFields;          
180                         } finally {
181                                 // if the original fields cannot be retrieved (ex. AbortCompilation), then assume we do not have any fields
182                                 if (this.fields == null) 
183                                         this.fields = NoFields;
184                         }
185                 }
186                 return this.fields;
187         }
188
189         /**
190          * Ltype<param1 ... paramN>;
191          * LY<TT;>;
192          */
193         public char[] genericTypeSignature() {
194             if (this.genericTypeSignature == null) {
195                     StringBuffer sig = new StringBuffer(10);
196                         if (this.isMemberType() && this.enclosingType().isParameterizedType()) {
197                             char[] typeSig = this.enclosingType().genericTypeSignature();
198                             for (int i = 0; i < typeSig.length-1; i++) sig.append(typeSig[i]); // copy all but trailing semicolon
199                             sig.append('.').append(this.sourceName());
200                         } else {
201                             char[] typeSig = this.type.signature();
202                             for (int i = 0; i < typeSig.length-1; i++) sig.append(typeSig[i]); // copy all but trailing semicolon
203                         }                   
204                         if (this.arguments != null) {
205                             sig.append('<');
206                             for (int i = 0, length = this.arguments.length; i < length; i++) {
207                                 sig.append(this.arguments[i].genericTypeSignature());
208                             }
209                             sig.append('>'); //$NON-NLS-1$
210                         }
211                         sig.append(';');
212                         int sigLength = sig.length();
213                         this.genericTypeSignature = new char[sigLength];
214                         sig.getChars(0, sigLength, this.genericTypeSignature, 0);                       
215             }
216                 return this.genericTypeSignature;           
217         }       
218
219         /**
220          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#getExactConstructor(TypeBinding[])
221          */
222         public MethodBinding getExactConstructor(TypeBinding[] argumentTypes) {
223                 int argCount = argumentTypes.length;
224
225                 if ((modifiers & AccUnresolved) == 0) { // have resolved all arg types & return type of the methods
226                         nextMethod : for (int m = methods.length; --m >= 0;) {
227                                 MethodBinding method = methods[m];
228                                 if (method.selector == TypeConstants.INIT && method.parameters.length == argCount) {
229                                         TypeBinding[] toMatch = method.parameters;
230                                         for (int p = 0; p < argCount; p++)
231                                                 if (toMatch[p] != argumentTypes[p])
232                                                         continue nextMethod;
233                                         return method;
234                                 }
235                         }
236                 } else {
237                         MethodBinding[] constructors = getMethods(TypeConstants.INIT); // takes care of duplicates & default abstract methods
238                         nextConstructor : for (int c = constructors.length; --c >= 0;) {
239                                 MethodBinding constructor = constructors[c];
240                                 TypeBinding[] toMatch = constructor.parameters;
241                                 if (toMatch.length == argCount) {
242                                         for (int p = 0; p < argCount; p++)
243                                                 if (toMatch[p] != argumentTypes[p])
244                                                         continue nextConstructor;
245                                         return constructor;
246                                 }
247                         }
248                 }
249                 return null;
250         }
251
252         /**
253          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#getExactMethod(char[], TypeBinding[])
254          */
255         public MethodBinding getExactMethod(char[] selector, TypeBinding[] argumentTypes, CompilationUnitScope refScope) {
256                 // sender from refScope calls recordTypeReference(this)
257                 int argCount = argumentTypes.length;
258                 int selectorLength = selector.length;
259                 boolean foundNothing = true;
260                 MethodBinding match = null;
261
262                 if ((modifiers & AccUnresolved) == 0) { // have resolved all arg types & return type of the methods
263                         nextMethod : for (int m = methods.length; --m >= 0;) {
264                                 MethodBinding method = methods[m];
265                                 if (method.selector.length == selectorLength && CharOperation.equals(method.selector, selector)) {
266                                         foundNothing = false; // inner type lookups must know that a method with this name exists
267                                         if (method.parameters.length == argCount) {
268                                                 TypeBinding[] toMatch = method.parameters;
269                                                 for (int p = 0; p < argCount; p++)
270                                                         if (toMatch[p] != argumentTypes[p])
271                                                                 continue nextMethod;
272                                                 if (match != null) return null; // collision case
273                                                 match = method;
274                                         }
275                                 }
276                         }
277                 } else {
278                         MethodBinding[] matchingMethods = getMethods(selector); // takes care of duplicates & default abstract methods
279                         foundNothing = matchingMethods == NoMethods;
280                         nextMethod : for (int m = matchingMethods.length; --m >= 0;) {
281                                 MethodBinding method = matchingMethods[m];
282                                 TypeBinding[] toMatch = method.parameters;
283                                 if (toMatch.length == argCount) {
284                                         for (int p = 0; p < argCount; p++)
285                                                 if (toMatch[p] != argumentTypes[p])
286                                                         continue nextMethod;
287                                                 if (match != null) return null; // collision case
288                                                 match = method;
289                                 }
290                         }
291                 }
292                 if (match != null) return match;
293
294                 if (foundNothing) {
295                         if (isInterface()) {
296                                  if (superInterfaces().length == 1) {
297                                         if (refScope != null)
298                                                 refScope.recordTypeReference(superInterfaces[0]);
299                                         return superInterfaces[0].getExactMethod(selector, argumentTypes, refScope);
300                                  }
301                         } else if (superclass() != null) {
302                                 if (refScope != null)
303                                         refScope.recordTypeReference(superclass);
304                                 return superclass.getExactMethod(selector, argumentTypes, refScope);
305                         }
306                 }
307                 return null;
308         }
309
310         /**
311          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#getField(char[], boolean)
312          */
313         public FieldBinding getField(char[] fieldName, boolean needResolve) {
314                 fields(); // ensure fields have been initialized... must create all at once unlike methods
315                 int fieldLength = fieldName.length;
316                 for (int i = fields.length; --i >= 0;) {
317                         FieldBinding field = fields[i];
318                         if (field.name.length == fieldLength && CharOperation.equals(field.name, fieldName))
319                                 return field;
320                 }
321                 return null;
322         }
323
324         /**
325          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#getMemberType(char[])
326          */
327         public ReferenceBinding getMemberType(char[] typeName) {
328                 memberTypes(); // ensure memberTypes have been initialized... must create all at once unlike methods
329                 int typeLength = typeName.length;
330                 for (int i = this.memberTypes.length; --i >= 0;) {
331                         ReferenceBinding memberType = this.memberTypes[i];
332                         if (memberType.sourceName.length == typeLength && CharOperation.equals(memberType.sourceName, typeName))
333                                 return memberType;
334                 }
335                 return null;
336         }
337
338         /**
339          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#getMethods(char[])
340          */
341         public MethodBinding[] getMethods(char[] selector) {
342                 java.util.ArrayList matchingMethods = null;
343                 if (this.methods != null) {
344                         int selectorLength = selector.length;
345                         for (int i = 0, length = this.methods.length; i < length; i++) {
346                                 MethodBinding method = this.methods[i];
347                                 if (method.selector.length == selectorLength && CharOperation.equals(method.selector, selector)) {
348                                         if (matchingMethods == null)
349                                                 matchingMethods = new java.util.ArrayList(2);
350                                         matchingMethods.add(method);
351                                 }
352                         }
353                         if (matchingMethods != null) {
354                                 MethodBinding[] result = new MethodBinding[matchingMethods.size()];
355                                 matchingMethods.toArray(result);
356                                 return result;
357                         }
358                 }
359                 if ((modifiers & AccUnresolved) == 0) return NoMethods; // have created all the methods and there are no matches
360
361                 MethodBinding[] parameterizedMethods = null;
362                 try {
363                     MethodBinding[] originalMethods = this.type.getMethods(selector);
364                     int length = originalMethods.length;
365                     if (length == 0) return NoMethods; 
366
367                     parameterizedMethods = new MethodBinding[length];
368                     for (int i = 0; i < length; i++)
369                         // substitute methods, so as to get updated declaring class at least
370                     parameterizedMethods[i] = createParameterizedMethod(originalMethods[i]);
371                     if (this.methods == null) {
372                         MethodBinding[] temp = new MethodBinding[length];
373                         System.arraycopy(parameterizedMethods, 0, temp, 0, length);
374                         this.methods = temp; // must be a copy of parameterizedMethods since it will be returned below
375                     } else {
376                         MethodBinding[] temp = new MethodBinding[length + this.methods.length];
377                         System.arraycopy(parameterizedMethods, 0, temp, 0, length);
378                         System.arraycopy(this.methods, 0, temp, length, this.methods.length);
379                         this.methods = temp;
380                         }
381                     return parameterizedMethods;
382                 } finally {
383                         // if the original methods cannot be retrieved (ex. AbortCompilation), then assume we do not have any methods
384                     if (parameterizedMethods == null) 
385                         this.methods = parameterizedMethods = NoMethods;
386                 }
387         }
388         public boolean hasMemberTypes() {
389             return this.type.hasMemberTypes();
390         }
391
392         /**
393          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#implementsMethod(MethodBinding)
394          */
395         public boolean implementsMethod(MethodBinding method) {
396                 return this.type.implementsMethod(method); // erasure
397         }
398
399         void initialize(ReferenceBinding someType, TypeBinding[] someArguments) {
400                 this.type = someType;
401                 this.sourceName = someType.sourceName;
402                 this.compoundName = someType.compoundName;
403                 this.fPackage = someType.fPackage;
404                 this.fileName = someType.fileName;
405                 // should not be set yet
406                 // this.superclass = null;
407                 // this.superInterfaces = null;
408                 // this.fields = null;
409                 // this.methods = null;         
410                 this.modifiers = someType.modifiers | AccGenericSignature | AccUnresolved; // until methods() is sent
411                 if (someArguments != null) {
412                         this.arguments = someArguments;
413                         for (int i = 0, length = someArguments.length; i < length; i++) {
414                                 TypeBinding someArgument = someArguments[i];
415                                 boolean isWildcardArgument = someArgument.isWildcard();
416                                 if (isWildcardArgument) {
417                                         this.tagBits |= HasDirectWildcard;
418                                 }
419                                 if (!isWildcardArgument || ((WildcardBinding) someArgument).kind != Wildcard.UNBOUND) {
420                                         this.tagBits |= IsBoundParameterizedType;
421                                 }
422                             this.tagBits |= someArgument.tagBits & (HasTypeVariable);
423                         }
424                 }           
425                 this.tagBits |= someType.tagBits & (IsLocalType| IsMemberType | IsNestedType);
426         }
427
428         protected void initializeArguments() {
429             // do nothing for true parameterized types (only for raw types)
430         }
431         
432         public boolean isEquivalentTo(TypeBinding otherType) {
433                 if (this == otherType) 
434                     return true;
435             if (otherType == null) 
436                 return false;
437             switch(otherType.kind()) {
438         
439                 case Binding.WILDCARD_TYPE :
440                         return ((WildcardBinding) otherType).boundCheck(this);
441                         
442                 case Binding.PARAMETERIZED_TYPE :
443                     if ((otherType.tagBits & HasDirectWildcard) == 0 && (!this.isMemberType() || !otherType.isMemberType())) 
444                         return false; // should have been identical
445                     ParameterizedTypeBinding otherParamType = (ParameterizedTypeBinding) otherType;
446                     if (this.type != otherParamType.type) 
447                         return false;
448                     if (!isStatic()) { // static member types do not compare their enclosing
449                             ReferenceBinding enclosing = enclosingType();
450                             if (enclosing != null && !enclosing.isEquivalentTo(otherParamType.enclosingType()))
451                                 return false;
452                     }
453                     int length = this.arguments == null ? 0 : this.arguments.length;
454                     TypeBinding[] otherArguments = otherParamType.arguments;
455                     int otherLength = otherArguments == null ? 0 : otherArguments.length;
456                     if (otherLength != length) 
457                         return false;
458                     for (int i = 0; i < length; i++) {
459                         if (!this.arguments[i].isTypeArgumentContainedBy(otherArguments[i]))
460                                 return false;
461                     }
462                     return true;
463                 
464                 case Binding.RAW_TYPE :
465                     return erasure() == otherType.erasure();
466             }
467         return false;
468         }
469
470         /**
471          * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#isParameterizedType()
472          */
473         public boolean isParameterizedType() {
474             return true;
475         }
476         
477         /**
478          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#memberTypes()
479          */
480         public ReferenceBinding[] memberTypes() {
481                 if (this.memberTypes == null) {
482                         try {
483                                 ReferenceBinding[] originalMemberTypes = this.type.memberTypes();
484                                 int length = originalMemberTypes.length;
485                                 ReferenceBinding[] parameterizedMemberTypes = new ReferenceBinding[length];
486                                 for (int i = 0; i < length; i++)
487                                         // substitute all member types, so as to get updated enclosing types
488                                         parameterizedMemberTypes[i] = this.environment.createParameterizedType(originalMemberTypes[i], null, this);
489                                 this.memberTypes = parameterizedMemberTypes;        
490                         } finally {
491                                 // if the original fields cannot be retrieved (ex. AbortCompilation), then assume we do not have any fields
492                                 if (this.memberTypes == null) 
493                                         this.memberTypes = NoMemberTypes;
494                         }
495                 }
496                 return this.memberTypes;
497         }
498
499         /**
500          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#methods()
501          */
502         public MethodBinding[] methods() {
503                 if ((modifiers & AccUnresolved) == 0)
504                         return this.methods;
505
506                 try {
507                     MethodBinding[] originalMethods = this.type.methods();
508                     int length = originalMethods.length;
509                     MethodBinding[] parameterizedMethods = new MethodBinding[length];
510                     for (int i = 0; i < length; i++)
511                         // substitute all methods, so as to get updated declaring class at least
512                     parameterizedMethods[i] = createParameterizedMethod(originalMethods[i]);
513                     this.methods = parameterizedMethods;
514                 } finally {
515                         // if the original methods cannot be retrieved (ex. AbortCompilation), then assume we do not have any methods
516                     if (this.methods == null) 
517                         this.methods = NoMethods;
518
519                         modifiers &= ~AccUnresolved;
520                 }               
521                 return this.methods;
522         }
523         
524         /**
525          * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#qualifiedSourceName()
526          */
527         public char[] qualifiedSourceName() {
528                 return this.type.qualifiedSourceName();
529         }
530
531         /**
532          * @see org.eclipse.jdt.internal.compiler.lookup.Binding#readableName()
533          */
534         public char[] readableName() {
535             StringBuffer nameBuffer = new StringBuffer(10);
536                 if (this.isMemberType()) {
537                         nameBuffer.append(CharOperation.concat(this.enclosingType().readableName(), sourceName, '.'));
538                 } else {
539                         nameBuffer.append(CharOperation.concatWith(this.type.compoundName, '.'));
540                 }           
541                 if (this.arguments != null) {
542                         nameBuffer.append('<');
543                     for (int i = 0, length = this.arguments.length; i < length; i++) {
544                         if (i > 0) nameBuffer.append(',');
545                         nameBuffer.append(this.arguments[i].readableName());
546                     }
547                     nameBuffer.append('>');
548                 }
549                 int nameLength = nameBuffer.length();
550                 char[] readableName = new char[nameLength];
551                 nameBuffer.getChars(0, nameLength, readableName, 0);            
552             return readableName;
553         }
554
555         ReferenceBinding resolve() {
556                 // TODO need flag to know that this has already been done... should it be on ReferenceBinding?
557                 ReferenceBinding resolvedType = BinaryTypeBinding.resolveType(this.type, this.environment, false); // still part of parameterized type ref
558                 if (this.arguments != null) {
559                         int argLength = this.arguments.length;
560                         for (int i = 0; i < argLength; i++)
561                                 BinaryTypeBinding.resolveType(this.arguments[i], this.environment, this, i);
562                         // arity check
563                         TypeVariableBinding[] refTypeVariables = resolvedType.typeVariables();
564                         if (refTypeVariables == NoTypeVariables) { // check generic
565                                 this.environment.problemReporter.nonGenericTypeCannotBeParameterized(null, resolvedType, this.arguments);
566                                 return this; // cannot reach here as AbortCompilation is thrown
567                         } else if (argLength != refTypeVariables.length) { // check arity
568                                 this.environment.problemReporter.incorrectArityForParameterizedType(null, resolvedType, this.arguments);
569                                 return this; // cannot reach here as AbortCompilation is thrown
570                         }                       
571                         // check argument type compatibility
572                         for (int i = 0; i < argLength; i++) {
573                             TypeBinding resolvedArgument = this.arguments[i];
574                                 if (!refTypeVariables[i].boundCheck(this, resolvedArgument)) {
575                                         this.environment.problemReporter.typeMismatchError(resolvedArgument, refTypeVariables[i], resolvedType, null);
576                             }
577                         }
578                 }
579                 return this;
580         }
581
582         /**
583          * @see org.eclipse.jdt.internal.compiler.lookup.Binding#shortReadableName()
584          */
585         public char[] shortReadableName() {
586             StringBuffer nameBuffer = new StringBuffer(10);
587                 if (this.isMemberType()) {
588                         nameBuffer.append(CharOperation.concat(this.enclosingType().shortReadableName(), sourceName, '.'));
589                 } else {
590                         nameBuffer.append(this.type.sourceName);
591                 }           
592                 if (this.arguments != null) {
593                         nameBuffer.append('<');
594                     for (int i = 0, length = this.arguments.length; i < length; i++) {
595                         if (i > 0) nameBuffer.append(',');
596                         nameBuffer.append(this.arguments[i].shortReadableName());
597                     }
598                     nameBuffer.append('>');
599                 }
600                 int nameLength = nameBuffer.length();
601                 char[] shortReadableName = new char[nameLength];
602                 nameBuffer.getChars(0, nameLength, shortReadableName, 0);           
603             return shortReadableName;
604         }
605         /**
606          * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#signature()
607          */
608         public char[] signature() {
609             if (this.signature == null) {
610                 this.signature = this.type.signature();  // erasure
611             }
612                 return this.signature; 
613         }
614
615         /**
616          * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#sourceName()
617          */
618         public char[] sourceName() {
619                 return this.type.sourceName();
620         }
621
622         /**
623          * Returns a type, where original type was substituted using the receiver
624          * parameterized type.
625          */
626         public TypeBinding substitute(TypeBinding originalType) {
627                 
628                 switch (originalType.kind()) {
629                         
630                         case Binding.TYPE_PARAMETER:
631                                 TypeVariableBinding originalVariable = (TypeVariableBinding) originalType;
632                                 ParameterizedTypeBinding currentType = this;
633                                 while (true) {
634                                         if (currentType.arguments != null) {
635                                                 TypeVariableBinding[] typeVariables = currentType.type.typeVariables();
636                                                 int length = typeVariables.length;
637                                                 // check this variable can be substituted given parameterized type
638                                                 if (originalVariable.rank < length && typeVariables[originalVariable.rank] == originalVariable) {
639                                                         return currentType.arguments[originalVariable.rank];
640                                                 }
641                                         }
642                                         // recurse on enclosing type, as it may hold more substitutions to perform
643                                         ReferenceBinding enclosing = currentType.enclosingType();
644                                         if (!(enclosing instanceof ParameterizedTypeBinding))
645                                                 break;
646                                         currentType = (ParameterizedTypeBinding) enclosing;
647                                 }
648                                 break;
649                                 
650                         case Binding.PARAMETERIZED_TYPE:
651                                 ParameterizedTypeBinding originalParameterizedType = (ParameterizedTypeBinding) originalType;
652                                 ReferenceBinding originalEnclosing = originalType.enclosingType();
653                                 ReferenceBinding substitutedEnclosing = originalEnclosing;
654                                 if (originalEnclosing != null) {
655                                         substitutedEnclosing = (ReferenceBinding) this.substitute(originalEnclosing);
656                                 }
657                                 TypeBinding[] originalArguments = originalParameterizedType.arguments;
658                                 TypeBinding[] substitutedArguments = originalArguments;
659                                 if (originalArguments != null) {
660                                         substitutedArguments = Scope.substitute(this, originalArguments);
661                                 }
662                                 if (substitutedArguments != originalArguments || substitutedEnclosing != originalEnclosing) {
663                                         identicalVariables: { // if substituted with original variables, then answer the generic type itself
664                                                 if (substitutedEnclosing != originalEnclosing) break identicalVariables;
665                                                 TypeVariableBinding[] originalVariables = originalParameterizedType.type.typeVariables();
666                                                 for (int i = 0, length = originalVariables.length; i < length; i++) {
667                                                         if (substitutedArguments[i] != originalVariables[i]) break identicalVariables;
668                                                 }
669                                                 return originalParameterizedType.type;
670                                         }
671                                         return this.environment.createParameterizedType(
672                                                         originalParameterizedType.type, substitutedArguments, substitutedEnclosing);
673                                 }
674                                 break;
675                                 
676                         case Binding.ARRAY_TYPE:
677                                 TypeBinding originalLeafComponentType = originalType.leafComponentType();
678                                 TypeBinding substitute = substitute(originalLeafComponentType); // substitute could itself be array type
679                                 if (substitute != originalLeafComponentType) {
680                                         return this.environment.createArrayType(substitute.leafComponentType(), substitute.dimensions() + originalType.dimensions());
681                                 }
682                                 break;
683
684                         case Binding.WILDCARD_TYPE:
685                         WildcardBinding wildcard = (WildcardBinding) originalType;
686                         if (wildcard.kind != Wildcard.UNBOUND) {
687                                 TypeBinding originalBound = wildcard.bound;
688                                 TypeBinding substitutedBound = substitute(originalBound);
689                                 if (substitutedBound != originalBound) {
690                                         return this.environment.createWildcard(wildcard.genericType, wildcard.rank, substitutedBound, wildcard.kind);
691                                 }
692                         }
693                                 break;
694
695                         case Binding.GENERIC_TYPE:
696                             // treat as if parameterized with its type variables
697                                 ReferenceBinding originalGenericType = (ReferenceBinding) originalType;
698                                 originalEnclosing = originalType.enclosingType();
699                                 substitutedEnclosing = originalEnclosing;
700                                 if (originalEnclosing != null) {
701                                         substitutedEnclosing = (ReferenceBinding) this.substitute(originalEnclosing);
702                                 }
703                                 TypeVariableBinding[] originalVariables = originalGenericType.typeVariables();
704                                 int length = originalVariables.length;
705                                 System.arraycopy(originalVariables, 0, originalArguments = new TypeBinding[length], 0, length);
706                                 substitutedArguments = Scope.substitute(this, originalArguments);
707                                 if (substitutedArguments != originalArguments || substitutedEnclosing != originalEnclosing) {
708                                         return this.environment.createParameterizedType(
709                                                         originalGenericType, substitutedArguments, substitutedEnclosing);
710                                 }
711                                 break;
712                 }
713                 return originalType;
714         }       
715
716         /**
717          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#superclass()
718          */
719         public ReferenceBinding superclass() {
720             if (this.superclass == null) {
721                 // note: Object cannot be generic
722                 ReferenceBinding genericSuperclass = this.type.superclass();
723                 if (genericSuperclass == null) return null; // e.g. interfaces
724                     this.superclass = (ReferenceBinding) substitute(genericSuperclass);
725             }
726                 return this.superclass;
727         }
728
729         /**
730          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#superInterfaces()
731          */
732         public ReferenceBinding[] superInterfaces() {
733             if (this.superInterfaces == null) {
734                 this.superInterfaces = Scope.substitute(this, this.type.superInterfaces());
735             }
736                 return this.superInterfaces;
737         }
738
739         public void swapUnresolved(UnresolvedReferenceBinding unresolvedType, ReferenceBinding resolvedType, LookupEnvironment env) {
740                 boolean update = false;
741                 if (this.type == unresolvedType) {
742                         this.type = resolvedType; // cannot be raw since being parameterized below
743                         update = true;
744                 }
745                 if (this.arguments != null) {
746                         for (int i = 0, l = this.arguments.length; i < l; i++) {
747                                 if (this.arguments[i] == unresolvedType) {
748                                         this.arguments[i] = resolvedType.isGenericType() ? env.createRawType(resolvedType, resolvedType.enclosingType()) : resolvedType;
749                                         update = true;
750                                 }
751                         }
752                 }
753                 if (update)
754                         initialize(this.type, this.arguments);
755         }
756
757         /**
758          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#syntheticEnclosingInstanceTypes()
759          */
760         public ReferenceBinding[] syntheticEnclosingInstanceTypes() {
761                 return this.type.syntheticEnclosingInstanceTypes();
762         }
763
764         /**
765          * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#syntheticOuterLocalVariables()
766          */
767         public SyntheticArgumentBinding[] syntheticOuterLocalVariables() {
768                 return this.type.syntheticOuterLocalVariables();
769         }
770
771         /**
772          * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#qualifiedPackageName()
773          */
774         public char[] qualifiedPackageName() {
775                 return this.type.qualifiedPackageName();
776         }
777
778         /**
779          * @see java.lang.Object#toString()
780          */
781         public String toString() {
782             StringBuffer buffer = new StringBuffer(30);
783                 if (isDeprecated()) buffer.append("deprecated "); //$NON-NLS-1$
784                 if (isPublic()) buffer.append("public "); //$NON-NLS-1$
785                 if (isProtected()) buffer.append("protected "); //$NON-NLS-1$
786                 if (isPrivate()) buffer.append("private "); //$NON-NLS-1$
787                 if (isAbstract() && isClass()) buffer.append("abstract "); //$NON-NLS-1$
788                 if (isStatic() && isNestedType()) buffer.append("static "); //$NON-NLS-1$
789                 if (isFinal()) buffer.append("final "); //$NON-NLS-1$
790         
791                 buffer.append(isInterface() ? "interface " : "class "); //$NON-NLS-1$ //$NON-NLS-2$
792                 buffer.append(this.debugName());
793         
794                 buffer.append("\n\textends "); //$NON-NLS-1$
795                 buffer.append((superclass != null) ? superclass.debugName() : "NULL TYPE"); //$NON-NLS-1$
796         
797                 if (superInterfaces != null) {
798                         if (superInterfaces != NoSuperInterfaces) {
799                                 buffer.append("\n\timplements : "); //$NON-NLS-1$
800                                 for (int i = 0, length = superInterfaces.length; i < length; i++) {
801                                         if (i  > 0)
802                                                 buffer.append(", "); //$NON-NLS-1$
803                                         buffer.append((superInterfaces[i] != null) ? superInterfaces[i].debugName() : "NULL TYPE"); //$NON-NLS-1$
804                                 }
805                         }
806                 } else {
807                         buffer.append("NULL SUPERINTERFACES"); //$NON-NLS-1$
808                 }
809         
810                 if (enclosingType() != null) {
811                         buffer.append("\n\tenclosing type : "); //$NON-NLS-1$
812                         buffer.append(enclosingType().debugName());
813                 }
814         
815                 if (fields != null) {
816                         if (fields != NoFields) {
817                                 buffer.append("\n/*   fields   */"); //$NON-NLS-1$
818                                 for (int i = 0, length = fields.length; i < length; i++)
819                                     buffer.append('\n').append((fields[i] != null) ? fields[i].toString() : "NULL FIELD"); //$NON-NLS-1$ 
820                         }
821                 } else {
822                         buffer.append("NULL FIELDS"); //$NON-NLS-1$
823                 }
824         
825                 if (methods != null) {
826                         if (methods != NoMethods) {
827                                 buffer.append("\n/*   methods   */"); //$NON-NLS-1$
828                                 for (int i = 0, length = methods.length; i < length; i++)
829                                         buffer.append('\n').append((methods[i] != null) ? methods[i].toString() : "NULL METHOD"); //$NON-NLS-1$ //$NON-NLS-2$
830                         }
831                 } else {
832                         buffer.append("NULL METHODS"); //$NON-NLS-1$
833                 }
834         
835 //              if (memberTypes != null) {
836 //                      if (memberTypes != NoMemberTypes) {
837 //                              buffer.append("\n/*   members   */"); //$NON-NLS-1$
838 //                              for (int i = 0, length = memberTypes.length; i < length; i++)
839 //                                      buffer.append('\n').append((memberTypes[i] != null) ? memberTypes[i].toString() : "NULL TYPE"); //$NON-NLS-1$ //$NON-NLS-2$
840 //                      }
841 //              } else {
842 //                      buffer.append("NULL MEMBER TYPES"); //$NON-NLS-1$
843 //              }
844         
845                 buffer.append("\n\n"); //$NON-NLS-1$
846                 return buffer.toString();
847                 
848         }
849         public TypeVariableBinding[] typeVariables() {
850                 if (this.arguments == null) {
851                         // retain original type variables if not substituted (member type of parameterized type)
852                         return this.type.typeVariables();
853                 } 
854                 return NoTypeVariables;
855         }       
856 }