]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-after/ui/org/eclipse/jdt/internal/ui/text/java/ParameterGuesser.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-after / ui / org / eclipse / jdt / internal / ui / text / java / ParameterGuesser.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2011 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.ui.text.java;
12
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.Comparator;
16 import java.util.HashSet;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Set;
20
21 import org.eclipse.swt.graphics.Image;
22
23 import org.eclipse.jface.resource.ImageDescriptor;
24
25 import org.eclipse.jface.text.Position;
26 import org.eclipse.jface.text.contentassist.ICompletionProposal;
27
28 import org.eclipse.jdt.core.BindingKey;
29 import org.eclipse.jdt.core.Flags;
30 import org.eclipse.jdt.core.IField;
31 import org.eclipse.jdt.core.IJavaElement;
32 import org.eclipse.jdt.core.ILocalVariable;
33 import org.eclipse.jdt.core.IMethod;
34 import org.eclipse.jdt.core.IType;
35 import org.eclipse.jdt.core.JavaModelException;
36 import org.eclipse.jdt.core.Signature;
37 import org.eclipse.jdt.core.dom.PrimitiveType;
38 import org.eclipse.jdt.core.dom.PrimitiveType.Code;
39
40 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
41
42 import org.eclipse.jdt.ui.JavaElementImageDescriptor;
43
44 import org.eclipse.jdt.internal.ui.JavaPlugin;
45 import org.eclipse.jdt.internal.ui.JavaPluginImages;
46 import org.eclipse.jdt.internal.ui.text.template.contentassist.PositionBasedCompletionProposal;
47 import org.eclipse.jdt.internal.ui.util.StringMatcher;
48 import org.eclipse.jdt.internal.ui.viewsupport.JavaElementImageProvider;
49
50
51 /**
52  * This class triggers a code-completion that will track all local and member variables for later
53  * use as a parameter guessing proposal.
54  */
55 public class ParameterGuesser {
56
57         final static class Variable {
58
59                 /**
60                  * Variable type. Used to choose the best guess based on scope (Local beats instance beats inherited).
61                  */
62                 public static final int LOCAL= 0;
63                 public static final int FIELD= 1;
64                 public static final int INHERITED_FIELD= 2;
65                 public static final int METHOD= 3;
66                 public static final int INHERITED_METHOD= 4;
67                 public static final int LITERALS= 5;
68
69                 public final String qualifiedTypeName;
70                 public final String name;
71                 public final int variableType;
72                 public final int positionScore;
73
74                 public final boolean isAutoboxingMatch;
75
76                 public final char[] triggerChars;
77                 public final ImageDescriptor descriptor;
78
79                 public boolean alreadyMatched;
80
81                 public Variable(String qualifiedTypeName, String name, int variableType, boolean isAutoboxMatch, int positionScore, char[] triggerChars, ImageDescriptor descriptor) {
82                         this.qualifiedTypeName= qualifiedTypeName;
83                         this.name= name;
84                         this.variableType= variableType;
85                         this.positionScore= positionScore;
86                         this.triggerChars= triggerChars;
87                         this.descriptor= descriptor;
88                         this.isAutoboxingMatch= isAutoboxMatch;
89                         this.alreadyMatched= false;
90                 }
91
92                 /*
93                  * @see Object#toString()
94                  */
95                 @Override
96                 public String toString() {
97
98                         StringBuffer buffer= new StringBuffer();
99                         buffer.append(qualifiedTypeName);
100                         buffer.append(' ');
101                         buffer.append(name);
102                         buffer.append(" ("); //$NON-NLS-1$
103                         buffer.append(variableType);
104                         buffer.append(')');
105
106                         return buffer.toString();
107                 }
108
109                 public void generated_8831347088891252839(ArrayList<Variable> res, ParameterGuesser parameterguesser) {
110                         if (parameterguesser.fAlreadyMatchedNames.contains(name)) {
111                                 alreadyMatched= true;
112                         }
113                         res.add(this);
114                 }
115
116                 public void generated_140389272619292531(ParameterGuesser parameterguesser, final char[] triggers) {
117                         System.arraycopy(triggerChars, 0, triggers, 0, triggerChars.length);
118                 }
119
120                 public int generated_5214888427673676057(MatchComparator matchcomparator) {
121                         int variableScore= 100 - variableType; // since these are increasing with distance
122                         int subStringScore= MatchComparator.getLongestCommonSubstring(name, matchcomparator.fParamName).length();
123                         // substring scores under 60% are not considered
124                         // this prevents marginal matches like a - ba and false - isBool that will
125                         // destroy the sort order
126                         int shorter= Math.min(name.length(), matchcomparator.fParamName.length());
127                         if (subStringScore < 0.6 * shorter)
128                                 subStringScore= 0;
129                 
130                         int positionScore= positionScore; // since ???
131                         int matchedScore= alreadyMatched ? 0 : 1;
132                         int autoboxingScore= isAutoboxingMatch ? 0 : 1;
133                 
134                         int score= autoboxingScore << 30 | variableScore << 21 | subStringScore << 11 | matchedScore << 10 | positionScore;
135                         return score;
136                 }
137         }
138
139         private static final char[] NO_TRIGGERS= new char[0];
140
141         private final Set<String> fAlreadyMatchedNames;
142         private final IJavaElement fEnclosingElement;
143
144         /**
145          * Creates a parameter guesser
146          * 
147          * @param enclosingElement the enclosing Java element
148          */
149         public ParameterGuesser(IJavaElement enclosingElement) {
150                 fEnclosingElement= enclosingElement;
151                 fAlreadyMatchedNames= new HashSet<String>();
152         }
153
154         private List<Variable> evaluateVisibleMatches(String expectedType, IJavaElement[] suggestions) throws JavaModelException {
155                 IType currentType= null;
156                 if (fEnclosingElement != null) {
157                         currentType= (IType) fEnclosingElement.getAncestor(IJavaElement.TYPE);
158                 }
159
160                 ArrayList<Variable> res= new ArrayList<Variable>();
161                 for (int i= 0; i < suggestions.length; i++) {
162                         Variable variable= createVariable(suggestions[i], currentType, expectedType, i);
163                         if (variable != null) {
164                                 variable.generated_8831347088891252839(res, this);
165                         }
166                 }
167
168                 // add 'this'
169                 if (currentType != null && !(fEnclosingElement instanceof IMethod && Flags.isStatic(((IMethod) fEnclosingElement).getFlags()))) {
170                         String fullyQualifiedName= currentType.getFullyQualifiedName('.');
171                         if (fullyQualifiedName.equals(expectedType)) {
172                                 ImageDescriptor desc= new JavaElementImageDescriptor(JavaPluginImages.DESC_FIELD_PUBLIC, JavaElementImageDescriptor.FINAL | JavaElementImageDescriptor.STATIC, JavaElementImageProvider.SMALL_SIZE);
173                                 res.add(new Variable(fullyQualifiedName, "this", Variable.LITERALS, false, res.size(), new char[] {'.'}, desc));  //$NON-NLS-1$
174                         }
175                 }
176
177                 Code primitiveTypeCode= getPrimitiveTypeCode(expectedType);
178                 if (primitiveTypeCode == null) {
179                         // add 'null'
180                         res.add(new Variable(expectedType, "null", Variable.LITERALS, false, res.size(), NO_TRIGGERS, null));  //$NON-NLS-1$
181                 } else {
182                         String typeName= primitiveTypeCode.toString();
183                         boolean isAutoboxing= !typeName.equals(expectedType);
184                         if (primitiveTypeCode == PrimitiveType.BOOLEAN) {
185                                 // add 'true', 'false'
186                                 res.add(new Variable(typeName, "true", Variable.LITERALS, isAutoboxing, res.size(), NO_TRIGGERS, null)); //$NON-NLS-1$
187                                 res.add(new Variable(typeName, "false", Variable.LITERALS, isAutoboxing, res.size(), NO_TRIGGERS, null)); //$NON-NLS-1$
188                         } else {
189                                 // add 0
190                                 res.add(new Variable(typeName, "0", Variable.LITERALS, isAutoboxing, res.size(), NO_TRIGGERS, null));   //$NON-NLS-1$
191                         }
192                 }
193                 return res;
194         }
195
196         public Variable createVariable(IJavaElement element, IType enclosingType, String expectedType, int positionScore) throws JavaModelException {
197                 int variableType;
198                 int elementType= element.getElementType();
199                 String elementName= element.getElementName();
200
201                 String typeSignature;
202                 switch (elementType) {
203                         case IJavaElement.FIELD: {
204                                 IField field= (IField) element;
205                                 if (field.getDeclaringType().equals(enclosingType)) {
206                                         variableType= Variable.FIELD;
207                                 } else {
208                                         variableType= Variable.INHERITED_FIELD;
209                                 }
210                                 if (field.isResolved()) {
211                                         typeSignature= new BindingKey(field.getKey()).toSignature();
212                                 } else {
213                                         typeSignature= field.getTypeSignature();
214                                 }
215                                 break;
216                         }
217                         case IJavaElement.LOCAL_VARIABLE: {
218                                 ILocalVariable locVar= (ILocalVariable) element;
219                                 variableType= Variable.LOCAL;
220                                 typeSignature= locVar.getTypeSignature();
221                                 break;
222                         }
223                         case IJavaElement.METHOD: {
224                                 IMethod method= (IMethod) element;
225                                 if (isMethodToSuggest(method)) {
226                                         if (method.getDeclaringType().equals(enclosingType)) {
227                                                 variableType= Variable.METHOD;
228                                         } else {
229                                                 variableType= Variable.INHERITED_METHOD;
230                                         }
231                                         if (method.isResolved()) {
232                                                 typeSignature= Signature.getReturnType(new BindingKey(method.getKey()).toSignature());
233                                         } else {
234                                                 typeSignature= method.getReturnType();
235                                         }
236                                         elementName= elementName + "()";  //$NON-NLS-1$
237                                 } else {
238                                         return null;
239                                 }
240                                 break;
241                         }
242                         default:
243                                 return null;
244                 }
245                 String type= Signature.toString(typeSignature);
246
247                 boolean isAutoboxMatch= isPrimitiveType(expectedType) != isPrimitiveType(type);
248                 return new Variable(type, elementName, variableType, isAutoboxMatch, positionScore, NO_TRIGGERS, getImageDescriptor(element));
249         }
250
251         private ImageDescriptor getImageDescriptor(IJavaElement elem) {
252                 JavaElementImageProvider imageProvider= new JavaElementImageProvider();
253                 return imageProvider.generated_470593898409950613(elem);
254         }
255
256         private boolean isPrimitiveType(String type) {
257                 return PrimitiveType.toCode(type) != null;
258         }
259
260         private PrimitiveType.Code getPrimitiveTypeCode(String type) {
261                 PrimitiveType.Code code= PrimitiveType.toCode(type);
262                 if (code != null) {
263                         return code;
264                 }
265                 if (fEnclosingElement != null && JavaModelUtil.is50OrHigher(fEnclosingElement.getJavaProject())) {
266                         if (code == PrimitiveType.SHORT) {
267                                 if ("java.lang.Short".equals(type)) { //$NON-NLS-1$
268                                         return code;
269                                 }
270                         } else if (code == PrimitiveType.INT) {
271                                 if ("java.lang.Integer".equals(type)) { //$NON-NLS-1$
272                                         return code;
273                                 }
274                         } else if (code == PrimitiveType.LONG) {
275                                 if ("java.lang.Long".equals(type)) { //$NON-NLS-1$
276                                         return code;
277                                 }
278                         } else if (code == PrimitiveType.FLOAT) {
279                                 if ("java.lang.Float".equals(type)) { //$NON-NLS-1$
280                                         return code;
281                                 }
282                         } else if (code == PrimitiveType.DOUBLE) {
283                                 if ("java.lang.Double".equals(type)) { //$NON-NLS-1$
284                                         return code;
285                                 }
286                         } else if (code == PrimitiveType.CHAR) {
287                                 if ("java.lang.Character".equals(type)) { //$NON-NLS-1$
288                                         return code;
289                                 }
290                         } else if (code == PrimitiveType.BYTE) {
291                                 if ("java.lang.Byte".equals(type)) { //$NON-NLS-1$
292                                         return code;
293                                 }
294                         }
295                 }
296                 return null;
297         }
298
299         private boolean isMethodToSuggest(IMethod method) {
300                 try {
301                         String methodName= method.getElementName();
302                         return method.getNumberOfParameters() == 0 && !Signature.SIG_VOID.equals(method.getReturnType())
303                                 && (methodName.startsWith("get") || methodName.startsWith("is"));    //$NON-NLS-1$//$NON-NLS-2$
304                 } catch (JavaModelException e) {
305                         return false;
306                 }
307         }
308
309         /**
310          * Returns the matches for the type and name argument, ordered by match quality.
311          * 
312          * @param expectedType - the qualified type of the parameter we are trying to match
313          * @param paramName - the name of the parameter (used to find similarly named matches)
314          * @param pos the position
315          * @param suggestions the suggestions or <code>null</code>
316          * @param fillBestGuess <code>true</code> if the best guess should be filled in
317          * @param isLastParameter <code>true</code> iff this proposal is for the last parameter of a method
318          * @return returns the name of the best match, or <code>null</code> if no match found
319          * @throws JavaModelException if it fails
320          */
321         public ICompletionProposal[] parameterProposals(String expectedType, String paramName, Position pos, IJavaElement[] suggestions, boolean fillBestGuess, boolean isLastParameter) throws JavaModelException {
322                 List<Variable> typeMatches= evaluateVisibleMatches(expectedType, suggestions);
323                 orderMatches(typeMatches, paramName);
324
325                 boolean hasVarWithParamName= false;
326                 ICompletionProposal[] ret= new ICompletionProposal[typeMatches.size()];
327                 int i= 0; int replacementLength= 0;
328                 for (Iterator<Variable> it= typeMatches.iterator(); it.hasNext();) {
329                         Variable v= it.next();
330                         if (i == 0) {
331                                 fAlreadyMatchedNames.add(v.name);
332                                 replacementLength= v.name.length();
333                         }
334
335                         String displayString= v.name;
336                         hasVarWithParamName |= displayString.equals(paramName);
337
338                         final char[] triggers;
339                         if (isLastParameter) {
340                                 triggers= v.triggerChars;
341                         } else {
342                                 triggers= new char[v.triggerChars.length + 1];
343                                 v.generated_140389272619292531(this, triggers);
344                                 triggers[triggers.length - 1]= ',';
345                         }
346                         
347                         ret[i++]= new PositionBasedCompletionProposal(v.name, pos, replacementLength, getImage(v.descriptor), displayString, null, null, triggers);
348                 }
349                 if (!fillBestGuess && !hasVarWithParamName) {
350                         // insert a proposal with the argument name
351                         ICompletionProposal[] extended= new ICompletionProposal[ret.length + 1];
352                         System.arraycopy(ret, 0, extended, 1, ret.length);
353                         extended[0]= new PositionBasedCompletionProposal(paramName, pos, replacementLength, null, paramName, null, null, isLastParameter ? null : new char[] {','});
354                         return extended;
355                 }
356                 return ret;
357         }
358
359         static class MatchComparator implements Comparator<Variable> {
360
361                 private String fParamName;
362
363                 MatchComparator(String paramName) {
364                         fParamName= paramName;
365                 }
366                 public int compare(Variable one, Variable two) {
367                         return score(two) - score(one);
368                 }
369
370                 /**
371                  * The four order criteria as described below - put already used into bit 10, all others
372                  * into bits 0-9, 11-20, 21-30; 31 is sign - always 0
373                  * 
374                  * @param v the variable
375                  * @return the score for <code>v</code>
376                  */
377                 private int score(Variable v) {
378                         int score= v.generated_5214888427673676057(MatchComparator.this);
379                         return score;
380                 }
381
382         }
383
384         /**
385          * Determine the best match of all possible type matches.  The input into this method is all
386          * possible completions that match the type of the argument. The purpose of this method is to
387          * choose among them based on the following simple rules:
388          *
389          *      1) Local Variables > Instance/Class Variables > Inherited Instance/Class Variables
390          *
391          *      2) A longer case insensitive substring match will prevail
392          *
393          *  3) Variables that have not been used already during this completion will prevail over
394          *              those that have already been used (this avoids the same String/int/char from being passed
395          *              in for multiple arguments)
396          *
397          *      4) A better source position score will prevail (the declaration point of the variable, or
398          *              "how close to the point of completion?"
399          *
400          * @param typeMatches the list of type matches
401          * @param paramName the parameter name
402          */
403         private static void orderMatches(List<Variable> typeMatches, String paramName) {
404                 if (typeMatches != null) Collections.sort(typeMatches, new MatchComparator(paramName));
405         }
406
407         /**
408          * Returns the longest common substring of two strings.
409          *
410          * @param first the first string
411          * @param second the second string
412          * @return the longest common substring
413          */
414         private static String getLongestCommonSubstring(String first, String second) {
415
416                 String shorter= (first.length() <= second.length()) ? first : second;
417                 String longer= shorter == first ? second : first;
418
419                 int minLength= shorter.length();
420
421                 StringBuffer pattern= new StringBuffer(shorter.length() + 2);
422                 String longestCommonSubstring= ""; //$NON-NLS-1$
423
424                 for (int i= 0; i < minLength; i++) {
425                         for (int j= i + 1; j <= minLength; j++) {
426                                 if (j - i < longestCommonSubstring.length())
427                                         continue;
428
429                                 String substring= shorter.substring(i, j);
430                                 pattern.setLength(0);
431                                 pattern.append('*');
432                                 pattern.append(substring);
433                                 pattern.append('*');
434
435                                 StringMatcher matcher= new StringMatcher(pattern.toString(), true, false);
436                                 if (matcher.match(longer))
437                                         longestCommonSubstring= substring;
438                         }
439                 }
440
441                 return longestCommonSubstring;
442         }
443
444         private Image getImage(ImageDescriptor descriptor) {
445                 return (descriptor == null) ? null : JavaPlugin.getImageDescriptorRegistry().get(descriptor);
446         }
447
448 }