diff options
Diffstat (limited to 'src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java')
-rw-r--r-- | src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java | 141 |
1 files changed, 138 insertions, 3 deletions
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java index bd6e328c..701a2029 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java @@ -21,21 +21,41 @@ */ package lombok.eclipse.agent; +import static lombok.eclipse.agent.PatchExtensionMethod.*; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import lombok.eclipse.EclipseNode; import lombok.eclipse.agent.PatchExtensionMethod.Extension; -import lombok.eclipse.agent.PatchExtensionMethod.Reflection; +import lombok.experimental.ExtensionMethod; import org.eclipse.jdt.core.CompletionProposal; import org.eclipse.jdt.internal.codeassist.InternalCompletionContext; import org.eclipse.jdt.internal.codeassist.InternalCompletionProposal; import org.eclipse.jdt.internal.codeassist.InternalExtendedCompletionContext; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMemberAccess; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedNameReference; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleNameReference; import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.FieldReference; +import org.eclipse.jdt.internal.compiler.ast.NameReference; +import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.lookup.Binding; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.Scope; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; import org.eclipse.jdt.internal.core.SearchableEnvironment; +import org.eclipse.jdt.internal.ui.text.java.AbstractJavaCompletionProposal; import org.eclipse.jdt.ui.text.java.CompletionProposalCollector; import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal; @@ -49,11 +69,11 @@ public class PatchExtensionMethodCompletionProposal { if (canExtendCodeAssist(proposals)) { IJavaCompletionProposal firstProposal = proposals.get(0); int replacementOffset = getReplacementOffset(firstProposal); - for (Extension extension : PatchExtensionMethod.getExtensionMethods(completionProposalCollector)) { + for (Extension extension : getExtensionMethods(completionProposalCollector)) { for (MethodBinding method : extension.extensionMethods) { ExtensionMethodCompletionProposal newProposal = new ExtensionMethodCompletionProposal(replacementOffset); copyNameLookupAndCompletionEngine(completionProposalCollector, firstProposal, newProposal); - ASTNode node = PatchExtensionMethod.getAssistNode(completionProposalCollector); + ASTNode node = getAssistNode(completionProposalCollector); newProposal.setMethodBinding(method, node); createAndAddJavaCompletionProposal(completionProposalCollector, newProposal, proposals); } @@ -62,6 +82,70 @@ public class PatchExtensionMethodCompletionProposal { return proposals.toArray(new IJavaCompletionProposal[proposals.size()]); } + + private static List<Extension> getExtensionMethods(CompletionProposalCollector completionProposalCollector) { + List<Extension> extensions = new ArrayList<Extension>(); + ClassScope classScope = getClassScope(completionProposalCollector); + if (classScope != null) { + TypeDeclaration decl = classScope.referenceContext; + TypeBinding firstParameterType = getFirstParameterType(decl, completionProposalCollector); + for (EclipseNode typeNode = getTypeNode(decl); typeNode != null; typeNode = upToType(typeNode)) { + Annotation ann = getAnnotation(ExtensionMethod.class, typeNode); + extensions.addAll(0, getApplicableExtensionMethods(typeNode, ann, firstParameterType)); + } + } + return extensions; + } + + static TypeBinding getFirstParameterType(TypeDeclaration decl, CompletionProposalCollector completionProposalCollector) { + TypeBinding firstParameterType = null; + ASTNode node = getAssistNode(completionProposalCollector); + if (node == null) return null; + if (!(node instanceof CompletionOnQualifiedNameReference) && !(node instanceof CompletionOnSingleNameReference) && !(node instanceof CompletionOnMemberAccess)) return null; + + if (node instanceof NameReference) { + Binding binding = ((NameReference) node).binding; + if ((node instanceof SingleNameReference) && (((SingleNameReference) node).token.length == 0)) { + firstParameterType = decl.binding; + } else if (binding instanceof VariableBinding) { + firstParameterType = ((VariableBinding) binding).type; + } else if (binding instanceof TypeBinding) { + firstParameterType = (TypeBinding) binding; + } + } else if (node instanceof FieldReference) { + firstParameterType = ((FieldReference) node).actualReceiverType; + } + return firstParameterType; + } + + private static ASTNode getAssistNode(CompletionProposalCollector completionProposalCollector) { + try { + InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector); + InternalExtendedCompletionContext extendedContext = (InternalExtendedCompletionContext) Reflection.extendedContextField.get(context); + if (extendedContext == null) return null; + return (ASTNode) Reflection.assistNodeField.get(extendedContext); + } catch (Exception ignore) { + return null; + } + } + + private static ClassScope getClassScope(CompletionProposalCollector completionProposalCollector) { + ClassScope scope = null; + try { + InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector); + InternalExtendedCompletionContext extendedContext = (InternalExtendedCompletionContext) Reflection.extendedContextField.get(context); + if (extendedContext != null) { + Scope assistScope = ((Scope) Reflection.assistScopeField.get(extendedContext)); + if (assistScope != null) { + scope = assistScope.classScope(); + } + } + } catch (IllegalAccessException ignore) { + // ignore + } + return scope; + } + private static void copyNameLookupAndCompletionEngine(CompletionProposalCollector completionProposalCollector, IJavaCompletionProposal proposal, InternalCompletionProposal newProposal) { @@ -97,4 +181,55 @@ public class PatchExtensionMethodCompletionProposal { return 0; } } + + static class Reflection { + public static final Field replacementOffsetField; + public static final Field contextField; + public static final Field extendedContextField; + public static final Field assistNodeField; + public static final Field assistScopeField; + public static final Field lookupEnvironmentField; + public static final Field completionEngineField; + public static final Field nameLookupField; + public static final Method createJavaCompletionProposalMethod; + + static { + replacementOffsetField = accessField(AbstractJavaCompletionProposal.class, "fReplacementOffset"); + contextField = accessField(CompletionProposalCollector.class, "fContext"); + extendedContextField = accessField(InternalCompletionContext.class, "extendedContext"); + assistNodeField = accessField(InternalExtendedCompletionContext.class, "assistNode"); + assistScopeField = accessField(InternalExtendedCompletionContext.class, "assistScope"); + lookupEnvironmentField = accessField(InternalExtendedCompletionContext.class, "lookupEnvironment"); + completionEngineField = accessField(InternalCompletionProposal.class, "completionEngine"); + nameLookupField = accessField(InternalCompletionProposal.class, "nameLookup"); + createJavaCompletionProposalMethod = accessMethod(CompletionProposalCollector.class, "createJavaCompletionProposal", CompletionProposal.class); + } + + static boolean isComplete() { + Object[] requiredFieldsAndMethods = { replacementOffsetField, contextField, extendedContextField, assistNodeField, assistScopeField, lookupEnvironmentField, completionEngineField, nameLookupField, createJavaCompletionProposalMethod }; + for (Object o : requiredFieldsAndMethods) if (o == null) return false; + return true; + } + + private static Field accessField(Class<?> clazz, String fieldName) { + try { + return makeAccessible(clazz.getDeclaredField(fieldName)); + } catch (Exception e) { + return null; + } + } + + private static Method accessMethod(Class<?> clazz, String methodName, Class<?> parameter) { + try { + return makeAccessible(clazz.getDeclaredMethod(methodName, parameter)); + } catch (Exception e) { + return null; + } + } + + private static <T extends AccessibleObject> T makeAccessible(T object) { + object.setAccessible(true); + return object; + } + } } |