KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > text > java > ParameterGuesser


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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
14 import java.util.ArrayList JavaDoc;
15 import java.util.Arrays JavaDoc;
16 import java.util.Collections JavaDoc;
17 import java.util.Comparator JavaDoc;
18 import java.util.HashMap JavaDoc;
19 import java.util.HashSet JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.List JavaDoc;
22 import java.util.ListIterator JavaDoc;
23 import java.util.Map JavaDoc;
24 import java.util.Set JavaDoc;
25
26 import org.eclipse.core.runtime.Assert;
27
28 import org.eclipse.swt.graphics.Image;
29
30 import org.eclipse.jface.resource.ImageDescriptor;
31
32 import org.eclipse.jface.text.IDocument;
33 import org.eclipse.jface.text.Position;
34 import org.eclipse.jface.text.contentassist.ICompletionProposal;
35
36 import org.eclipse.jdt.core.CompletionProposal;
37 import org.eclipse.jdt.core.CompletionRequestor;
38 import org.eclipse.jdt.core.Flags;
39 import org.eclipse.jdt.core.ICompilationUnit;
40 import org.eclipse.jdt.core.IJavaElement;
41 import org.eclipse.jdt.core.IJavaProject;
42 import org.eclipse.jdt.core.IType;
43 import org.eclipse.jdt.core.ITypeHierarchy;
44 import org.eclipse.jdt.core.JavaCore;
45 import org.eclipse.jdt.core.JavaModelException;
46 import org.eclipse.jdt.core.Signature;
47
48 import org.eclipse.jdt.internal.corext.util.SuperTypeHierarchyCache;
49
50 import org.eclipse.jdt.ui.JavaElementImageDescriptor;
51
52 import org.eclipse.jdt.internal.ui.JavaPlugin;
53 import org.eclipse.jdt.internal.ui.JavaPluginImages;
54 import org.eclipse.jdt.internal.ui.text.template.contentassist.PositionBasedCompletionProposal;
55 import org.eclipse.jdt.internal.ui.util.StringMatcher;
56 import org.eclipse.jdt.internal.ui.viewsupport.ImageDescriptorRegistry;
57 import org.eclipse.jdt.internal.ui.viewsupport.JavaElementImageProvider;
58
59 /**
60  * This class triggers a code-completion that will track all local ane member variables for later
61  * use as a parameter guessing proposal.
62  *
63  * @author Andrew McCullough
64  */

