1 11 package org.eclipse.jdt.internal.ui.text.java; 12 13 14 import java.util.ArrayList ; 15 import java.util.Arrays ; 16 import java.util.Collections ; 17 import java.util.Comparator ; 18 import java.util.HashMap ; 19 import java.util.HashSet ; 20 import java.util.Iterator ; 21 import java.util.List ; 22 import java.util.ListIterator ; 23 import java.util.Map ; 24 import java.util.Set ; 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 65 public class ParameterGuesser { 66 67 final class Variable { 68 69 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 typePackage; 79 public final String typeName; 80 public final String 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 fFQN; 88 private boolean fFQNResolved= false; 89 private IType fType; 90 private boolean fTypeResolved= false; 91 92 95 public Variable(String typePackage, String typeName, String name, int variableType, int positionScore, char[] triggers, ImageDescriptor descriptor) { 96 if (typePackage == null) 97 typePackage= ""; if (typeName == null) 99 typeName= ""; 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 112 public String toString() { 113 114 StringBuffer buffer= new StringBuffer (); 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(" ("); buffer.append(variableType); 126 buffer.append(')'); 127 128 return buffer.toString(); 129 } 130 131 String getFQN() { 132 if (!fFQNResolved) { 133 fFQNResolved= true; 134 fFQN= computeFQN(typePackage, typeName); 135 } 136 return fFQN; 137 } 138 139 private String computeFQN(String pkg, String 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 return getFQN().endsWith("[]"); } 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 if (isPrimitive()) { 179 String unboxedVariable= ParameterGuesser.getAutoUnboxedType(rhs.getFQN()); 180 return ParameterGuesser.isPrimitiveAssignable(typeName, unboxedVariable); 181 } 182 183 if (rhs.isPrimitive()) { 185 String unboxedType= ParameterGuesser.getAutoUnboxedType(getFQN()); 186 return ParameterGuesser.isPrimitiveAssignable(unboxedType, rhs.typeName); 187 } 188 189 return false; 190 } 191 192 195 boolean isAssignable(Variable rhs) throws JavaModelException { 196 197 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 218 if (rhs.getFQN().equals(getFQN())) 221 return true; 222 223 return isHierarchyAssignable(rhs); 225 } 226 } 227 228 private static final char[] NO_TRIGGERS= new char[0]; 229 private static final char[] VOID= "void".toCharArray(); private static final char[] HASHCODE= "hashCode()".toCharArray(); private static final char[] TOSTRING= "toString()".toCharArray(); private static final char[] CLONE= "clone()".toCharArray(); 234 private final class VariableCollector extends CompletionRequestor { 235 236 237 private String fEnclosingTypeName; 238 239 private List 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 collect(int codeAssistOffset, ICompilationUnit compilationUnit) throws JavaModelException { 259 Assert.isTrue(codeAssistOffset >= 0); 260 Assert.isNotNull(compilationUnit); 261 262 fVars= new ArrayList (); 263 264 String source= compilationUnit.getSource(); 265 if (source == null) 266 return fVars; 267 268 fEnclosingTypeName= getEnclosingTypeName(codeAssistOffset, compilationUnit); 269 270 int completionOffset= getCompletionOffset(source, codeAssistOffset); 273 274 compilationUnit.codeComplete(completionOffset, this); 275 276 int dotPos= fEnclosingTypeName.lastIndexOf('.'); 278 String thisType; 279 String thisPkg; 280 if (dotPos != -1) { 281 thisType= fEnclosingTypeName.substring(dotPos + 1); 282 thisPkg= fEnclosingTypeName.substring(0, dotPos); 283 } else { 284 thisPkg= new String (); 285 thisType= fEnclosingTypeName; 286 } 287 addVariable(Variable.FIELD, thisPkg.toCharArray(), thisType.toCharArray(), "this".toCharArray(), new char[] {'.'}, getFieldDescriptor(Flags.AccPublic | Flags.AccFinal)); addVariable(Variable.FIELD, NO_TRIGGERS, "boolean".toCharArray(), "true".toCharArray(), NO_TRIGGERS, null); addVariable(Variable.FIELD, NO_TRIGGERS, "boolean".toCharArray(), "false".toCharArray(), NO_TRIGGERS, null); 291 return fVars; 292 } 293 294 private String 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 310 private final boolean isInherited(String 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 (typePackageName), new String (typeName), new String (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 (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 (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 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 PRIMITIVE_ASSIGNMENTS; 416 private static final Map AUTOUNBOX; 417 418 static { 419 HashMap primitiveAssignments= new HashMap (); 420 primitiveAssignments.put("boolean", Collections.singleton("boolean")); primitiveAssignments.put("byte", Collections.singleton("byte")); primitiveAssignments.put("short", Collections.unmodifiableSet(new HashSet (Arrays.asList(new String [] {"short", "byte"})))); primitiveAssignments.put("char", Collections.singleton("char")); primitiveAssignments.put("int", Collections.unmodifiableSet(new HashSet (Arrays.asList(new String [] {"int", "short", "char", "byte"})))); primitiveAssignments.put("long", Collections.unmodifiableSet(new HashSet (Arrays.asList(new String [] {"long", "int", "short", "char", "byte"})))); primitiveAssignments.put("float", Collections.unmodifiableSet(new HashSet (Arrays.asList(new String [] {"float", "long", "int", "short", "char", "byte"})))); primitiveAssignments.put("double", Collections.unmodifiableSet(new HashSet (Arrays.asList(new String [] {"double", "float", "long", "int", "short", "char", "byte"})))); primitiveAssignments.put("primitive number", Collections.unmodifiableSet(new HashSet (Arrays.asList(new String [] {"double", "float", "long", "int", "short", "byte"})))); PRIMITIVE_ASSIGNMENTS= Collections.unmodifiableMap(primitiveAssignments); 431 432 HashMap autounbox= new HashMap (); 433 autounbox.put("java.lang.Boolean", "boolean"); autounbox.put("java.lang.Byte", "byte"); autounbox.put("java.lang.Short", "short"); autounbox.put("java.lang.Character", "char"); autounbox.put("java.lang.Integer", "int"); autounbox.put("java.lang.Long", "long"); autounbox.put("java.lang.Float", "float"); autounbox.put("java.lang.Double", "double"); autounbox.put("java.lang.Number", "primitive number"); AUTOUNBOX= Collections.unmodifiableMap(autounbox); 443 } 444 445 private static final boolean isPrimitiveAssignable(String lhs, String rhs) { 446 Set targets= (Set ) PRIMITIVE_ASSIGNMENTS.get(lhs); 447 return targets != null && targets.contains(rhs); 448 } 449 450 private static final String getAutoUnboxedType(String type) { 451 String primitive= (String ) AUTOUNBOX.get(type); 452 return primitive; 453 } 454 455 456 private final ICompilationUnit fCompilationUnit; 457 458 private final int fCodeAssistOffset; 459 460 private List fVariables; 461 private ImageDescriptorRegistry fRegistry= JavaPlugin.getImageDescriptorRegistry(); 462 private boolean fAllowAutoBoxing; 463 464 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 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 489 public int getCodeAssistOffset() { 490 return fCodeAssistOffset; 491 } 492 493 496 public ICompilationUnit getCompilationUnit() { 497 return fCompilationUnit; 498 } 499 500 511 public ICompletionProposal[] parameterProposals(String paramPackage, String paramType, String 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 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 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 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 { 548 549 private String fParamName; 550 551 MatchComparator(String paramName) { 552 fParamName= paramName; 553 } 554 public int compare(Object o1, Object o2) { 555 Variable one= (Variable)o1; 556 Variable two= (Variable)o2; 557 558 return score(two) - score(one); 559 } 560 561 567 private int score(Variable v) { 568 int variableScore= 100 - v.variableType; int subStringScore= getLongestCommonSubstring(v.name, fParamName).length(); 570 int shorter= Math.min(v.name.length(), fParamName.length()); 574 if (subStringScore < 0.6 * shorter) 575 subStringScore= 0; 576 577 int positionScore= v.positionScore; 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 603 private static void orderMatches(List typeMatches, String paramName) { 604 if (typeMatches != null) Collections.sort(typeMatches, new MatchComparator(paramName)); 605 } 606 607 610 private List findProposalsMatchingType(List proposals, Variable parameter) throws JavaModelException { 611 612 if (parameter.getFQN().length() == 0) 613 return null; 614 615 619 List matches= new ArrayList (); 620 621 for (ListIterator 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 634 private static String getLongestCommonSubstring(String first, String second) { 635 636 String shorter= (first.length() <= second.length()) ? first : second; 637 String longer= shorter == first ? second : first; 638 639 int minLength= shorter.length(); 640 641 StringBuffer pattern= new StringBuffer (shorter.length() + 2); 642 String longestCommonSubstring= ""; 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 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 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 |