aboutsummaryrefslogtreecommitdiff
path: root/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java')
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java141
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;
+ }
+ }
}