65 public class ParameterGuesser {
66
67     final class Variable {
68
69         /**
70          * Variable type. Used to choose the best guess based on scope (Local beats instance beats inherited)
71          */

72         public static final int LOCAL= 0;
73         public static final int FIELD= 1;
74         public static final int METHOD= 1;
75         public static final int INHERITED_FIELD= 3;
76         public static final int INHERITED_METHOD= 3;
77
78         public final String JavaDoc typePackage;
79         public final String JavaDoc typeName;
80         public final String JavaDoc name;
81         public final int variableType;
82         public final int positionScore;
83         public boolean alreadyMatched;
84         public char[] triggerChars;
85         public ImageDescriptor descriptor;
86         public boolean isAutoboxingMatch;
87         private String JavaDoc fFQN;
88         private boolean fFQNResolved= false;
89         private IType fType;
90         private boolean fTypeResolved= false;
91
92         /**
93          * Creates a variable.
94          */

95         public Variable(String JavaDoc typePackage, String JavaDoc typeName, String JavaDoc name, int variableType, int positionScore, char[] triggers, ImageDescriptor descriptor) {
96             if (typePackage == null)
97                 typePackage= ""; //$NON-NLS-1$
98
if (typeName == null)
99                 typeName= ""; //$NON-NLS-1$
100
this.typePackage= typePackage;
101             this.typeName= typeName;
102             this.name= name;
103             this.variableType= variableType;
104             this.positionScore= positionScore;
105             triggerChars= triggers;
106             this.descriptor= descriptor;
107         }
108
109         /*
110          * @see Object#toString()
111          */

112         public String JavaDoc toString() {
113
114             StringBuffer JavaDoc buffer= new StringBuffer JavaDoc();
115
116             if (typePackage.length() != 0) {
117                 buffer.append(typePackage);
118                 buffer.append('.');
119             }
120
121             buffer.append(typeName);
122             buffer.append(' ');
123             buffer.append(name);
124             buffer.append(" ("); //$NON-NLS-1$
125
buffer.append(variableType);
126             buffer.append(')');
127
128             return buffer.toString();
129         }
130
131         String JavaDoc getFQN() {
132             if (!fFQNResolved) {
133                 fFQNResolved= true;
134                 fFQN= computeFQN(typePackage, typeName);
135             }
136             return fFQN;
137         }
138         
139         private String JavaDoc computeFQN(String JavaDoc pkg, String JavaDoc type) {
140             if (pkg.length() != 0) {
141                 return pkg + '.' + type;
142             }
143             return type;
144         }
145
146
147         IType getType(IJavaProject project) throws JavaModelException {
148             if (!fTypeResolved) {
149                 fTypeResolved= true;
150                 if (typePackage.length() > 0)
151                     fType= project.findType(getFQN());
152             }
153             return fType;
154         }
155
156         boolean isPrimitive() {
157             return ParameterGuesser.PRIMITIVE_ASSIGNMENTS.containsKey(getFQN());
158         }
159
160         boolean isArrayType() {
161             // check for an exact match (fast)
162
return getFQN().endsWith("[]"); //$NON-NLS-1$
163
}
164
165         boolean isHierarchyAssignable(Variable rhs) throws JavaModelException {
166             IJavaProject project= fCompilationUnit.getJavaProject();
167             IType paramType= getType(project);
168             IType varType= rhs.getType(project);
169             if (varType == null || paramType == null)
170                 return false;
171         
172             ITypeHierarchy hierarchy= SuperTypeHierarchyCache.getTypeHierarchy(varType);
173             return hierarchy.contains(paramType);
174         }
175
176         boolean isAutoBoxingAssignable(Variable rhs) {
177             // auto-unbox variable to match primitive parameter
178
if (isPrimitive()) {
179                 String JavaDoc unboxedVariable= ParameterGuesser.getAutoUnboxedType(rhs.getFQN());
180                 return ParameterGuesser.isPrimitiveAssignable(typeName, unboxedVariable);
181             }
182         
183             // variable is primitive, auto-box to match parameter type
184
if (rhs.isPrimitive()) {
185                 String JavaDoc unboxedType= ParameterGuesser.getAutoUnboxedType(getFQN());
186                 return ParameterGuesser.isPrimitiveAssignable(unboxedType, rhs.typeName);
187             }
188         
189             return false;
190         }
191
192         /**
193          * Return true if <code>rhs</code> is assignable to the receiver
194          */

195         boolean isAssignable(Variable rhs) throws JavaModelException {
196         
197             // if there is no package specified, do the check on type name only. This will work for primitives
198
// and for local variables that cannot be resolved.
199
if (typePackage.length() == 0 || rhs.typePackage.length() == 0) {
200         
201                 if (rhs.typeName.equals(typeName))
202                     return true;
203         
204                 if (ParameterGuesser.isPrimitiveAssignable(typeName, rhs.typeName))
205                     return true;
206         
207                 if (fAllowAutoBoxing && isAutoBoxingAssignable(rhs)) {
208                     rhs.isAutoboxingMatch= true;
209                     return true;
210                 }
211         
212                 return false;
213             }
214         
215             // if we get to here, we're doing a "fully qualified match" -- meaning including packages, no primitives
216
// and no unresolved variables.
217

218             // if there is an exact textual match, there is no need to search type hierarchy.. this is
219
// a quick way to pick up an exact match.
220
if (rhs.getFQN().equals(getFQN()))
221                 return true;
222         
223             // otherwise, we get a match/no match by searching the type hierarchy
224
return isHierarchyAssignable(rhs);
225         }
226     }
227
228     private static final char[] NO_TRIGGERS= new char[0];
229     private static final char[] VOID= "void".toCharArray(); //$NON-NLS-1$
230
private static final char[] HASHCODE= "hashCode()".toCharArray(); //$NON-NLS-1$
231
private static final char[] TOSTRING= "toString()".toCharArray(); //$NON-NLS-1$
232
private static final char[] CLONE= "clone()".toCharArray(); //$NON-NLS-1$
233

234     private final class VariableCollector extends CompletionRequestor {
235
236         /** The enclosing type name */
237         private String JavaDoc fEnclosingTypeName;
238         /** The local and member variables */
239         private List JavaDoc fVars;
240         
241         
242         VariableCollector() {
243             setIgnored(CompletionProposal.ANONYMOUS_CLASS_DECLARATION, true);
244             setIgnored(CompletionProposal.FIELD_REF, false);
245             setIgnored(CompletionProposal.KEYWORD, true);
246             setIgnored(CompletionProposal.LABEL_REF, true);
247             setIgnored(CompletionProposal.METHOD_DECLARATION, true);
248             setIgnored(CompletionProposal.METHOD_NAME_REFERENCE, true);
249             setIgnored(CompletionProposal.METHOD_REF, false);
250             setIgnored(CompletionProposal.PACKAGE_REF, true);
251             setIgnored(CompletionProposal.POTENTIAL_METHOD_DECLARATION, true);
252             setIgnored(CompletionProposal.VARIABLE_DECLARATION, true);
253             setIgnored(CompletionProposal.TYPE_REF, true);
254             setIgnored(CompletionProposal.ANNOTATION_ATTRIBUTE_REF, false);
255             setIgnored(CompletionProposal.LOCAL_VARIABLE_REF, false);
256         }
257
258         public List JavaDoc collect(int codeAssistOffset, ICompilationUnit compilationUnit) throws JavaModelException {
259             Assert.isTrue(codeAssistOffset >= 0);
260             Assert.isNotNull(compilationUnit);
261
262             fVars= new ArrayList JavaDoc();
263
264             String JavaDoc source= compilationUnit.getSource();
265             if (source == null)
266                 return fVars;
267
268             fEnclosingTypeName= getEnclosingTypeName(codeAssistOffset, compilationUnit);
269
270             // find some whitespace to start our variable-finding code complete from.
271
// this allows the VariableTracker to find all available variables (no prefix to match for the code completion)
272
int completionOffset= getCompletionOffset(source, codeAssistOffset);
273
274             compilationUnit.codeComplete(completionOffset, this);
275
276             // add this, true, false
277
int dotPos= fEnclosingTypeName.lastIndexOf('.');
278             String JavaDoc thisType;
279             String JavaDoc thisPkg;
280             if (dotPos != -1) {
281                 thisType= fEnclosingTypeName.substring(dotPos + 1);
282                 thisPkg= fEnclosingTypeName.substring(0, dotPos);
283             } else {
284                 thisPkg= new String JavaDoc();
285                 thisType= fEnclosingTypeName;
286             }
287             addVariable(Variable.FIELD, thisPkg.toCharArray(), thisType.toCharArray(), "this".toCharArray(), new char[] {'.'}, getFieldDescriptor(Flags.AccPublic | Flags.AccFinal)); //$NON-NLS-1$
288
addVariable(Variable.FIELD, NO_TRIGGERS, "boolean".toCharArray(), "true".toCharArray(), NO_TRIGGERS, null); //$NON-NLS-1$//$NON-NLS-2$
289
addVariable(Variable.FIELD, NO_TRIGGERS, "boolean".toCharArray(), "false".toCharArray(), NO_TRIGGERS, null); //$NON-NLS-1$//$NON-NLS-2$
290

291             return fVars;
292         }
293
294         private String JavaDoc getEnclosingTypeName(int codeAssistOffset, ICompilationUnit compilationUnit) throws JavaModelException {
295
296             IJavaElement element= compilationUnit.getElementAt(codeAssistOffset);
297             if (element == null)
298                 return null;
299
300             element= element.getAncestor(IJavaElement.TYPE);
301             if (element == null)
302                 return null;
303
304             return element.getElementName();
305         }
306
307         /**
308          * Determine if the declaring type matches the type of the code completion invocation
309          */

310         private final boolean isInherited(String JavaDoc declaringTypeName) {
311             return !declaringTypeName.equals(fEnclosingTypeName);
312         }
313
314         private void addVariable(int varType, char[] typePackageName, char[] typeName, char[] name, char[] triggers, ImageDescriptor descriptor) {
315             fVars.add(new Variable(new String JavaDoc(typePackageName), new String JavaDoc(typeName), new String JavaDoc(name), varType, fVars.size(), triggers, descriptor));
316         }
317
318         private void acceptField(char[] declaringTypeName, char[] name, char[] typePackageName, char[] typeName, int modifiers) {
319             if (!isInherited(new String JavaDoc(declaringTypeName)))
320                 addVariable(Variable.FIELD, typePackageName, typeName, name, NO_TRIGGERS, getFieldDescriptor(modifiers));
321             else
322                 addVariable(Variable.INHERITED_FIELD, typePackageName, typeName, name, NO_TRIGGERS, getFieldDescriptor(modifiers));
323         }
324
325         private void acceptLocalVariable(char[] name, char[] typePackageName, char[] typeName, int modifiers) {
326             addVariable(Variable.LOCAL, typePackageName, typeName, name, NO_TRIGGERS, decorate(JavaPluginImages.DESC_OBJS_LOCAL_VARIABLE, modifiers, false));
327         }
328
329         private void acceptMethod(char[] declaringTypeName, char[] returnTypePackageName, char[] returnTypeName, char[] completionName, int modifiers) {
330             if (!filter(returnTypeName, completionName))
331                 addVariable(isInherited(new String JavaDoc(declaringTypeName)) ? Variable.INHERITED_METHOD : Variable.METHOD, returnTypePackageName, returnTypeName, completionName, NO_TRIGGERS, getMemberDescriptor(modifiers));
332         }
333
334         private boolean filter(char[] returnTypeName, char[] completionName) {
335             return Arrays.equals(VOID, returnTypeName) || Arrays.equals(HASHCODE, completionName) || Arrays.equals(TOSTRING, completionName) || Arrays.equals(CLONE, completionName);
336         }
337
338         protected ImageDescriptor getMemberDescriptor(int modifiers) {
339             ImageDescriptor desc= JavaElementImageProvider.getMethodImageDescriptor(false, modifiers);
340             return decorate(desc, modifiers, false);
341         }
342
343         protected ImageDescriptor getFieldDescriptor(int modifiers) {
344             ImageDescriptor desc= JavaElementImageProvider.getFieldImageDescriptor(false, modifiers);
345             return decorate(desc, modifiers, true);
346         }
347
348         private ImageDescriptor decorate(ImageDescriptor descriptor, int modifiers, boolean isField) {
349             int flags= 0;
350
351             if (Flags.isDeprecated(modifiers))
352                 flags |= JavaElementImageDescriptor.DEPRECATED;
353
354             if (Flags.isStatic(modifiers))
355                 flags |= JavaElementImageDescriptor.STATIC;
356
357             if (Flags.isFinal(modifiers))
358                 flags |= JavaElementImageDescriptor.FINAL;
359
360             if (Flags.isSynchronized(modifiers))
361                 flags |= JavaElementImageDescriptor.SYNCHRONIZED;
362
363             if (Flags.isAbstract(modifiers))
364                 flags |= JavaElementImageDescriptor.ABSTRACT;
365             
366             if (isField) {
367                 if (Flags.isVolatile(modifiers))
368                     flags |= JavaElementImageDescriptor.VOLATILE;
369     
370                 if (Flags.isTransient(modifiers))
371                     flags |= JavaElementImageDescriptor.TRANSIENT;
372             }
373             
374             return new JavaElementImageDescriptor(descriptor, flags, JavaElementImageProvider.SMALL_SIZE);
375
376         }
377
378         /*
379          * @see org.eclipse.jdt.core.CompletionRequestor#accept(org.eclipse.jdt.core.CompletionProposal)
380          */

381         public void accept(CompletionProposal proposal) {
382             if (isIgnored(proposal.getKind()))
383                 return;
384             
385             switch (proposal.getKind()) {
386                 case CompletionProposal.FIELD_REF:
387                     acceptField(
388                             Signature.getSignatureSimpleName(proposal.getDeclarationSignature()),
389                             proposal.getName(),
390                             Signature.getSignatureQualifier(proposal.getSignature()),
391                             Signature.getSignatureSimpleName(proposal.getSignature()),
392                             proposal.getFlags());
393                     return;
394                 case CompletionProposal.LOCAL_VARIABLE_REF:
395                     acceptLocalVariable(
396                             proposal.getCompletion(),
397                             Signature.getSignatureQualifier(proposal.getSignature()),
398                             Signature.getSignatureSimpleName(proposal.getSignature()),
399                             proposal.getFlags());
400                     return;
401                 case CompletionProposal.METHOD_REF:
402                     if (Signature.getParameterCount(proposal.getSignature()) == 0)
403                         acceptMethod(
404                                 Signature.getSignatureSimpleName(proposal.getDeclarationSignature()),
405                                 Signature.getSignatureQualifier(Signature.getReturnType(proposal.getSignature())),
406                                 Signature.getSignatureSimpleName(Signature.getReturnType(proposal.getSignature())),
407                                 proposal.getCompletion(),
408                                 proposal.getFlags());
409
410             }
411             
412         }
413     }
414
415     private static final Map JavaDoc PRIMITIVE_ASSIGNMENTS;
416     private static final Map JavaDoc AUTOUNBOX;
417
418     static {
419         HashMap JavaDoc primitiveAssignments= new HashMap JavaDoc();
420         // put (LHS, RHS)
421
primitiveAssignments.put("boolean", Collections.singleton("boolean")); //$NON-NLS-1$ //$NON-NLS-2$
422
primitiveAssignments.put("byte", Collections.singleton("byte")); //$NON-NLS-1$ //$NON-NLS-2$
423
primitiveAssignments.put("short", Collections.unmodifiableSet(new HashSet JavaDoc(Arrays.asList(new String JavaDoc[] {"short", "byte"})))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
424
primitiveAssignments.put("char", Collections.singleton("char")); //$NON-NLS-1$ //$NON-NLS-2$
425
primitiveAssignments.put("int", Collections.unmodifiableSet(new HashSet JavaDoc(Arrays.asList(new String JavaDoc[] {"int", "short", "char", "byte"})))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
426
primitiveAssignments.put("long", Collections.unmodifiableSet(new HashSet JavaDoc(Arrays.asList(new String JavaDoc[] {"long", "int", "short", "char", "byte"})))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
427
primitiveAssignments.put("float", Collections.unmodifiableSet(new HashSet JavaDoc(Arrays.asList(new String JavaDoc[] {"float", "long", "int", "short", "char", "byte"})))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
428
primitiveAssignments.put("double", Collections.unmodifiableSet(new HashSet JavaDoc(Arrays.asList(new String JavaDoc[] {"double", "float", "long", "int", "short", "char", "byte"})))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
429
primitiveAssignments.put("primitive number", Collections.unmodifiableSet(new HashSet JavaDoc(Arrays.asList(new String JavaDoc[] {"double", "float", "long", "int", "short", "byte"})))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
430
PRIMITIVE_ASSIGNMENTS= Collections.unmodifiableMap(primitiveAssignments);
431
432         HashMap JavaDoc autounbox= new HashMap JavaDoc();
433         autounbox.put("java.lang.Boolean", "boolean"); //$NON-NLS-1$ //$NON-NLS-2$
434
autounbox.put("java.lang.Byte", "byte"); //$NON-NLS-1$ //$NON-NLS-2$
435
autounbox.put("java.lang.Short", "short"); //$NON-NLS-1$ //$NON-NLS-2$
436
autounbox.put("java.lang.Character", "char"); //$NON-NLS-1$ //$NON-NLS-2$
437
autounbox.put("java.lang.Integer", "int"); //$NON-NLS-1$ //$NON-NLS-2$
438
autounbox.put("java.lang.Long", "long"); //$NON-NLS-1$ //$NON-NLS-2$
439
autounbox.put("java.lang.Float", "float"); //$NON-NLS-1$ //$NON-NLS-2$
440
autounbox.put("java.lang.Double", "double"); //$NON-NLS-1$ //$NON-NLS-2$
441
autounbox.put("java.lang.Number", "primitive number"); // dummy for reverse assignment //$NON-NLS-1$ //$NON-NLS-2$
442
AUTOUNBOX= Collections.unmodifiableMap(autounbox);
443     }
444
445     private static final boolean isPrimitiveAssignable(String JavaDoc lhs, String JavaDoc rhs) {
446         Set JavaDoc targets= (Set JavaDoc) PRIMITIVE_ASSIGNMENTS.get(lhs);
447         return targets != null && targets.contains(rhs);
448     }
449
450     private static final String JavaDoc getAutoUnboxedType(String JavaDoc type) {
451         String JavaDoc primitive= (String JavaDoc) AUTOUNBOX.get(type);
452         return primitive;
453     }
454
455     /** The compilation unit we are computing the completion for */
456     private final ICompilationUnit fCompilationUnit;
457     /** The code assist offset. */
458     private final int fCodeAssistOffset;
459     /** Local and member variables of the compilation unit */
460     private List JavaDoc fVariables;
461     private ImageDescriptorRegistry fRegistry= JavaPlugin.getImageDescriptorRegistry();
462     private boolean fAllowAutoBoxing;
463
464     /**
465      * Creates a parameter guesser for compilation unit and offset.
466      *
467      * @param codeAssistOffset the offset at which to perform code assist
468      * @param compilationUnit the compilation unit in which code assist is performed
469      */

470     public ParameterGuesser(int codeAssistOffset, ICompilationUnit compilationUnit) {
471         Assert.isTrue(codeAssistOffset >= 0);
472         Assert.isNotNull(compilationUnit);
473
474         fCodeAssistOffset= codeAssistOffset;
475         fCompilationUnit= compilationUnit;
476
477
478         IJavaProject project= fCompilationUnit.getJavaProject();
479         String JavaDoc sourceVersion= project == null
480                 ? JavaCore.getOption(JavaCore.COMPILER_SOURCE)
481                 : project.getOption(JavaCore.COMPILER_SOURCE, true);
482
483         fAllowAutoBoxing= JavaCore.VERSION_1_5.compareTo(sourceVersion) <= 0;
484     }
485
486     /**
487      * Returns the offset at which code assist is performed.
488      */

489     public int getCodeAssistOffset() {
490         return fCodeAssistOffset;
491     }
492
493     /**
494      * Returns the compilation unit in which code assist is performed.
495      */

496     public ICompilationUnit getCompilationUnit() {
497         return fCompilationUnit;
498     }
499
500     /**
501      * Returns the matches for the type and name argument, ordered by match quality.
502      *
503      * @param paramPackage - the package of the parameter we are trying to match
504      * @param paramType - the qualified name of the parameter we are trying to match
505      * @param paramName - the name of the parameter (used to find similarly named matches)
506      * @param pos
507      * @param document
508      * @return returns the name of the best match, or <code>null</code> if no match found
509      * @throws JavaModelException
510      */

511     public ICompletionProposal[] parameterProposals(String JavaDoc paramPackage, String JavaDoc paramType, String JavaDoc paramName, Position pos, IDocument document) throws JavaModelException {
512
513         if (fVariables == null) {
514             VariableCollector variableCollector= new VariableCollector();
515             fVariables= variableCollector.collect(fCodeAssistOffset, fCompilationUnit);
516         }
517
518         Variable parameter= new Variable(paramPackage, paramType, paramName, Variable.LOCAL, 0, null, null);
519
520         List JavaDoc typeMatches= findProposalsMatchingType(fVariables, parameter);
521         orderMatches(typeMatches, paramName);
522
523         ICompletionProposal[] ret= new ICompletionProposal[typeMatches.size()];
524         int i= 0; int replacementLength= 0;
525         for (Iterator JavaDoc it= typeMatches.iterator(); it.hasNext();) {
526             Variable v= (Variable)it.next();
527             if (i == 0) {
528                 v.alreadyMatched= true;
529                 replacementLength= v.name.length();
530             }
531
532             final char[] triggers= new char[v.triggerChars.length + 1];
533             System.arraycopy(v.triggerChars, 0, triggers, 0, v.triggerChars.length);
534             String JavaDoc displayString= v.isAutoboxingMatch ? v.name : v.name;
535             triggers[triggers.length - 1]= ';';
536             ICompletionProposal proposal= new PositionBasedCompletionProposal(v.name, pos, replacementLength, getImage(v.descriptor), displayString, null, null) {
537                 public char[] getTriggerCharacters() {
538                     return triggers;
539                 }
540             };
541             ret[i++]= proposal;
542         }
543
544         return ret;
545     }
546
547     private static class MatchComparator implements Comparator JavaDoc {
548
549         private String JavaDoc fParamName;
550
551         MatchComparator(String JavaDoc paramName) {
552             fParamName= paramName;
553         }
554         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
555             Variable one= (Variable)o1;
556             Variable two= (Variable)o2;
557
558             return score(two) - score(one);
559         }
560
561         /**
562          * The four order criteria as described below - put already used into bit 10, all others into
563          * bits 0-9, 11-20, 21-30; 31 is sign - always 0
564          * @param v
565          * @return the score for <code>v</code>
566          */

567         private int score(Variable v) {
568             int variableScore= 100 - v.variableType; // since these are increasing with distance
569
int subStringScore= getLongestCommonSubstring(v.name, fParamName).length();
570             // substring scores under 60% are not considered
571
// this prevents marginal matches like a - ba and false - isBool that will
572
// destroy the sort order
573
int shorter= Math.min(v.name.length(), fParamName.length());
574             if (subStringScore < 0.6 * shorter)
575                 subStringScore= 0;
576
577             int positionScore= v.positionScore; // since ???
578
int matchedScore= v.alreadyMatched ? 0 : 1;
579             int autoboxingScore= v.isAutoboxingMatch ? 0 : 1;
580
581             int score= autoboxingScore << 30 | variableScore << 21 | subStringScore << 11 | matchedScore << 10 | positionScore;
582             return score;
583         }
584
585     }
586
587     /**
588      * Determine the best match of all possible type matches. The input into this method is all
589      * possible completions that match the type of the argument. The purpose of this method is to
590      * choose among them based on the following simple rules:
591      *
592      * 1) Local Variables > Instance/Class Variables > Inherited Instance/Class Variables
593      *
594      * 2) A longer case insensitive substring match will prevail
595      *
596      * 3) Variables that have not been used already during this completion will prevail over
597      * those that have already been used (this avoids the same String/int/char from being passed
598      * in for multiple arguments)
599      *
600      * 4) A better source position score will prevail (the declaration point of the variable, or
601      * "how close to the point of completion?"
602      */

603     private static void orderMatches(List JavaDoc typeMatches, String JavaDoc paramName) {
604         if (typeMatches != null) Collections.sort(typeMatches, new MatchComparator(paramName));
605     }
606
607     /**
608      * Finds a local or member variable that matched the type of the parameter
609      */

610     private List JavaDoc findProposalsMatchingType(List JavaDoc proposals, Variable parameter) throws JavaModelException {
611
612         if (parameter.getFQN().length() == 0)
613             return null;
614
615         // traverse the lists in reverse order, since it is empirically true that the code
616
// completion engine returns variables in the order they are found -- and we want to find
617
// matches closest to the code completion point.. No idea if this behavior is guaranteed.
618

619         List JavaDoc matches= new ArrayList JavaDoc();
620
621         for (ListIterator JavaDoc iterator= proposals.listIterator(proposals.size()); iterator.hasPrevious(); ) {
622             Variable variable= (Variable) iterator.previous();
623             variable.isAutoboxingMatch= false;
624             if (parameter.isAssignable(variable))
625                 matches.add(variable);
626         }
627
628         return matches;
629     }
630
631     /**
632      * Returns the longest common substring of two strings.
633      */

634     private static String JavaDoc getLongestCommonSubstring(String JavaDoc first, String JavaDoc second) {
635
636         String JavaDoc shorter= (first.length() <= second.length()) ? first : second;
637         String JavaDoc longer= shorter == first ? second : first;
638
639         int minLength= shorter.length();
640
641         StringBuffer JavaDoc pattern= new StringBuffer JavaDoc(shorter.length() + 2);
642         String JavaDoc longestCommonSubstring= ""; //$NON-NLS-1$
643

644         for (int i= 0; i < minLength; i++) {
645             for (int j= i + 1; j <= minLength; j++) {
646                 if (j - i < longestCommonSubstring.length())
647                     continue;
648
649                 String JavaDoc substring= shorter.substring(i, j);
650                 pattern.setLength(0);
651                 pattern.append('*');
652                 pattern.append(substring);
653                 pattern.append('*');
654
655                 StringMatcher matcher= new StringMatcher(pattern.toString(), true, false);
656                 if (matcher.match(longer))
657                     longestCommonSubstring= substring;
658             }
659         }
660
661         return longestCommonSubstring;
662     }
663
664     private Image getImage(ImageDescriptor descriptor) {
665         return (descriptor == null) ? null : fRegistry.get(descriptor);
666     }
667
668     private static int getCompletionOffset(String JavaDoc source, int start) {
669         int index= start;
670         char c;
671         while (index > 0 && (c= source.charAt(index - 1)) != '{' && c != ';')
672             index--;
673         return Math.min(index + 1, source.length());
674     }
675
676 }
677
Popular Tags