mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-13 20:15:09 +00:00
Port of lombok-pg's @ExtensionMethod support for eclipse. Doesn't work yet.
This commit is contained in:
1 parent
a6a8e4c555
commit
7496a8f170
7 files changed
+876
-1
No files matched your search
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Project Lombok Authors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package lombok.eclipse.handlers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.internal.compiler.ast.Annotation;
|
||||
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
|
||||
|
||||
import lombok.core.AnnotationValues;
|
||||
import lombok.eclipse.EclipseAnnotationHandler;
|
||||
import lombok.eclipse.EclipseNode;
|
||||
import lombok.experimental.ExtensionMethod;
|
||||
|
||||
// This handler just does some additional error checking; the real work is done in the agent.
|
||||
public class HandleExtensionMethod extends EclipseAnnotationHandler<ExtensionMethod> {
|
||||
@Override public void handle(AnnotationValues<ExtensionMethod> annotation, Annotation ast, EclipseNode annotationNode) {
|
||||
TypeDeclaration typeDecl = null;
|
||||
EclipseNode owner = annotationNode.up();
|
||||
if (owner.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) owner.get();
|
||||
int modifiers = typeDecl == null ? 0 : typeDecl.modifiers;
|
||||
|
||||
boolean notAClass = (modifiers &
|
||||
(ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0;
|
||||
|
||||
if (typeDecl == null || notAClass) {
|
||||
annotationNode.addError("@ExtensionMethod is legal only on classes and enums.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<Object> listenerInterfaces = annotation.getActualExpressions("value");
|
||||
if (listenerInterfaces.isEmpty()) {
|
||||
annotationNode.addWarning(String.format("@ExtensionMethod has no effect since no extension types were specified."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,5 +60,9 @@ public @interface ExtensionMethod {
|
||||
/** All types whose static methods will be exposed as extension methods. */
|
||||
Class<?>[] value();
|
||||
|
||||
/**
|
||||
* If {@code true}, an applicable extension method is used (if found) even if the method call already was compilable (this is the default).
|
||||
* If {@code false}, an extension method is only used if the method call is not also defined by the type itself.
|
||||
*/
|
||||
boolean suppressBaseMethods() default true;
|
||||
}
|
||||
@@ -21,6 +21,8 @@
|
||||
*/
|
||||
package lombok.eclipse.agent;
|
||||
|
||||
import static lombok.patcher.scripts.ScriptBuilder.*;
|
||||
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -101,6 +103,7 @@ public class EclipsePatcher extends Agent {
|
||||
patchAvoidReparsingGeneratedCode(sm);
|
||||
patchLombokizeAST(sm);
|
||||
patchEcjTransformers(sm, ecjOnly);
|
||||
patchExtensionMethod(sm, ecjOnly);
|
||||
|
||||
if (reloadExistingClasses) sm.reloadClasses(instrumentation);
|
||||
}
|
||||
@@ -546,7 +549,7 @@ public class EclipsePatcher extends Agent {
|
||||
sm.addScript(ScriptBuilder.exitEarly()
|
||||
.target(new MethodTarget(CLASSSCOPE_SIG, "buildFieldsAndMethods", "void"))
|
||||
.request(StackRequest.THIS)
|
||||
.decisionMethod(new Hook(PatchDelegate.class.getName(), "handleDelegateForType", "boolean", CLASSSCOPE_SIG))
|
||||
.decisionMethod(new Hook("lombok.eclipse.agent.PatchDelegatePortal", "handleDelegateForType", "boolean", "java.lang.Object"))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -641,4 +644,45 @@ public class EclipsePatcher extends Agent {
|
||||
.wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "convertAnnotations", ANNOTATION_SIG + "[]", ANNOTATION_SIG + "[]", I_ANNOTATABLE_SIG))
|
||||
.request(StackRequest.PARAM1, StackRequest.RETURN_VALUE).build());
|
||||
}
|
||||
|
||||
private static void patchExtensionMethod(ScriptManager sm, boolean ecj) {
|
||||
final String PATCH_EXTENSIONMETHOD_PORTAL = "lombok.eclipse.agent.PatchExtensionMethodPortal";
|
||||
final String MESSAGE_SEND_SIG = "org.eclipse.jdt.internal.compiler.ast.MessageSend";
|
||||
final String TYPE_BINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding";
|
||||
final String BLOCK_SCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.BlockScope";
|
||||
final String TYPE_BINDINGS_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding[]";
|
||||
final String PROBLEM_REPORTER_SIG = "org.eclipse.jdt.internal.compiler.problem.ProblemReporter";
|
||||
final String METHOD_BINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.MethodBinding";
|
||||
final String COMPLETION_PROPOSAL_COLLECTOR_SIG = "org.eclipse.jdt.ui.text.java.CompletionProposalCollector";
|
||||
final String I_JAVA_COMPLETION_PROPOSAL_SIG = "org.eclipse.jdt.ui.text.java.IJavaCompletionProposal[]";
|
||||
|
||||
sm.addScript(wrapReturnValue()
|
||||
.target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG))
|
||||
.request(StackRequest.RETURN_VALUE)
|
||||
.request(StackRequest.THIS)
|
||||
.request(StackRequest.PARAM1)
|
||||
.wrapMethod(new Hook(PATCH_EXTENSIONMETHOD_PORTAL, "resolveType", TYPE_BINDING_SIG, "java.lang.Object", "java.lang.Object", "java.lang.Object"))
|
||||
.build());
|
||||
|
||||
sm.addScript(replaceMethodCall()
|
||||
.target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG))
|
||||
.methodToReplace(new Hook(PROBLEM_REPORTER_SIG, "errorNoMethodFor", "void", MESSAGE_SEND_SIG, TYPE_BINDING_SIG, TYPE_BINDINGS_SIG))
|
||||
.replacementMethod(new Hook(PATCH_EXTENSIONMETHOD_PORTAL, "errorNoMethodFor", "void", "java.lang.Object", "java.lang.Object", "java.lang.Object", "java.lang.Object"))
|
||||
.build());
|
||||
sm.addScript(replaceMethodCall()
|
||||
.target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG))
|
||||
.methodToReplace(new Hook(PROBLEM_REPORTER_SIG, "invalidMethod", "void", MESSAGE_SEND_SIG, METHOD_BINDING_SIG))
|
||||
.replacementMethod(new Hook(PATCH_EXTENSIONMETHOD_PORTAL, "invalidMethod", "void", "java.lang.Object", "java.lang.Object", "java.lang.Object"))
|
||||
.build());
|
||||
|
||||
if (!ecj) {
|
||||
sm.addScript(wrapReturnValue()
|
||||
.target(new MethodTarget(COMPLETION_PROPOSAL_COLLECTOR_SIG, "getJavaCompletionProposals", I_JAVA_COMPLETION_PROPOSAL_SIG))
|
||||
.request(StackRequest.RETURN_VALUE)
|
||||
.request(StackRequest.THIS)
|
||||
// .wrapMethod(new Hook(PATCH_EXTENSIONMETHOD_PORTAL, "getJavaCompletionProposals", I_JAVA_COMPLETION_PROPOSAL_SIG, I_JAVA_COMPLETION_PROPOSAL_SIG, COMPLETION_PROPOSAL_COLLECTOR_SIG))
|
||||
.wrapMethod(new Hook(PATCH_EXTENSIONMETHOD_PORTAL, "getJavaCompletionProposals", "java.lang.Object", "java.lang.Object", "java.lang.Object"))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Project Lombok Authors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package lombok.eclipse.agent;
|
||||
|
||||
import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccStatic;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.jdt.core.CompletionProposal;
|
||||
import org.eclipse.jdt.core.compiler.CharOperation;
|
||||
import org.eclipse.jdt.internal.codeassist.CompletionEngine;
|
||||
import org.eclipse.jdt.internal.codeassist.InternalCompletionProposal;
|
||||
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.lookup.MethodBinding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
|
||||
|
||||
public class ExtensionMethodCompletionProposal extends InternalCompletionProposal {
|
||||
public ExtensionMethodCompletionProposal(final int replacementOffset) {
|
||||
super(CompletionProposal.METHOD_REF, replacementOffset - 1);
|
||||
}
|
||||
|
||||
public void setMethodBinding(final MethodBinding method, final ASTNode node) {
|
||||
MethodBinding original = method.original();
|
||||
TypeBinding[] parameters = Arrays.copyOf(method.parameters, method.parameters.length);
|
||||
method.parameters = Arrays.copyOfRange(method.parameters, 1, method.parameters.length);
|
||||
TypeBinding[] originalParameters = null;
|
||||
if (original != method) {
|
||||
originalParameters = Arrays.copyOf(method.original().parameters, method.original().parameters.length);
|
||||
method.original().parameters = Arrays.copyOfRange(method.original().parameters, 1, method.original().parameters.length);
|
||||
}
|
||||
|
||||
int length = method.parameters == null ? 0 : method.parameters.length;
|
||||
char[][] parameterPackageNames = new char[length][];
|
||||
char[][] parameterTypeNames = new char[length][];
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
TypeBinding type = method.original().parameters[i];
|
||||
parameterPackageNames[i] = type.qualifiedPackageName();
|
||||
parameterTypeNames[i] = type.qualifiedSourceName();
|
||||
}
|
||||
char[] completion = CharOperation.concat(method.selector, new char[] { '(', ')' });
|
||||
setDeclarationSignature(CompletionEngine.getSignature(method.declaringClass));
|
||||
setSignature(CompletionEngine.getSignature(method));
|
||||
|
||||
if (original != method) {
|
||||
setOriginalSignature(CompletionEngine.getSignature(original));
|
||||
}
|
||||
setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
|
||||
setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
|
||||
setParameterPackageNames(parameterPackageNames);
|
||||
setParameterTypeNames(parameterTypeNames);
|
||||
setPackageName(method.returnType.qualifiedPackageName());
|
||||
setTypeName(method.returnType.qualifiedSourceName());
|
||||
setName(method.selector);
|
||||
setCompletion(completion);
|
||||
setFlags(method.modifiers & (~AccStatic));
|
||||
int index = node.sourceEnd + 1;
|
||||
if (node instanceof CompletionOnQualifiedNameReference) {
|
||||
index -= ((CompletionOnQualifiedNameReference) node).completionIdentifier.length;
|
||||
}
|
||||
if (node instanceof CompletionOnMemberAccess) {
|
||||
index -= ((CompletionOnMemberAccess) node).token.length;
|
||||
}
|
||||
if (node instanceof CompletionOnSingleNameReference) {
|
||||
index -= ((CompletionOnSingleNameReference) node).token.length;
|
||||
}
|
||||
setReplaceRange(index, index);
|
||||
setTokenRange(index, index);
|
||||
|
||||
setRelevance(100);
|
||||
|
||||
method.parameters = parameters;
|
||||
if (original != method) {
|
||||
method.original().parameters = originalParameters;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Project Lombok Authors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package lombok.eclipse.agent;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import lombok.Lombok;
|
||||
|
||||
public class PatchDelegatePortal {
|
||||
static final String CLASS_SCOPE = "org.eclipse.jdt.internal.compiler.lookup.ClassScope";
|
||||
|
||||
public static boolean handleDelegateForType(Object classScope) {
|
||||
try {
|
||||
return (Boolean) Reflection.handleDelegateForType.invoke(null, classScope);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
return false;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (NullPointerException e) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Reflection {
|
||||
public static final Method handleDelegateForType;
|
||||
public static final Throwable problem;
|
||||
|
||||
static {
|
||||
Method m = null;
|
||||
Throwable problem_ = null;
|
||||
try {
|
||||
m = PatchDelegate.class.getMethod("handleDelegateForType", Class.forName(CLASS_SCOPE));
|
||||
} catch (Throwable t) {
|
||||
// That's problematic, but as long as no local classes are used we don't actually need it.
|
||||
// Better fail on local classes than crash altogether.
|
||||
problem_ = t;
|
||||
}
|
||||
handleDelegateForType = m;
|
||||
problem = problem_;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Project Lombok Authors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package lombok.eclipse.agent;
|
||||
|
||||
import static lombok.eclipse.handlers.EclipseHandlerUtil.createAnnotation;
|
||||
|
||||
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 java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import lombok.core.AST.Kind;
|
||||
import lombok.core.AnnotationValues;
|
||||
import lombok.core.AnnotationValues.AnnotationValueDecodeFail;
|
||||
import lombok.eclipse.EclipseAST;
|
||||
import lombok.eclipse.EclipseNode;
|
||||
import lombok.eclipse.TransformEclipseAST;
|
||||
import lombok.eclipse.handlers.EclipseHandlerUtil;
|
||||
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.ClassLiteralAccess;
|
||||
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.ast.Expression;
|
||||
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
|
||||
import org.eclipse.jdt.internal.compiler.ast.NameReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.Binding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.Scope;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
|
||||
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
|
||||
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;
|
||||
|
||||
public class PatchExtensionMethod {
|
||||
private static class Extension {
|
||||
List<MethodBinding> extensionMethods;
|
||||
boolean suppressBaseMethods;
|
||||
}
|
||||
|
||||
private static class PostponedNoMethodError implements PostponedError {
|
||||
private final ProblemReporter problemReporter;
|
||||
private final MessageSend messageSend;
|
||||
private final TypeBinding recType;
|
||||
private final TypeBinding[] params;
|
||||
|
||||
PostponedNoMethodError(ProblemReporter problemReporter, MessageSend messageSend, TypeBinding recType, TypeBinding[] params) {
|
||||
this.problemReporter = problemReporter;
|
||||
this.messageSend = messageSend;
|
||||
this.recType = recType;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public void fire() {
|
||||
problemReporter.errorNoMethodFor(messageSend, recType, params);
|
||||
}
|
||||
}
|
||||
|
||||
private static class PostponedInvalidMethodError implements PostponedError {
|
||||
private final ProblemReporter problemReporter;
|
||||
private final MessageSend messageSend;
|
||||
private final MethodBinding method;
|
||||
|
||||
PostponedInvalidMethodError(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) {
|
||||
this.problemReporter = problemReporter;
|
||||
this.messageSend = messageSend;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public void fire() {
|
||||
problemReporter.invalidMethod(messageSend, method);
|
||||
}
|
||||
}
|
||||
|
||||
private static interface PostponedError {
|
||||
public void fire();
|
||||
}
|
||||
|
||||
public static EclipseNode getTypeNode(TypeDeclaration decl) {
|
||||
CompilationUnitDeclaration cud = decl.scope.compilationUnitScope().referenceContext;
|
||||
EclipseAST astNode = TransformEclipseAST.getAST(cud, false);
|
||||
EclipseNode node = astNode.get(decl);
|
||||
if (node == null) {
|
||||
astNode = TransformEclipseAST.getAST(cud, true);
|
||||
node = astNode.get(decl);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
public static Annotation getAnnotation(Class<? extends java.lang.annotation.Annotation> expectedType, EclipseNode node) {
|
||||
TypeDeclaration decl = (TypeDeclaration) node.get();
|
||||
if (decl.annotations != null) for (Annotation ann : decl.annotations) {
|
||||
if (EclipseHandlerUtil.typeMatches(expectedType, node, ann.type)) return ann;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static EclipseNode upToType(EclipseNode typeNode) {
|
||||
EclipseNode node = typeNode;
|
||||
do {
|
||||
node = node.up();
|
||||
} while ((node != null) && (node.getKind() != Kind.TYPE));
|
||||
return node;
|
||||
}
|
||||
|
||||
private static List<Extension> getApplicableExtensionMethods(EclipseNode typeNode, Annotation ann, TypeBinding receiverType) {
|
||||
List<Extension> extensions = new ArrayList<Extension>();
|
||||
if ((typeNode != null) && (ann != null) && (receiverType != null)) {
|
||||
BlockScope blockScope = ((TypeDeclaration) typeNode.get()).initializerScope;
|
||||
EclipseNode annotationNode = typeNode.getNodeFor(ann);
|
||||
AnnotationValues<ExtensionMethod> annotation = createAnnotation(ExtensionMethod.class, annotationNode);
|
||||
boolean suppressBaseMethods = false;
|
||||
try {
|
||||
suppressBaseMethods = annotation.getInstance().suppressBaseMethods();
|
||||
} catch (AnnotationValueDecodeFail fail) {
|
||||
fail.owner.setError(fail.getMessage(), fail.idx);
|
||||
}
|
||||
for (Object extensionMethodProvider : annotation.getActualExpressions("value")) {
|
||||
if (extensionMethodProvider instanceof ClassLiteralAccess) {
|
||||
TypeBinding binding = ((ClassLiteralAccess) extensionMethodProvider).type.resolveType(blockScope);
|
||||
if (binding == null) continue;
|
||||
if (!binding.isClass() && !binding.isEnum()) continue;
|
||||
Extension e = new Extension();
|
||||
e.extensionMethods = getApplicableExtensionMethodsDefinedInProvider(typeNode, (ReferenceBinding) binding, receiverType);
|
||||
e.suppressBaseMethods = suppressBaseMethods;
|
||||
extensions.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return extensions;
|
||||
}
|
||||
|
||||
private static List<MethodBinding> getApplicableExtensionMethodsDefinedInProvider(EclipseNode typeNode, ReferenceBinding extensionMethodProviderBinding,
|
||||
TypeBinding receiverType) {
|
||||
|
||||
List<MethodBinding> extensionMethods = new ArrayList<MethodBinding>();
|
||||
CompilationUnitScope cuScope = ((CompilationUnitDeclaration) typeNode.top().get()).scope;
|
||||
for (MethodBinding method : extensionMethodProviderBinding.methods()) {
|
||||
if (!method.isStatic()) continue;
|
||||
if (!method.isPublic()) continue;
|
||||
if (method.parameters == null || method.parameters.length == 0) continue;
|
||||
TypeBinding firstArgType = method.parameters[0];
|
||||
if (receiverType.isProvablyDistinct(firstArgType) && !receiverType.isCompatibleWith(firstArgType.erasure())) continue;
|
||||
TypeBinding[] argumentTypes = Arrays.copyOfRange(method.parameters, 1, method.parameters.length);
|
||||
if ((receiverType instanceof ReferenceBinding) && ((ReferenceBinding) receiverType).getExactMethod(method.selector, argumentTypes, cuScope) != null) continue;
|
||||
extensionMethods.add(method);
|
||||
}
|
||||
return extensionMethods;
|
||||
}
|
||||
|
||||
private static final Map<MessageSend, PostponedError> ERRORS = new WeakHashMap<MessageSend, PostponedError>();
|
||||
|
||||
public static void errorNoMethodFor(ProblemReporter problemReporter, MessageSend messageSend, TypeBinding recType, TypeBinding[] params) {
|
||||
ERRORS.put(messageSend, new PostponedNoMethodError(problemReporter, messageSend, recType, params));
|
||||
}
|
||||
|
||||
public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) {
|
||||
ERRORS.put(messageSend, new PostponedInvalidMethodError(problemReporter, messageSend, method));
|
||||
}
|
||||
|
||||
public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) {
|
||||
List<Extension> extensions = new ArrayList<Extension>();
|
||||
TypeDeclaration decl = scope.classScope().referenceContext;
|
||||
|
||||
EclipseNode owningType = null;
|
||||
|
||||
for (EclipseNode typeNode = getTypeNode(decl); typeNode != null; typeNode = upToType(typeNode)) {
|
||||
Annotation ann = getAnnotation(ExtensionMethod.class, typeNode);
|
||||
if (ann != null) {
|
||||
extensions.addAll(0, getApplicableExtensionMethods(typeNode, ann, methodCall.receiver.resolvedType));
|
||||
if (owningType == null) owningType = typeNode;
|
||||
}
|
||||
}
|
||||
|
||||
if (methodCall.binding == null) return resolvedType;
|
||||
|
||||
for (Extension extension : extensions) {
|
||||
if (!extension.suppressBaseMethods && !(methodCall.binding instanceof ProblemMethodBinding)) continue;
|
||||
for (MethodBinding extensionMethod : extension.extensionMethods) {
|
||||
if (!Arrays.equals(methodCall.selector, extensionMethod.selector)) continue;
|
||||
ERRORS.remove(methodCall);
|
||||
if (methodCall.receiver instanceof ThisReference) {
|
||||
methodCall.receiver.bits &= ~ASTNode.IsImplicitThis;
|
||||
}
|
||||
List<Expression> arguments = new ArrayList<Expression>();
|
||||
arguments.add(methodCall.receiver);
|
||||
if (methodCall.arguments != null) arguments.addAll(Arrays.asList(methodCall.arguments));
|
||||
List<TypeBinding> argumentTypes = new ArrayList<TypeBinding>();
|
||||
argumentTypes.add(methodCall.receiver.resolvedType);
|
||||
if (methodCall.binding.parameters != null) argumentTypes.addAll(Arrays.asList(methodCall.binding.parameters));
|
||||
MethodBinding fixedBinding = scope.getMethod(extensionMethod.declaringClass, methodCall.selector, argumentTypes.toArray(new TypeBinding[0]), methodCall);
|
||||
if (fixedBinding instanceof ProblemMethodBinding) {
|
||||
if (fixedBinding.declaringClass != null) {
|
||||
scope.problemReporter().invalidMethod(methodCall, fixedBinding);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0, iend = arguments.size(); i < iend; i++) {
|
||||
Expression arg = arguments.get(i);
|
||||
if (fixedBinding.parameters[i].isArrayType() != arg.resolvedType.isArrayType()) break;
|
||||
if (arg.resolvedType.isArrayType()) {
|
||||
if (arg instanceof MessageSend) {
|
||||
((MessageSend) arg).valueCast = arg.resolvedType;
|
||||
}
|
||||
}
|
||||
if (!fixedBinding.parameters[i].isBaseType() && arg.resolvedType.isBaseType()) {
|
||||
int id = arg.resolvedType.id;
|
||||
arg.implicitConversion = TypeIds.BOXING | (id + (id << 4)); // magic see TypeIds
|
||||
} else if (fixedBinding.parameters[i].isBaseType() && !arg.resolvedType.isBaseType()) {
|
||||
int id = fixedBinding.parameters[i].id;
|
||||
arg.implicitConversion = TypeIds.UNBOXING | (id + (id << 4)); // magic see TypeIds
|
||||
}
|
||||
}
|
||||
methodCall.arguments = arguments.toArray(new Expression[0]);
|
||||
|
||||
methodCall.receiver = createNameRef(extensionMethod.declaringClass, methodCall);
|
||||
methodCall.actualReceiverType = extensionMethod.declaringClass;
|
||||
methodCall.binding = fixedBinding;
|
||||
methodCall.resolvedType = methodCall.binding.returnType;
|
||||
}
|
||||
return methodCall.resolvedType;
|
||||
}
|
||||
}
|
||||
|
||||
PostponedError error = ERRORS.get(methodCall);
|
||||
if (error != null) error.fire();
|
||||
|
||||
ERRORS.remove(methodCall);
|
||||
return resolvedType;
|
||||
}
|
||||
|
||||
private static NameReference createNameRef(TypeBinding typeBinding, ASTNode source) {
|
||||
long p = ((long) source.sourceStart << 32) | source.sourceEnd;
|
||||
char[] pkg = typeBinding.qualifiedPackageName();
|
||||
char[] basename = typeBinding.qualifiedSourceName();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (pkg != null) sb.append(pkg);
|
||||
if (sb.length() > 0) sb.append(".");
|
||||
sb.append(basename);
|
||||
|
||||
String tName = sb.toString();
|
||||
|
||||
if (tName.indexOf('.') == -1) {
|
||||
return new SingleNameReference(basename, p);
|
||||
} else {
|
||||
char[][] sources;
|
||||
String[] in = tName.split("\\.");
|
||||
sources = new char[in.length][];
|
||||
for (int i = 0; i < in.length; i++) sources[i] = in[i].toCharArray();
|
||||
long[] poss = new long[in.length];
|
||||
Arrays.fill(poss, p);
|
||||
return new QualifiedNameReference(sources, poss, source.sourceStart, source.sourceEnd);
|
||||
}
|
||||
}
|
||||
|
||||
public static IJavaCompletionProposal[] getJavaCompletionProposals(IJavaCompletionProposal[] javaCompletionProposals,
|
||||
CompletionProposalCollector completionProposalCollector) {
|
||||
|
||||
List<IJavaCompletionProposal> proposals = new ArrayList<IJavaCompletionProposal>(Arrays.asList(javaCompletionProposals));
|
||||
if (canExtendCodeAssist(proposals)) {
|
||||
IJavaCompletionProposal firstProposal = proposals.get(0);
|
||||
int replacementOffset = getReplacementOffset(firstProposal);
|
||||
for (Extension extension : getExtensionMethods(completionProposalCollector)) {
|
||||
for (MethodBinding method : extension.extensionMethods) {
|
||||
ExtensionMethodCompletionProposal newProposal = new ExtensionMethodCompletionProposal(replacementOffset);
|
||||
copyNameLookupAndCompletionEngine(completionProposalCollector, firstProposal, newProposal);
|
||||
ASTNode node = getAssistNode(completionProposalCollector);
|
||||
newProposal.setMethodBinding(method, node);
|
||||
createAndAddJavaCompletionProposal(completionProposalCollector, newProposal, proposals);
|
||||
}
|
||||
}
|
||||
}
|
||||
return proposals.toArray(new IJavaCompletionProposal[proposals.size()]);
|
||||
}
|
||||
|
||||
private static void copyNameLookupAndCompletionEngine(CompletionProposalCollector completionProposalCollector, IJavaCompletionProposal proposal,
|
||||
InternalCompletionProposal newProposal) {
|
||||
|
||||
try {
|
||||
InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector);
|
||||
InternalExtendedCompletionContext extendedContext = (InternalExtendedCompletionContext) Reflection.extendedContextField.get(context);
|
||||
LookupEnvironment lookupEnvironment = (LookupEnvironment) Reflection.lookupEnvironmentField.get(extendedContext);
|
||||
Reflection.nameLookupField.set(newProposal, ((SearchableEnvironment) lookupEnvironment.nameEnvironment).nameLookup);
|
||||
Reflection.completionEngineField.set(newProposal, lookupEnvironment.typeRequestor);
|
||||
} catch (IllegalAccessException ignore) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private static void createAndAddJavaCompletionProposal(CompletionProposalCollector completionProposalCollector, CompletionProposal newProposal,
|
||||
List<IJavaCompletionProposal> proposals) {
|
||||
|
||||
try {
|
||||
proposals.add((IJavaCompletionProposal) Reflection.createJavaCompletionProposalMethod.invoke(completionProposalCollector, newProposal));
|
||||
} catch (Exception ignore) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canExtendCodeAssist(List<IJavaCompletionProposal> proposals) {
|
||||
return !proposals.isEmpty() && Reflection.isComplete();
|
||||
}
|
||||
|
||||
private static int getReplacementOffset(IJavaCompletionProposal proposal) {
|
||||
try {
|
||||
return Reflection.replacementOffsetField.getInt(proposal);
|
||||
} catch (Exception ignore) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 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 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);
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Project Lombok Authors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package lombok.eclipse.agent;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
|
||||
|
||||
import lombok.Lombok;
|
||||
|
||||
|
||||
public class PatchExtensionMethodPortal {
|
||||
private static final String TYPE_BINDING = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding";
|
||||
private static final String TYPE_BINDING_ARRAY = "[Lorg.eclipse.jdt.internal.compiler.lookup.TypeBinding;";
|
||||
private static final String MESSAGE_SEND = "org.eclipse.jdt.internal.compiler.ast.MessageSend";
|
||||
private static final String BLOCK_SCOPE = "org.eclipse.jdt.internal.compiler.lookup.BlockScope";
|
||||
private static final String METHOD_BINDING = "org.eclipse.jdt.internal.compiler.lookup.MethodBinding";
|
||||
private static final String PROBLEM_REPORTER = "org.eclipse.jdt.internal.compiler.problem.ProblemReporter";
|
||||
private static final String COMPLETION_PROPOSAL_COLLECTOR = "org.eclipse.jdt.ui.text.java.CompletionProposalCollector";
|
||||
private static final String I_JAVA_COMPLETION_PROPOSAL_ARRAY = "[Lorg.eclipse.jdt.ui.text.java.IJavaCompletionProposal;";
|
||||
|
||||
public static TypeBinding resolveType(Object resolvedType, Object methodCall, Object scope) {
|
||||
try {
|
||||
return (TypeBinding) Reflection.resolveType.invoke(null, resolvedType, methodCall, scope);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
return (TypeBinding) resolvedType;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (NullPointerException e) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static void errorNoMethodFor(Object problemReporter, Object messageSend, Object recType, Object params) {
|
||||
try {
|
||||
Reflection.errorNoMethodFor.invoke(null, problemReporter, messageSend, recType, params);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
} catch (IllegalAccessException e) {
|
||||
Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Lombok.sneakyThrow(e);
|
||||
} catch (NullPointerException e) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static void invalidMethod(Object problemReporter, Object messageSend, Object method) {
|
||||
try {
|
||||
Reflection.invalidMethod.invoke(null, problemReporter, messageSend, method);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
} catch (IllegalAccessException e) {
|
||||
Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Lombok.sneakyThrow(e);
|
||||
} catch (NullPointerException e) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static Object getJavaCompletionProposals(Object[] javaCompletionProposals, Object completionProposalCollector) {
|
||||
try {
|
||||
return Reflection.getJavaCompletionProposals.invoke(null, javaCompletionProposals, completionProposalCollector);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
return javaCompletionProposals;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (NullPointerException e) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Reflection {
|
||||
public static final Method resolveType, errorNoMethodFor, invalidMethod, getJavaCompletionProposals;
|
||||
public static final Throwable problem;
|
||||
|
||||
static {
|
||||
Method m = null, n = null, o = null, p = null;
|
||||
Throwable problem_ = null;
|
||||
try {
|
||||
m = PatchExtensionMethod.class.getMethod("resolveType", Class.forName(TYPE_BINDING), Class.forName(MESSAGE_SEND), Class.forName(BLOCK_SCOPE));
|
||||
n = PatchExtensionMethod.class.getMethod("errorNoMethodFor", Class.forName(PROBLEM_REPORTER),
|
||||
Class.forName(MESSAGE_SEND), Class.forName(TYPE_BINDING), Class.forName(TYPE_BINDING_ARRAY));
|
||||
o = PatchExtensionMethod.class.getMethod("invalidMethod", Class.forName(PROBLEM_REPORTER), Class.forName(MESSAGE_SEND), Class.forName(METHOD_BINDING));
|
||||
p = PatchExtensionMethod.class.getMethod("getJavaCompletionProposals", Class.forName(I_JAVA_COMPLETION_PROPOSAL_ARRAY), Class.forName(COMPLETION_PROPOSAL_COLLECTOR));
|
||||
} catch (Throwable t) {
|
||||
// That's problematic, but as long as no local classes are used we don't actually need it.
|
||||
// Better fail on local classes than crash altogether.
|
||||
problem_ = t;
|
||||
}
|
||||
resolveType = m;
|
||||
errorNoMethodFor = n;
|
||||
invalidMethod = o;
|
||||
getJavaCompletionProposals = p;
|
||||
problem = problem_;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user