Basic Reafctoring functionality adapted from Leif Frenzels sources in eclipse-magazin...
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / lookup / SyntheticAccessMethodBinding.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 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 net.sourceforge.phpdt.internal.compiler.lookup;
12
13 import net.sourceforge.phpdt.core.compiler.CharOperation;
14 import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
15 import net.sourceforge.phpdt.internal.compiler.ast.FieldDeclaration;
16
17 public class SyntheticAccessMethodBinding extends MethodBinding {
18
19         public FieldBinding targetReadField;            // read access to a field
20         public FieldBinding targetWriteField;           // write access to a field
21         public MethodBinding targetMethod;      // method or constructor
22         
23         public int accessType;
24
25         public final static int FieldReadAccess = 1;            // field read
26         public final static int FieldWriteAccess = 2;           // field write
27         public final static int MethodAccess = 3;               // normal method 
28         public final static int ConstructorAccess = 4;  // constructor
29         public final static int SuperMethodAccess = 5; // super method
30
31         final static char[] AccessMethodPrefix = { 'a', 'c', 'c', 'e', 's', 's', '$' };
32
33         public int sourceStart = 0; // start position of the matching declaration
34         public int index; // used for sorting access methods in the class file
35         
36         public SyntheticAccessMethodBinding(FieldBinding targetField, boolean isReadAccess, ReferenceBinding declaringClass) {
37
38                 this.modifiers = AccDefault | AccStatic;// | AccSynthetic;
39                 SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
40                 SyntheticAccessMethodBinding[] knownAccessMethods = declaringSourceType.syntheticAccessMethods();
41                 int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
42                 this.index = methodId;
43                 this.selector = CharOperation.concat(AccessMethodPrefix, String.valueOf(methodId).toCharArray());
44                 if (isReadAccess) {
45                         this.returnType = targetField.type;
46                         if (targetField.isStatic()) {
47                                 this.parameters = NoParameters;
48                         } else {
49                                 this.parameters = new TypeBinding[1];
50                                 this.parameters[0] = declaringSourceType;
51                         }
52                         this.targetReadField = targetField;
53                         this.accessType = FieldReadAccess;
54                 } else {
55                         this.returnType = VoidBinding;
56                         if (targetField.isStatic()) {
57                                 this.parameters = new TypeBinding[1];
58                                 this.parameters[0] = targetField.type;
59                         } else {
60                                 this.parameters = new TypeBinding[2];
61                                 this.parameters[0] = declaringSourceType;
62                                 this.parameters[1] = targetField.type;
63                         }
64                         this.targetWriteField = targetField;
65                         this.accessType = FieldWriteAccess;
66                 }
67                 this.thrownExceptions = NoExceptions;
68                 this.declaringClass = declaringSourceType;
69         
70                 // check for method collision
71                 boolean needRename;
72                 do {
73                         check : {
74                                 needRename = false;
75                                 // check for collision with known methods
76                                 MethodBinding[] methods = declaringSourceType.methods;
77                                 for (int i = 0, length = methods.length; i < length; i++) {
78                                         if (this.selector == methods[i].selector && this.areParametersEqual(methods[i])) {
79                                                 needRename = true;
80                                                 break check;
81                                         }
82                                 }
83                                 // check for collision with synthetic accessors
84                                 if (knownAccessMethods != null) {
85                                         for (int i = 0, length = knownAccessMethods.length; i < length; i++) {
86                                                 if (knownAccessMethods[i] == null) continue;
87                                                 if (this.selector == knownAccessMethods[i].selector && this.areParametersEqual(methods[i])) {
88                                                         needRename = true;
89                                                         break check;
90                                                 }
91                                         }
92                                 }
93                         }
94                         if (needRename) { // retry with a selector postfixed by a growing methodId
95                                 this.selector(CharOperation.concat(AccessMethodPrefix, String.valueOf(++methodId).toCharArray()));
96                         }
97                 } while (needRename);
98         
99                 // retrieve sourceStart position for the target field for line number attributes
100                 FieldDeclaration[] fieldDecls = declaringSourceType.scope.referenceContext.fields;
101                 if (fieldDecls != null) {
102                         for (int i = 0, max = fieldDecls.length; i < max; i++) {
103                                 if (fieldDecls[i].binding == targetField) {
104                                         this.sourceStart = fieldDecls[i].sourceStart;
105                                         return;
106                                 }
107                         }
108                 }
109         
110         /* did not find the target field declaration - it is a synthetic one
111                 public class A {
112                         public class B {
113                                 public class C {
114                                         void foo() {
115                                                 System.out.println("A.this = " + A.this);
116                                         }
117                                 }
118                         }
119                         public static void main(String args[]) {
120                                 new A().new B().new C().foo();
121                         }
122                 }       
123         */
124                 // We now at this point - per construction - it is for sure an enclosing instance, we are going to
125                 // show the target field type declaration location.
126                 this.sourceStart = declaringSourceType.scope.referenceContext.sourceStart; // use the target declaring class name position instead
127         }
128
129         public SyntheticAccessMethodBinding(MethodBinding targetMethod, boolean isSuperAccess, ReferenceBinding receiverType) {
130         
131                 if (targetMethod.isConstructor()) {
132                         this.initializeConstructorAccessor(targetMethod);
133                 } else {
134                         this.initializeMethodAccessor(targetMethod, isSuperAccess, receiverType);
135                 }
136         }
137
138         /**
139          * An constructor accessor is a constructor with an extra argument (declaringClass), in case of
140          * collision with an existing constructor, then add again an extra argument (declaringClass again).
141          */
142          public void initializeConstructorAccessor(MethodBinding targetConstructor) {
143         
144                 this.targetMethod = targetConstructor;
145                 this.modifiers = AccDefault;// | AccSynthetic;
146                 SourceTypeBinding sourceType = (SourceTypeBinding) targetConstructor.declaringClass; 
147                 SyntheticAccessMethodBinding[] knownAccessMethods = 
148                         sourceType.syntheticAccessMethods(); 
149                 this.index = knownAccessMethods == null ? 0 : knownAccessMethods.length;
150         
151                 this.selector = targetConstructor.selector;
152                 this.returnType = targetConstructor.returnType;
153                 this.accessType = ConstructorAccess;
154                 this.parameters = new TypeBinding[targetConstructor.parameters.length + 1];
155                 System.arraycopy(
156                         targetConstructor.parameters, 
157                         0, 
158                         this.parameters, 
159                         0, 
160                         targetConstructor.parameters.length); 
161                 parameters[targetConstructor.parameters.length] = 
162                         targetConstructor.declaringClass; 
163                 this.thrownExceptions = targetConstructor.thrownExceptions;
164                 this.declaringClass = sourceType;
165         
166                 // check for method collision
167                 boolean needRename;
168                 do {
169                         check : {
170                                 needRename = false;
171                                 // check for collision with known methods
172                                 MethodBinding[] methods = sourceType.methods;
173                                 for (int i = 0, length = methods.length; i < length; i++) {
174                                         if (this.selector == methods[i].selector
175                                                 && this.areParametersEqual(methods[i])) {
176                                                 needRename = true;
177                                                 break check;
178                                         }
179                                 }
180                                 // check for collision with synthetic accessors
181                                 if (knownAccessMethods != null) {
182                                         for (int i = 0, length = knownAccessMethods.length; i < length; i++) {
183                                                 if (knownAccessMethods[i] == null)
184                                                         continue;
185                                                 if (this.selector == knownAccessMethods[i].selector
186                                                         && this.areParametersEqual(knownAccessMethods[i])) {
187                                                         needRename = true;
188                                                         break check;
189                                                 }
190                                         }
191                                 }
192                         }
193                         if (needRename) { // retry with a new extra argument
194                                 int length = this.parameters.length;
195                                 System.arraycopy(
196                                         this.parameters, 
197                                         0, 
198                                         this.parameters = new TypeBinding[length + 1], 
199                                         0, 
200                                         length); 
201                                 this.parameters[length] = this.declaringClass;
202                         }
203                 } while (needRename);
204         
205                 // retrieve sourceStart position for the target method for line number attributes
206                 AbstractMethodDeclaration[] methodDecls = 
207                         sourceType.scope.referenceContext.methods; 
208                 if (methodDecls != null) {
209                         for (int i = 0, length = methodDecls.length; i < length; i++) {
210                                 if (methodDecls[i].binding == targetConstructor) {
211                                         this.sourceStart = methodDecls[i].sourceStart;
212                                         return;
213                                 }
214                         }
215                 }
216         }
217
218         /**
219          * An method accessor is a method with an access$N selector, where N is incremented in case of collisions.
220          */
221         public void initializeMethodAccessor(MethodBinding targetMethod, boolean isSuperAccess, ReferenceBinding declaringClass) {
222                 
223                 this.targetMethod = targetMethod;
224                 this.modifiers = AccDefault | AccStatic;// | AccSynthetic;
225                 SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
226                 SyntheticAccessMethodBinding[] knownAccessMethods = declaringSourceType.syntheticAccessMethods();
227                 int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
228                 this.index = methodId;
229         
230                 this.selector = CharOperation.concat(AccessMethodPrefix, String.valueOf(methodId).toCharArray());
231                 this.returnType = targetMethod.returnType;
232                 this.accessType = isSuperAccess ? SuperMethodAccess : MethodAccess;
233                 
234                 if (targetMethod.isStatic()) {
235                         this.parameters = targetMethod.parameters;
236                 } else {
237                         this.parameters = new TypeBinding[targetMethod.parameters.length + 1];
238                         this.parameters[0] = declaringSourceType;
239                         System.arraycopy(targetMethod.parameters, 0, this.parameters, 1, targetMethod.parameters.length);
240                 }
241                 this.thrownExceptions = targetMethod.thrownExceptions;
242                 this.declaringClass = declaringSourceType;
243         
244                 // check for method collision
245                 boolean needRename;
246                 do {
247                         check : {
248                                 needRename = false;
249                                 // check for collision with known methods
250                                 MethodBinding[] methods = declaringSourceType.methods;
251                                 for (int i = 0, length = methods.length; i < length; i++) {
252                                         if (this.selector == methods[i].selector && this.areParametersEqual(methods[i])) {
253                                                 needRename = true;
254                                                 break check;
255                                         }
256                                 }
257                                 // check for collision with synthetic accessors
258                                 if (knownAccessMethods != null) {
259                                         for (int i = 0, length = knownAccessMethods.length; i < length; i++) {
260                                                 if (knownAccessMethods[i] == null) continue;
261                                                 if (this.selector == knownAccessMethods[i].selector && this.areParametersEqual(knownAccessMethods[i])) {
262                                                         needRename = true;
263                                                         break check;
264                                                 }
265                                         }
266                                 }
267                         }
268                         if (needRename) { // retry with a selector & a growing methodId
269                                 this.selector(CharOperation.concat(AccessMethodPrefix, String.valueOf(++methodId).toCharArray()));
270                         }
271                 } while (needRename);
272         
273                 // retrieve sourceStart position for the target method for line number attributes
274                 AbstractMethodDeclaration[] methodDecls = declaringSourceType.scope.referenceContext.methods;
275                 if (methodDecls != null) {
276                         for (int i = 0, length = methodDecls.length; i < length; i++) {
277                                 if (methodDecls[i].binding == targetMethod) {
278                                         this.sourceStart = methodDecls[i].sourceStart;
279                                         return;
280                                 }
281                         }
282                 }
283         }
284
285         protected boolean isConstructorRelated() {
286                 return accessType == ConstructorAccess;
287         }
288 }