### Eclipse Workspace Patch 1.0 #P org.codehaus.groovy.eclipse.quickfix Index: src/org/codehaus/groovy/eclipse/quickfix/processors/GroovyQuickFixProcessor.java =================================================================== --- src/org/codehaus/groovy/eclipse/quickfix/processors/GroovyQuickFixProcessor.java (revision 21412) +++ src/org/codehaus/groovy/eclipse/quickfix/processors/GroovyQuickFixProcessor.java (working copy) @@ -1,5 +1,5 @@ /* - * Copyright 2010 the original author or authors. + * Copyright 2010-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; +import org.codehaus.groovy.eclipse.quickfix.proposals.AbstractGroovyQuickFixProposal; import org.codehaus.groovy.eclipse.quickfix.proposals.GroovyProblemFactory; import org.codehaus.groovy.eclipse.quickfix.proposals.GroovyQuickFixResolverRegistry; import org.codehaus.groovy.eclipse.quickfix.proposals.IProblemDescriptor; @@ -66,8 +67,8 @@ // as the proposals should not appear if the problem is in any other // type of project if (isProblemInGroovyProject(context, locations)) { - IQuickFixProblemContext problemContext = getQuickFixProblemContext(context, - locations); + IQuickFixProblemContext problemContext = getQuickFixProblemContext( + context, locations); if (problemContext != null) { List resolvers = new GroovyQuickFixResolverRegistry( @@ -188,6 +189,8 @@ } final ICompletionProposal proposalToConvert = proposal; + final int relevance = proposalToConvert instanceof AbstractGroovyQuickFixProposal ? ((AbstractGroovyQuickFixProposal) proposalToConvert) + .getRelevance() : 0; return new IJavaCompletionProposal() { @@ -216,7 +219,7 @@ } public int getRelevance() { - return 0; + return relevance; } }; } Index: src/org/codehaus/groovy/eclipse/quickfix/proposals/AbstractQuickFixResolver.java =================================================================== --- src/org/codehaus/groovy/eclipse/quickfix/proposals/AbstractQuickFixResolver.java (revision 21412) +++ src/org/codehaus/groovy/eclipse/quickfix/proposals/AbstractQuickFixResolver.java (working copy) @@ -1,5 +1,5 @@ /* - * Copyright 2010 the original author or authors. + * Copyright 2010-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,10 @@ import java.util.ArrayList; import java.util.List; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; + /** * Base Groovy quick fix resolver that makes it more convenient for concrete * resolvers to define which problem types this resolver can handle. @@ -74,10 +78,25 @@ return problemTypes; } + protected IType[] getContextTypes() { + IQuickFixProblemContext context = getQuickFixProblem(); + if (context != null) { + ICompilationUnit unit = context.getCompilationUnit(); + if (unit != null) { + try { + return unit.getAllTypes(); + } catch (JavaModelException e) { + // do nothing + } + } + } + return null; + } + /** * - * @return non null, non empty list of problem types that this resolver - * can handle + * @return non null, non empty list of problem types that this resolver can + * handle */ protected abstract IProblemType[] getTypes(); Index: src/org/codehaus/groovy/eclipse/quickfix/proposals/AddMissingGroovyImportsResolver.java =================================================================== --- src/org/codehaus/groovy/eclipse/quickfix/proposals/AddMissingGroovyImportsResolver.java (revision 21412) +++ src/org/codehaus/groovy/eclipse/quickfix/proposals/AddMissingGroovyImportsResolver.java (working copy) @@ -1,5 +1,5 @@ /* - * Copyright 2010 the original author or authors. + * Copyright 2010-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,8 @@ import java.util.List; import java.util.Map; +import org.codehaus.groovy.eclipse.codeassist.proposals.RelevanceRules; +import org.codehaus.groovy.eclipse.codeassist.proposals.RelevanceRules.RelevanceRuleType; import org.codehaus.groovy.eclipse.quickfix.GroovyQuickFixPlugin; import org.codehaus.groovy.eclipse.refactoring.actions.OrganizeGroovyImports; import org.codehaus.groovy.eclipse.refactoring.actions.OrganizeGroovyImports.UnresolvedTypeData; @@ -59,11 +61,11 @@ private GroovyCompilationUnit unit; public AddMissingImportProposal(IType resolvedSuggestedType, - GroovyCompilationUnit unit, IQuickFixProblemContext problem) { - super(problem); + GroovyCompilationUnit unit, IQuickFixProblemContext problem, + int relevance) { + super(problem, relevance); this.resolvedSuggestedType = resolvedSuggestedType; this.unit = unit; - } public IType getSuggestedJavaType() { @@ -111,8 +113,9 @@ // For inner types, display the fully qualified top-level type as // the declaration for the suggested type String declaration = declaringType != null ? declaringType - .getFullyQualifiedName().replace('$', '.') : getSuggestedJavaType() - .getPackageFragment().getElementName(); + .getFullyQualifiedName().replace('$', '.') + : getSuggestedJavaType().getPackageFragment() + .getElementName(); return "Import '" + getSuggestedJavaType().getElementName() + "' (" + declaration + ")"; } @@ -248,8 +251,10 @@ if (suggestions != null) { List fixes = new ArrayList(); for (IType type : suggestions) { + int revelance = getRelevance(type); fixes.add(new AddMissingImportProposal(type, - getGroovyCompilationUnit(), getQuickFixProblem())); + getGroovyCompilationUnit(), getQuickFixProblem(), + revelance)); } return fixes; } @@ -267,4 +272,18 @@ .getCompilationUnit(); } + protected int getRelevance(IType type) { + if (type == null) { + return 0; + } + RelevanceRuleType[] rules = new RelevanceRuleType[] { + RelevanceRuleType.LIBRARY_TYPE, RelevanceRuleType.SOURCE_TYPE, + // FIXNS: Enable only after it has been tested + // RelevanceRuleType.ACCESSIBILITY, + RelevanceRuleType.MODIFIERS, RelevanceRuleType.SIMILAR_PACKAGE }; + + return new RelevanceRules(rules).getRelevance(type, getContextTypes()); + + } + } Index: src/org/codehaus/groovy/eclipse/quickfix/proposals/AbstractGroovyQuickFixProposal.java =================================================================== --- src/org/codehaus/groovy/eclipse/quickfix/proposals/AbstractGroovyQuickFixProposal.java (revision 21412) +++ src/org/codehaus/groovy/eclipse/quickfix/proposals/AbstractGroovyQuickFixProposal.java (working copy) @@ -1,5 +1,5 @@ /* - * Copyright 2010 the original author or authors. + * Copyright 2010-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,10 +33,21 @@ ICompletionProposal { private IQuickFixProblemContext problemContext; + private int relevance; public AbstractGroovyQuickFixProposal(IQuickFixProblemContext problem) { this.problemContext = problem; } + + public AbstractGroovyQuickFixProposal(IQuickFixProblemContext problem, int relevance) { + this.problemContext = problem; + this.relevance = relevance; + } + + + public int getRelevance() { + return relevance; + } /** * #P org.codehaus.groovy.eclipse.codeassist.completion Index: src/org/codehaus/groovy/eclipse/codeassist/proposals/AbstractRule.java =================================================================== --- src/org/codehaus/groovy/eclipse/codeassist/proposals/AbstractRule.java (revision 0) +++ src/org/codehaus/groovy/eclipse/codeassist/proposals/AbstractRule.java (revision 0) @@ -0,0 +1,163 @@ +/* + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.codehaus.groovy.eclipse.codeassist.proposals; + +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IType; + +/** + * + * Abstract class containing helper methods to obtain information like enclosing + * package or project for the context types, as well as uses an optional + * relevance category to further prioritise rules. A relevance category is + * applied to the relevance value of this rule. Categories may be useful to + * avoid rule clashes, in case one rule has a higher priority than another one. + * + * @author Nieraj Singh + * @created 2011-02-18 + */ +public abstract class AbstractRule implements IRelevanceRule { + + /** + * Additional granularity for Types only. Allows rules to have different + * priorities, as some rules may carry more weight than others. + * + */ + public enum TypeRelevanceCategory { + + LOWEST_TYPE(1), LOW_TYPE(4), MEDIUM_TYPE(8), MEDIUM_HIGH_TYPE(12), HIGH_TYPE( + 16); + + private double multiplier; + + private TypeRelevanceCategory(double multiplier) { + this.multiplier = multiplier; + } + + public double getMultiplier() { + return multiplier; + } + + public int applyCategory(int value) { + return (int) getMultiplier() * value; + } + + } + + /** + * Returns true if an only if the relevance type is contained in the same + * project as ALL other context types. If the context types includes types + * from different folders, or the relevance type is in a different project + * than the context types, false is returned. + * + * @param relevanceType + * type whose relevance needs to be determined + * @param contextTypes + * context types where this rule is being invoked, like the + * compilation unit where the relevance type is being imported + * @return true if and only if the relevance type is contained in the same + * project as ALL other context types + */ + public boolean areTypesInSameProject(IType relevanceType, + IType[] contextTypes) { + if (relevanceType == null || contextTypes == null + || contextTypes.length == 0) { + return false; + } + + IJavaProject relevanceProject = relevanceType.getJavaProject(); + + if (relevanceProject == null) { + return false; + } + for (IType cType : contextTypes) { + + if (!relevanceProject.equals(cType.getJavaProject())) { + return false; + } + } + return true; + + } + + /** + * Return true if and only if the relevance type and ALL the context types + * are in the same compilation unit. If at least one of the context types is + * in a different compilation unit, or the compilation unit of either cannot + * be resolved, false is returned. + * + * @param relevanceType + * @param contextTypes + * @return true if and only if the relevance type and all context types are + * in the same compilation unit. False otherwise + */ + public boolean areTypesInSameCompilationUnit(IType relevanceType, + IType[] contextTypes) { + + if (relevanceType == null || contextTypes == null + || contextTypes.length == 0) { + return false; + } + + ICompilationUnit relevanceCompilationUnit = relevanceType + .getCompilationUnit(); + if (relevanceCompilationUnit != null) { + for (IType contextType : contextTypes) { + ICompilationUnit contextCu = contextType.getCompilationUnit(); + if (!relevanceCompilationUnit.equals(contextCu)) { + return false; + } + } + return true; + } + return false; + } + + /** + * This returns the package fragment containing ALL the context types. If + * the list of context types includes types from different packages, null is + * returned. + * + * @param contextTypes + * . Should all be part of the same compilation unit. therefore + * contained in the same package + * @return package fragment containing ALL the context types, or null if a + * single package fragment cannot be resolved from the context types + */ + public IPackageFragment getContextPackageFragment(IType[] contextTypes) { + if (contextTypes == null) { + return null; + } + IPackageFragment frag = null; + for (IType type : contextTypes) { + IPackageFragment fragToCheck = type.getPackageFragment(); + if (frag != null && !frag.equals(fragToCheck)) { + return null; + } + frag = fragToCheck; + + } + return frag; + } + + public IType getFirstContextType(IType[] contextTypes) { + return contextTypes != null && contextTypes.length > 0 ? contextTypes[0] + : null; + } + +} \ No newline at end of file Index: src/org/codehaus/groovy/eclipse/codeassist/ProposalUtils.java =================================================================== --- src/org/codehaus/groovy/eclipse/codeassist/ProposalUtils.java (revision 21412) +++ src/org/codehaus/groovy/eclipse/codeassist/ProposalUtils.java (working copy) @@ -1,5 +1,5 @@ /* - * Copyright 2003-2009 the original author or authors. + * Copyright 2003-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,8 +24,17 @@ import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.MethodNode; import org.eclipse.jdt.core.CompletionProposal; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; +import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.groovy.search.VariableScope; +import org.eclipse.jdt.internal.compiler.env.AccessRestriction; +import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; +import org.eclipse.jdt.internal.core.ClasspathEntry; +import org.eclipse.jdt.internal.core.PackageFragmentRoot; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.viewsupport.ImageDescriptorRegistry; import org.eclipse.jdt.ui.text.java.CompletionProposalLabelProvider; @@ -66,6 +75,47 @@ } } + /** + * Can be null if access restriction cannot be resolved for given type + * + * @param type + * @param project + * @return + */ + public static AccessRestriction getTypeAccessibility(IType type) { + + PackageFragmentRoot root = (PackageFragmentRoot) type + .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); + + try { + IClasspathEntry entry = root.getResolvedClasspathEntry(); + // Alternative: + // entry = ((JavaProject) typeProject).getClasspathEntryFor(root + // .getPath()); + if (entry instanceof ClasspathEntry) { + AccessRuleSet accessRuleSet = ((ClasspathEntry) entry) + .getAccessRuleSet(); + if (accessRuleSet != null) { + char[] packageName = type.getPackageFragment() + .getElementName().toCharArray(); + char[][] packageChars = CharOperation.splitOn('.', + packageName); + char[] fileWithoutExtension = type.getElementName() + .toCharArray(); + + return accessRuleSet + .getViolatedRestriction(CharOperation.concatWith( + packageChars, fileWithoutExtension, '/')); + + } + } + } catch (JavaModelException e) { + // nothing + } + + return null; + } + public static char[] createMethodSignature(MethodNode node) { return createMethodSignatureStr(node, 0).toCharArray(); } Index: src/org/codehaus/groovy/eclipse/codeassist/proposals/RelevanceRules.java =================================================================== --- src/org/codehaus/groovy/eclipse/codeassist/proposals/RelevanceRules.java (revision 0) +++ src/org/codehaus/groovy/eclipse/codeassist/proposals/RelevanceRules.java (revision 0) @@ -0,0 +1,365 @@ +/* + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.codehaus.groovy.eclipse.codeassist.proposals; + +import org.codehaus.groovy.eclipse.codeassist.ProposalUtils; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IAccessRule; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.compiler.IProblem; +import org.eclipse.jdt.internal.compiler.env.AccessRestriction; +import org.eclipse.jdt.internal.core.SourceType; + +/** + * Computes the relevance of a type based on a set of relevance rules. The + * caller has the option of specifying a subset of available rules to use, or if + * none is provided, by default all the rules will be used.
+ * New rules can be added and require a corresponding type definition
+ * + * + * @author Nieraj Singh + * @created 2011-02-17 + */ +public class RelevanceRules implements IRelevanceRule { + + public static int DEFAULT_STARTING_RELEVANCE_VALUE = 1; + + public static enum RelevanceRuleType { + SOURCE_TYPE, ACCESSIBILITY, MODIFIERS, LIBRARY_TYPE, SIMILAR_PACKAGE + } + + private RelevanceRuleType[] ruleTypes; + + public static IRelevanceRule getRule(RelevanceRuleType type) { + + if (type == null) { + return null; + } + + IRelevanceRule rule = null; + switch (type) { + case SOURCE_TYPE: + rule = new SourceRule(); + break; + // FIXNS: Enable only after it has been tested + // case ACCESSIBILITY: + // rule = new AccessibilityRule(); + // break; + case MODIFIERS: + rule = new ModifierRule(); + break; + case LIBRARY_TYPE: + rule = new LibraryTypeRule(); + break; + case SIMILAR_PACKAGE: + rule = new SimilarPackagesRule(); + break; + } + return rule; + } + + /** + * Argument is a list of relevance rule types that should be used when + * computing the relevance of a type. If null or empty is passed, all the + * available rule types will be used. This constructor allows a caller to + * use only a subset of rules. + */ + public RelevanceRules(RelevanceRuleType[] ruleTypes) { + this.ruleTypes = ruleTypes; + } + + /** + * Computes the integer relevance value of a given type based on registered + * relevance rule types + */ + public int getRelevance(IType type, IType[] contextTypes) { + if (type == null) { + return 0; + } + + // use all the rule types if none were specified by the invoker. + RelevanceRuleType[] rTypes = ruleTypes == null || ruleTypes.length == 0 ? RelevanceRuleType + .values() : ruleTypes; + + int relevance = getStartingRelevanceValue(); + for (RelevanceRuleType ruleType : rTypes) { + IRelevanceRule rule = getRule(ruleType); + if (rule != null) { + relevance += rule.getRelevance(type, contextTypes); + } + } + // User lowest Relevance category as Types have lowest relevance + // category + return Relevance.LOWEST.getRelavance(relevance); + } + + protected int getStartingRelevanceValue() { + return DEFAULT_STARTING_RELEVANCE_VALUE; + } + + /* + * RULE DEFINITIONS + */ + + /** + * Simple rule that places higher priority on Source types. Binary types + * have lower priority. + */ + public static class SourceRule extends AbstractRule { + + public int getRelevance(IType relevanceType, IType[] contextTypes) { + // Source have higher relevance than Binary + return relevanceType instanceof SourceType ? TypeRelevanceCategory.MEDIUM_TYPE + .applyCategory(1) : TypeRelevanceCategory.LOW_TYPE + .applyCategory(1); + } + + } + + /** + * FIXNS: Not fully tested. Not recommended for use. + * + * Accessible types have higher priority than restricted types. + * + */ + public static class AccessibilityRule extends AbstractRule { + + public int getRelevance(IType relevanceType, IType[] contextTypes) { + if (relevanceType == null) { + return 0; + } + + // If in the same project, all types are accessible + if (areTypesInSameProject(relevanceType, contextTypes)) { + return 0; + } + + // determine associated access restriction + AccessRestriction accessRestriction = ProposalUtils + .getTypeAccessibility(relevanceType); + + // If no access restriction found, assume accessible? + int accessibility = IAccessRule.K_ACCESSIBLE; + + if (accessRestriction != null) { + switch (accessRestriction.getProblemId()) { + case IProblem.ForbiddenReference: + accessibility = IAccessRule.K_NON_ACCESSIBLE; + break; + case IProblem.DiscouragedReference: + // discouraged references have a lower priority + accessibility = IAccessRule.K_DISCOURAGED; + break; + } + } + return accessibility == IAccessRule.K_ACCESSIBLE ? TypeRelevanceCategory.MEDIUM_TYPE + .applyCategory(1) : 0; + } + + } + + /** + * Types in the same project as the context types (the context compilation + * unit where a type is being imported or referenced) have higher priority + * that types in other projects. Furthermore, private types have the highest + * priority, followed by package private, followed by public + */ + public static class ModifierRule extends AbstractRule { + + protected TypeRelevanceCategory getTypeCategory(IType relevanceType, + IType[] contextTypes) { + + TypeRelevanceCategory category = null; + if (areTypesInSameCompilationUnit(relevanceType, contextTypes)) { + category = TypeRelevanceCategory.HIGH_TYPE; + } else if (areTypesInSameProject(relevanceType, contextTypes)) { + category = TypeRelevanceCategory.MEDIUM_HIGH_TYPE; + } else { + category = TypeRelevanceCategory.LOWEST_TYPE; + } + return category; + + } + + public int getRelevance(IType relevanceType, IType[] contextTypes) { + + int relevance = 0; + TypeRelevanceCategory category = null; + try { + int modifiers = relevanceType.getFlags(); + category = getTypeCategory(relevanceType, contextTypes); + + relevance += (modifiers & Flags.AccDefault) != 0 ? 0 : 1; + relevance += (modifiers & Flags.AccPrivate) != 0 ? 0 : 1; + } catch (JavaModelException e) { + // Do nothing + } + return category != null ? category.applyCategory(relevance) + : relevance; + } + + } + + /** + * + * Types from certain libraries have higher priority. In particular, types + * from java, groovy, groovyx, and javax have higher priority than types + * from other packages. The order is shown in the following example
+ * Example:
  • + * java.lang.SomeType
  • + * groovy.lang.SomeType
  • + * groovyx.lang.SomeType
  • + * javax.lang.SomeType
  • + * com.lang.SomeType
  • + * + */ + public static class LibraryTypeRule extends AbstractRule { + + enum LibraryType { + JAVA("java"), JAVAX("javax"), GROOVY("groovy"), GROOVYX("groovyx"); + + private LibraryType(String value) { + this.value = value; + } + + private String value; + + public String getValue() { + return value; + } + + } + + /** + * The library type is the first segment in the package name. + * + * @param relevanceType + * @return first segment in the package name containing the type + */ + protected LibraryType getLibraryType(IType relevanceType) { + if (relevanceType == null) { + return null; + } + String[] segments = relevanceType.getPackageFragment() + .getElementName().split("\\."); + if (segments != null && segments.length > 0) { + String firstPackSegment = segments[0]; + for (LibraryType type : LibraryType.values()) { + if (type.getValue().equals(firstPackSegment)) { + return type; + } + } + } + return null; + + } + + public int getRelevance(IType relevanceType, IType[] contextTypes) { + // Default is zero, meaning relevance for types in any other library + // is governed by other rules. Only types in the following libraries + // get higher priority + int relevance = 0; + LibraryType packType = getLibraryType(relevanceType); + if (packType != null) { + switch (packType) { + case JAVA: + relevance += 4; + break; + case GROOVY: + relevance += 3; + break; + case GROOVYX: + relevance += 2; + break; + case JAVAX: + relevance += 1; + break; + } + } + + return TypeRelevanceCategory.LOW_TYPE.applyCategory(relevance); + } + + } + + /** + * + * Types in packages with common segments have higher priority. As this rule + * may clash with LibraryTypeRule, a higher relevance category is assigned + * to this rule. This example shows the effect of the higher category.
    + * With just LibraryTypeRule, the following order would be expected:
    + *
  • + * java.lang.SomeType
  • + * groovy.lang.SomeType
  • + * groovyx.lang.SomeType
  • + * javax.lang.SomeType
  • + * com.lang.SomeType
  • + *

    + * However, if both LibraryTypeRule and SimilarPackagesRule are enabled, and + * if SomeType is being imported into a compilation unit in + * com.lang.AnotherType, the order would be: + *

    + *
  • + * com.lang.SomeType
  • + * java.lang.SomeType
  • + * groovy.lang.SomeType
  • + * groovyx.lang.SomeType
  • + * javax.lang.SomeType
  • + * + */ + public static class SimilarPackagesRule extends AbstractRule { + + protected String convertToDot(String name) { + return name != null ? name.replace('$', '.') : name; + } + + public int getRelevance(IType relevanceType, IType[] contextTypes) { + int relevance = 0; + + IPackageFragment contextFragment = getContextPackageFragment(contextTypes); + + if (contextFragment != null && relevanceType != null) { + IPackageFragment relevanceFragment = relevanceType + .getPackageFragment(); + String relQualified = convertToDot(relevanceFragment + .getElementName()); + String contextQualified = convertToDot(contextFragment + .getElementName()); + + String[] relSegments = relQualified.split("\\."); + String[] contextSegments = contextQualified.split("\\."); + + for (int i = 0; i < relSegments.length + && i < contextSegments.length; i++) { + if (relSegments[i].equals(contextSegments[i])) { + relevance++; + } else { + // Stop relevance counting once different segments are + // encountered + break; + } + } + + } + + return TypeRelevanceCategory.HIGH_TYPE.applyCategory(relevance); + } + + } + +} Index: src/org/codehaus/groovy/eclipse/codeassist/proposals/IRelevanceRule.java =================================================================== --- src/org/codehaus/groovy/eclipse/codeassist/proposals/IRelevanceRule.java (revision 0) +++ src/org/codehaus/groovy/eclipse/codeassist/proposals/IRelevanceRule.java (revision 0) @@ -0,0 +1,46 @@ +/* + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.codehaus.groovy.eclipse.codeassist.proposals; + +import org.eclipse.jdt.core.IType; + +/** + * Computes the relevance of a type. The higher the value the higher the + * relevance. If no relevance can be determined, zero MUST be returned. + * + * @author Nieraj Singh + * @created 2011-02-17 + */ +public interface IRelevanceRule { + + /** + * Relevance value of a type. Higher relevance should be indicated by a + * higher integer value. If no relevance can be computed, a value of zero + * MUST be returned. + * + * @param relevanceType + * whose relevance must be computed. Must not be null + * @param contextTypes + * context types where a relevance calculation is requested, for + * example the top level type where the relevance type needs to + * be resolved. Context types should all be types in the same + * compilation unit. + * @return positive value, with a higher value indicating higher relevance, + * or zero if relevance cannot be computed + */ + public int getRelevance(IType relevanceType, IType[] contextTypes); + +}