();
- for (IMethod m : methods) {
- if (m.getNameRange().getLength() > 0 && !m.getNameRange().equals(m.getSourceRange())) result.add(m);
- }
- return result.size() == methods.length ? methods : result.toArray(new IMethod[result.size()]);
- }
-
- public static SimpleName[] removeGeneratedSimpleNames(SimpleName[] in) throws Exception {
- Field f = SimpleName.class.getField("$isGenerated");
-
- int count = 0;
- for (int i = 0; i < in.length; i++) {
- if (in[i] == null || !((Boolean)f.get(in[i])).booleanValue()) count++;
- }
- if (count == in.length) return in;
- SimpleName[] newSimpleNames = new SimpleName[count];
- count = 0;
- for (int i = 0; i < in.length; i++) {
- if (in[i] == null || !((Boolean)f.get(in[i])).booleanValue()) newSimpleNames[count++] = in[i];
- }
- return newSimpleNames;
- }
-
- public static byte[] runPostCompiler(byte[] bytes, String fileName) {
- byte[] transformed = PostCompiler.applyTransformations(bytes, fileName, DiagnosticsReceiver.CONSOLE);
- return transformed == null ? bytes : transformed;
- }
-
- public static OutputStream runPostCompiler(OutputStream out) throws IOException {
- return PostCompiler.wrapOutputStream(out, "TEST", DiagnosticsReceiver.CONSOLE);
- }
-
- public static BufferedOutputStream runPostCompiler(BufferedOutputStream out, String path, String name) throws IOException {
- String fileName = path + "/" + name;
- return new BufferedOutputStream(PostCompiler.wrapOutputStream(out, fileName, DiagnosticsReceiver.CONSOLE));
- }
-
- public static Annotation[] convertAnnotations(Annotation[] out, IAnnotatable annotatable) {
- IAnnotation[] in;
-
- try {
- in = annotatable.getAnnotations();
- } catch (Exception e) {
- return out;
- }
-
- if (out == null) return null;
- int toWrite = 0;
-
- for (int idx = 0; idx < out.length; idx++) {
- String oName = new String(out[idx].type.getLastToken());
- boolean found = false;
- for (IAnnotation i : in) {
- String name = i.getElementName();
- int li = name.lastIndexOf('.');
- if (li > -1) name = name.substring(li + 1);
- if (name.equals(oName)) {
- found = true;
- break;
- }
- }
- if (!found) out[idx] = null;
- else toWrite++;
- }
-
- Annotation[] replace = out;
- if (toWrite < out.length) {
- replace = new Annotation[toWrite];
- int idx = 0;
- for (int i = 0; i < out.length; i++) {
- if (out[i] == null) continue;
- replace[idx++] = out[i];
- }
- }
-
- return replace;
- }
-}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java b/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java
new file mode 100644
index 00000000..6685b6bb
--- /dev/null
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import lombok.core.DiagnosticsReceiver;
+import lombok.core.PostCompiler;
+import lombok.core.Version;
+
+public class PatchFixesShadowLoaded {
+ public static String addLombokNotesToEclipseAboutDialog(String origReturnValue, String key) {
+ if ("aboutText".equals(key)) {
+ return origReturnValue + "\n\nLombok " + Version.getFullVersion() + " is installed. http://projectlombok.org/";
+ }
+ return origReturnValue;
+ }
+
+ public static byte[] runPostCompiler(byte[] bytes, String fileName) {
+ byte[] transformed = PostCompiler.applyTransformations(bytes, fileName, DiagnosticsReceiver.CONSOLE);
+ return transformed == null ? bytes : transformed;
+ }
+
+ public static OutputStream runPostCompiler(OutputStream out) throws IOException {
+ return PostCompiler.wrapOutputStream(out, "TEST", DiagnosticsReceiver.CONSOLE);
+ }
+
+ public static BufferedOutputStream runPostCompiler(BufferedOutputStream out, String path, String name) throws IOException {
+ String fileName = path + "/" + name;
+ return new BufferedOutputStream(PostCompiler.wrapOutputStream(out, fileName, DiagnosticsReceiver.CONSOLE));
+ }
+}
diff --git a/src/eclipseAgent/lombok/launch/PatchFixesHider.java b/src/eclipseAgent/lombok/launch/PatchFixesHider.java
new file mode 100644
index 00000000..23774d0f
--- /dev/null
+++ b/src/eclipseAgent/lombok/launch/PatchFixesHider.java
@@ -0,0 +1,587 @@
+/*
+ * Copyright (C) 2010-2015 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.launch;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+
+import lombok.eclipse.EclipseAugments;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IAnnotatable;
+import org.eclipse.jdt.core.IAnnotation;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.parser.Parser;
+import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
+import org.eclipse.jdt.internal.core.dom.rewrite.NodeRewriteEvent;
+import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent;
+import org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner;
+import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
+
+/** These contain a mix of the following:
+ *
+ * - 'dependency free' method wrappers that cross the shadowloader barrier.
+ *
- methods that directly patch, but, these should ALWAYS be transplanted.
+ *
+ *
+ * This class lives on the outside of the shadowloader barrier, and as a consequence, cannot access any other lombok code except other
+ * code in the {@code lombok.launch} package!.
+ *
+ * This class is package private with lots of public inner static classes. This hides all of them from IDE autocomplete dialogs and such but at the JVM
+ * level the inner static class are just plain public, which is important, because calls to the contents of these inner static classes are injected into
+ * various eclipse classes verbatim, and if they weren't public, the verifier wouldn't accept it.
+ */
+final class PatchFixesHider {
+
+ /** These utility methods are only used 'internally', but because of transplant methods, the class (and its methods) still have to be public! */
+ public static final class Util {
+ public static Class> shadowLoadClass(String name) {
+ try {
+ return Class.forName(name, true, Main.createShadowClassLoader());
+ } catch (ClassNotFoundException e) {
+ throw sneakyThrow(e);
+ }
+ }
+
+ public static Method findMethod(Class> type, String name, Class>... parameterTypes) {
+ try {
+ return type.getDeclaredMethod(name, parameterTypes);
+ } catch (NoSuchMethodException e) {
+ throw sneakyThrow(e);
+ }
+ }
+
+ public static Object invokeMethod(Method method, Object... args) {
+ try {
+ return method.invoke(null, args);
+ } catch (IllegalAccessException e) {
+ throw sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ throw sneakyThrow(e.getCause());
+ }
+ }
+
+ private static RuntimeException sneakyThrow(Throwable t) {
+ if (t == null) throw new NullPointerException("t");
+ Util.sneakyThrow0(t);
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void sneakyThrow0(Throwable t) throws T {
+ throw (T)t;
+ }
+ }
+
+ /** Contains patch fixes that are dependent on lombok internals. */
+ public static final class LombokDeps {
+ public static final Method ADD_LOMBOK_NOTES;
+ public static final Method POST_COMPILER_BYTES_STRING;
+ public static final Method POST_COMPILER_OUTPUTSTREAM;
+ public static final Method POST_COMPILER_BUFFEREDOUTPUTSTREAM_STRING_STRING;
+
+ static {
+ Class> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchFixesShadowLoaded");
+ ADD_LOMBOK_NOTES = Util.findMethod(shadowed, "addLombokNotesToEclipseAboutDialog", String.class, String.class);
+ POST_COMPILER_BYTES_STRING = Util.findMethod(shadowed, "runPostCompiler", byte[].class, String.class);
+ POST_COMPILER_OUTPUTSTREAM = Util.findMethod(shadowed, "runPostCompiler", OutputStream.class);
+ POST_COMPILER_BUFFEREDOUTPUTSTREAM_STRING_STRING = Util.findMethod(shadowed, "runPostCompiler", BufferedOutputStream.class, String.class, String.class);
+ }
+
+ public static String addLombokNotesToEclipseAboutDialog(String origReturnValue, String key) {
+ return (String) Util.invokeMethod(LombokDeps.ADD_LOMBOK_NOTES, origReturnValue, key);
+ }
+
+ public static byte[] runPostCompiler(byte[] bytes, String fileName) {
+ return (byte[]) Util.invokeMethod(LombokDeps.POST_COMPILER_BYTES_STRING, bytes, fileName);
+ }
+
+ public static OutputStream runPostCompiler(OutputStream out) throws IOException {
+ return (OutputStream) Util.invokeMethod(LombokDeps.POST_COMPILER_OUTPUTSTREAM, out);
+ }
+
+ public static BufferedOutputStream runPostCompiler(BufferedOutputStream out, String path, String name) throws IOException {
+ return (BufferedOutputStream) Util.invokeMethod(LombokDeps.POST_COMPILER_BUFFEREDOUTPUTSTREAM_STRING_STRING, out, path, name);
+ }
+ }
+
+ public static final class Transform {
+ private static final Method TRANSFORM;
+ private static final Method TRANSFORM_SWAPPED;
+
+ static {
+ Class> shadowed = Util.shadowLoadClass("lombok.eclipse.TransformEclipseAST");
+ TRANSFORM = Util.findMethod(shadowed, "transform", Parser.class, CompilationUnitDeclaration.class);
+ TRANSFORM_SWAPPED = Util.findMethod(shadowed, "transform_swapped", CompilationUnitDeclaration.class, Parser.class);
+ }
+
+ public static void transform(Parser parser, CompilationUnitDeclaration ast) throws IOException {
+ Util.invokeMethod(TRANSFORM, parser, ast);
+ }
+
+ public static void transform_swapped(CompilationUnitDeclaration ast, Parser parser) throws IOException {
+ Util.invokeMethod(TRANSFORM_SWAPPED, ast, parser);
+ }
+ }
+
+ /** Contains patch code to support {@code @Delegate} */
+ public static final class Delegate {
+ private static final Method HANDLE_DELEGATE_FOR_TYPE;
+
+ static {
+ Class> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchDelegatePortal");
+ HANDLE_DELEGATE_FOR_TYPE = Util.findMethod(shadowed, "handleDelegateForType", Object.class);
+ }
+
+ public static boolean handleDelegateForType(Object classScope) {
+ return (Boolean) Util.invokeMethod(HANDLE_DELEGATE_FOR_TYPE, classScope);
+ }
+ }
+
+ /** Contains patch code to support {@code val} (eclipse specific) */
+ public static final class ValPortal {
+ private static final Method COPY_INITIALIZATION_OF_FOR_EACH_ITERABLE;
+ private static final Method COPY_INITIALIZATION_OF_LOCAL_DECLARATION;
+ private static final Method ADD_FINAL_AND_VAL_ANNOTATION_TO_VARIABLE_DECLARATION_STATEMENT;
+ private static final Method ADD_FINAL_AND_VAL_ANNOTATION_TO_SINGLE_VARIABLE_DECLARATION;
+
+ static {
+ Class> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchValEclipsePortal");
+ COPY_INITIALIZATION_OF_FOR_EACH_ITERABLE = Util.findMethod(shadowed, "copyInitializationOfForEachIterable", Object.class);
+ COPY_INITIALIZATION_OF_LOCAL_DECLARATION = Util.findMethod(shadowed, "copyInitializationOfLocalDeclaration", Object.class);
+ ADD_FINAL_AND_VAL_ANNOTATION_TO_VARIABLE_DECLARATION_STATEMENT = Util.findMethod(shadowed, "addFinalAndValAnnotationToVariableDeclarationStatement", Object.class, Object.class, Object.class);
+ ADD_FINAL_AND_VAL_ANNOTATION_TO_SINGLE_VARIABLE_DECLARATION = Util.findMethod(shadowed, "addFinalAndValAnnotationToSingleVariableDeclaration", Object.class, Object.class, Object.class);
+ }
+
+ public static void copyInitializationOfForEachIterable(Object parser) {
+ Util.invokeMethod(COPY_INITIALIZATION_OF_FOR_EACH_ITERABLE, parser);
+ }
+
+ public static void copyInitializationOfLocalDeclaration(Object parser) {
+ Util.invokeMethod(COPY_INITIALIZATION_OF_LOCAL_DECLARATION, parser);
+ }
+
+ public static void addFinalAndValAnnotationToVariableDeclarationStatement(Object converter, Object out, Object in) {
+ Util.invokeMethod(ADD_FINAL_AND_VAL_ANNOTATION_TO_VARIABLE_DECLARATION_STATEMENT, converter, out, in);
+ }
+
+ public static void addFinalAndValAnnotationToSingleVariableDeclaration(Object converter, Object out, Object in) {
+ Util.invokeMethod(ADD_FINAL_AND_VAL_ANNOTATION_TO_SINGLE_VARIABLE_DECLARATION, converter, out, in);
+ }
+ }
+
+ /** Contains patch code to support {@code val} (eclipse and ecj) */
+ public static final class Val {
+ private static final Method SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED;
+ private static final Method SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2;
+ private static final Method HANDLE_VAL_FOR_LOCAL_DECLARATION;
+ private static final Method HANDLE_VAL_FOR_FOR_EACH;
+
+ static {
+ Class> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchVal");
+ SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED = Util.findMethod(shadowed, "skipResolveInitializerIfAlreadyCalled", Expression.class, BlockScope.class);
+ SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2 = Util.findMethod(shadowed, "skipResolveInitializerIfAlreadyCalled2", Expression.class, BlockScope.class, LocalDeclaration.class);
+ HANDLE_VAL_FOR_LOCAL_DECLARATION = Util.findMethod(shadowed, "handleValForLocalDeclaration", LocalDeclaration.class, BlockScope.class);
+ HANDLE_VAL_FOR_FOR_EACH = Util.findMethod(shadowed, "handleValForForEach", ForeachStatement.class, BlockScope.class);
+ }
+
+ public static TypeBinding skipResolveInitializerIfAlreadyCalled(Expression expr, BlockScope scope) {
+ return (TypeBinding) Util.invokeMethod(SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED, expr, scope);
+ }
+
+ public static TypeBinding skipResolveInitializerIfAlreadyCalled2(Expression expr, BlockScope scope, LocalDeclaration decl) {
+ return (TypeBinding) Util.invokeMethod(SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2, expr, scope, decl);
+ }
+
+ public static boolean handleValForLocalDeclaration(LocalDeclaration local, BlockScope scope) {
+ return (Boolean) Util.invokeMethod(HANDLE_VAL_FOR_LOCAL_DECLARATION, local, scope);
+ }
+
+ public static boolean handleValForForEach(ForeachStatement forEach, BlockScope scope) {
+ return (Boolean) Util.invokeMethod(HANDLE_VAL_FOR_FOR_EACH, forEach, scope);
+ }
+ }
+
+ /** Contains patch code to support {@code @ExtensionMethod} */
+ public static final class ExtensionMethod {
+ private static final Method RESOLVE_TYPE;
+ private static final Method ERROR_NO_METHOD_FOR;
+ private static final Method INVALID_METHOD;
+
+ static {
+ Class> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchExtensionMethod");
+ RESOLVE_TYPE = Util.findMethod(shadowed, "resolveType", TypeBinding.class, MessageSend.class, BlockScope.class);
+ ERROR_NO_METHOD_FOR = Util.findMethod(shadowed, "errorNoMethodFor", ProblemReporter.class, MessageSend.class, TypeBinding.class, TypeBinding[].class);
+ INVALID_METHOD = Util.findMethod(shadowed, "invalidMethod", ProblemReporter.class, MessageSend.class, MethodBinding.class);
+ }
+
+ public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) {
+ return (TypeBinding) Util.invokeMethod(RESOLVE_TYPE, resolvedType, methodCall, scope);
+ }
+
+ public static void errorNoMethodFor(ProblemReporter problemReporter, MessageSend messageSend, TypeBinding recType, TypeBinding[] params) {
+ Util.invokeMethod(ERROR_NO_METHOD_FOR, problemReporter, messageSend, recType, params);
+ }
+
+ public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) {
+ Util.invokeMethod(INVALID_METHOD, problemReporter, messageSend, method);
+ }
+ }
+
+ /**
+ * Contains a mix of methods: ecj only, ecj+eclipse, and eclipse only. As a consequence, _EVERY_ method from here used for ecj MUST be
+ * transplanted, as ecj itself cannot load this class (signatures refer to things that don't exist in ecj-only mode).
+ *
+ * Because of usage of transplant(), a bunch of these contain direct code and don't try to cross the shadowloader barrier.
+ */
+ public static final class PatchFixes {
+ public static boolean isGenerated(org.eclipse.jdt.core.dom.ASTNode node) {
+ boolean result = false;
+ try {
+ result = ((Boolean)node.getClass().getField("$isGenerated").get(node)).booleanValue();
+ if (!result && node.getParent() != null && node.getParent() instanceof org.eclipse.jdt.core.dom.QualifiedName) {
+ result = isGenerated(node.getParent());
+ }
+ } catch (Exception e) {
+ // better to assume it isn't generated
+ }
+ return result;
+ }
+
+ public static boolean isListRewriteOnGeneratedNode(org.eclipse.jdt.core.dom.rewrite.ListRewrite rewrite) {
+ return isGenerated(rewrite.getParent());
+ }
+
+ public static boolean returnFalse(java.lang.Object object) {
+ return false;
+ }
+
+ public static boolean returnTrue(java.lang.Object object) {
+ return true;
+ }
+
+ @java.lang.SuppressWarnings({"unchecked", "rawtypes"}) public static java.util.List removeGeneratedNodes(java.util.List list) {
+ try {
+ java.util.List realNodes = new java.util.ArrayList(list.size());
+ for (java.lang.Object node : list) {
+ if(!isGenerated(((org.eclipse.jdt.core.dom.ASTNode)node))) {
+ realNodes.add(node);
+ }
+ }
+ return realNodes;
+ } catch (Exception e) {
+ }
+ return list;
+ }
+
+ public static java.lang.String getRealMethodDeclarationSource(java.lang.String original, Object processor, org.eclipse.jdt.core.dom.MethodDeclaration declaration) throws Exception {
+ if (!isGenerated(declaration)) return original;
+
+ List annotations = new ArrayList();
+ for (Object modifier : declaration.modifiers()) {
+ if (modifier instanceof org.eclipse.jdt.core.dom.Annotation) {
+ org.eclipse.jdt.core.dom.Annotation annotation = (org.eclipse.jdt.core.dom.Annotation)modifier;
+ String qualifiedAnnotationName = annotation.resolveTypeBinding().getQualifiedName();
+ if (!"java.lang.Override".equals(qualifiedAnnotationName) && !"java.lang.SuppressWarnings".equals(qualifiedAnnotationName)) annotations.add(annotation);
+ }
+ }
+
+ StringBuilder signature = new StringBuilder();
+ addAnnotations(annotations, signature);
+
+ if ((Boolean)processor.getClass().getDeclaredField("fPublic").get(processor)) signature.append("public ");
+ if ((Boolean)processor.getClass().getDeclaredField("fAbstract").get(processor)) signature.append("abstract ");
+
+ signature
+ .append(declaration.getReturnType2().toString())
+ .append(" ").append(declaration.getName().getFullyQualifiedName())
+ .append("(");
+
+ boolean first = true;
+ for (Object parameter : declaration.parameters()) {
+ if (!first) signature.append(", ");
+ first = false;
+ // We should also add the annotations of the parameters
+ signature.append(parameter);
+ }
+
+ signature.append(");");
+ return signature.toString();
+ }
+
+ // part of getRealMethodDeclarationSource(...)
+ public static void addAnnotations(List annotations, StringBuilder signature) {
+ /*
+ * We SHOULD be able to handle the following cases:
+ * @Override
+ * @Override()
+ * @SuppressWarnings("all")
+ * @SuppressWarnings({"all", "unused"})
+ * @SuppressWarnings(value = "all")
+ * @SuppressWarnings(value = {"all", "unused"})
+ * @EqualsAndHashCode(callSuper=true, of="id")
+ *
+ * Currently, we only seem to correctly support:
+ * @Override
+ * @Override() N.B. We lose the parentheses here, since there are no values. No big deal.
+ * @SuppressWarnings("all")
+ */
+ for (org.eclipse.jdt.core.dom.Annotation annotation : annotations) {
+ List values = new ArrayList();
+ if (annotation.isSingleMemberAnnotation()) {
+ org.eclipse.jdt.core.dom.SingleMemberAnnotation smAnn = (org.eclipse.jdt.core.dom.SingleMemberAnnotation) annotation;
+ values.add(smAnn.getValue().toString());
+ } else if (annotation.isNormalAnnotation()) {
+ org.eclipse.jdt.core.dom.NormalAnnotation normalAnn = (org.eclipse.jdt.core.dom.NormalAnnotation) annotation;
+ for (Object value : normalAnn.values()) values.add(value.toString());
+ }
+
+ signature.append("@").append(annotation.resolveTypeBinding().getQualifiedName());
+ if (!values.isEmpty()) {
+ signature.append("(");
+ boolean first = true;
+ for (String string : values) {
+ if (!first) signature.append(", ");
+ first = false;
+ signature.append('"').append(string).append('"');
+ }
+ signature.append(")");
+ }
+ signature.append(" ");
+ }
+ }
+
+ public static org.eclipse.jdt.core.dom.MethodDeclaration getRealMethodDeclarationNode(org.eclipse.jdt.core.IMethod sourceMethod, org.eclipse.jdt.core.dom.CompilationUnit cuUnit) throws JavaModelException {
+ MethodDeclaration methodDeclarationNode = ASTNodeSearchUtil.getMethodDeclarationNode(sourceMethod, cuUnit);
+ if (isGenerated(methodDeclarationNode)) {
+ IType declaringType = sourceMethod.getDeclaringType();
+ Stack typeStack = new Stack();
+ while (declaringType != null) {
+ typeStack.push(declaringType);
+ declaringType = declaringType.getDeclaringType();
+ }
+
+ IType rootType = typeStack.pop();
+ org.eclipse.jdt.core.dom.AbstractTypeDeclaration typeDeclaration = findTypeDeclaration(rootType, cuUnit.types());
+ while (!typeStack.isEmpty() && typeDeclaration != null) {
+ typeDeclaration = findTypeDeclaration(typeStack.pop(), typeDeclaration.bodyDeclarations());
+ }
+
+ if (typeStack.isEmpty() && typeDeclaration != null) {
+ String methodName = sourceMethod.getElementName();
+ for (Object declaration : typeDeclaration.bodyDeclarations()) {
+ if (declaration instanceof org.eclipse.jdt.core.dom.MethodDeclaration) {
+ org.eclipse.jdt.core.dom.MethodDeclaration methodDeclaration = (org.eclipse.jdt.core.dom.MethodDeclaration) declaration;
+ if (methodDeclaration.getName().toString().equals(methodName)) {
+ return methodDeclaration;
+ }
+ }
+ }
+ }
+ }
+ return methodDeclarationNode;
+ }
+
+ // part of getRealMethodDeclarationNode
+ public static org.eclipse.jdt.core.dom.AbstractTypeDeclaration findTypeDeclaration(IType searchType, List> nodes) {
+ for (Object object : nodes) {
+ if (object instanceof org.eclipse.jdt.core.dom.AbstractTypeDeclaration) {
+ org.eclipse.jdt.core.dom.AbstractTypeDeclaration typeDeclaration = (org.eclipse.jdt.core.dom.AbstractTypeDeclaration) object;
+ if (typeDeclaration.getName().toString().equals(searchType.getElementName()))
+ return typeDeclaration;
+ }
+ }
+ return null;
+ }
+
+ public static int getSourceEndFixed(int sourceEnd, org.eclipse.jdt.internal.compiler.ast.ASTNode node) throws Exception {
+ if (sourceEnd == -1) {
+ org.eclipse.jdt.internal.compiler.ast.ASTNode object = (org.eclipse.jdt.internal.compiler.ast.ASTNode)node.getClass().getField("$generatedBy").get(node);
+ if (object != null) {
+ return object.sourceEnd;
+ }
+ }
+ return sourceEnd;
+ }
+
+ public static int fixRetrieveStartingCatchPosition(int original, int start) {
+ return original == -1 ? start : original;
+ }
+
+ public static int fixRetrieveIdentifierEndPosition(int original, int end) {
+ return original == -1 ? end : original;
+ }
+
+ public static int fixRetrieveEllipsisStartPosition(int original, int end) {
+ return original == -1 ? end : original;
+ }
+
+ public static int fixRetrieveRightBraceOrSemiColonPosition(int original, int end) {
+ return original == -1 ? end : original; // Need to fix: see issue 325.
+ }
+
+ public static final int ALREADY_PROCESSED_FLAG = 0x800000; //Bit 24
+
+ public static boolean checkBit24(Object node) throws Exception {
+ int bits = (Integer)(node.getClass().getField("bits").get(node));
+ return (bits & ALREADY_PROCESSED_FLAG) != 0;
+ }
+
+ public static boolean skipRewritingGeneratedNodes(org.eclipse.jdt.core.dom.ASTNode node) throws Exception {
+ return ((Boolean) node.getClass().getField("$isGenerated").get(node)).booleanValue();
+ }
+
+ public static void setIsGeneratedFlag(org.eclipse.jdt.core.dom.ASTNode domNode,
+ org.eclipse.jdt.internal.compiler.ast.ASTNode internalNode) throws Exception {
+
+ if (internalNode == null || domNode == null) return;
+ boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(internalNode) != null;
+ if (isGenerated) domNode.getClass().getField("$isGenerated").set(domNode, true);
+ }
+
+ public static void setIsGeneratedFlagForName(org.eclipse.jdt.core.dom.Name name, Object internalNode) throws Exception {
+ if (internalNode instanceof org.eclipse.jdt.internal.compiler.ast.ASTNode) {
+ boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get((org.eclipse.jdt.internal.compiler.ast.ASTNode) internalNode) != null;
+ if (isGenerated) name.getClass().getField("$isGenerated").set(name, true);
+ }
+ }
+
+ public static RewriteEvent[] listRewriteHandleGeneratedMethods(RewriteEvent parent) {
+ RewriteEvent[] children = parent.getChildren();
+ List newChildren = new ArrayList();
+ List modifiedChildren = new ArrayList();
+ for (int i = 0; i < children.length; i++) {
+ RewriteEvent child = children[i];
+ boolean isGenerated = isGenerated((org.eclipse.jdt.core.dom.ASTNode) child.getOriginalValue());
+ if (isGenerated) {
+ boolean isReplacedOrRemoved = child.getChangeKind() == RewriteEvent.REPLACED || child.getChangeKind() == RewriteEvent.REMOVED;
+ boolean convertingFromMethod = child.getOriginalValue() instanceof org.eclipse.jdt.core.dom.MethodDeclaration;
+ if (isReplacedOrRemoved && convertingFromMethod && child.getNewValue() != null) {
+ modifiedChildren.add(new NodeRewriteEvent(null, child.getNewValue()));
+ }
+ } else {
+ newChildren.add(child);
+ }
+ }
+ // Since Eclipse doesn't honor the "insert at specified location" for already existing members,
+ // we'll just add them last
+ newChildren.addAll(modifiedChildren);
+ return newChildren.toArray(new RewriteEvent[newChildren.size()]);
+ }
+
+ public static int getTokenEndOffsetFixed(TokenScanner scanner, int token, int startOffset, Object domNode) throws CoreException {
+ boolean isGenerated = false;
+ try {
+ isGenerated = (Boolean) domNode.getClass().getField("$isGenerated").get(domNode);
+ } catch (Exception e) {
+ // If this fails, better to break some refactor scripts than to crash eclipse.
+ }
+ if (isGenerated) return -1;
+ return scanner.getTokenEndOffset(token, startOffset);
+ }
+
+ public static IMethod[] removeGeneratedMethods(IMethod[] methods) throws Exception {
+ List result = new ArrayList();
+ for (IMethod m : methods) {
+ if (m.getNameRange().getLength() > 0 && !m.getNameRange().equals(m.getSourceRange())) result.add(m);
+ }
+ return result.size() == methods.length ? methods : result.toArray(new IMethod[result.size()]);
+ }
+
+ public static SimpleName[] removeGeneratedSimpleNames(SimpleName[] in) throws Exception {
+ Field f = SimpleName.class.getField("$isGenerated");
+
+ int count = 0;
+ for (int i = 0; i < in.length; i++) {
+ if (in[i] == null || !((Boolean)f.get(in[i])).booleanValue()) count++;
+ }
+ if (count == in.length) return in;
+ SimpleName[] newSimpleNames = new SimpleName[count];
+ count = 0;
+ for (int i = 0; i < in.length; i++) {
+ if (in[i] == null || !((Boolean)f.get(in[i])).booleanValue()) newSimpleNames[count++] = in[i];
+ }
+ return newSimpleNames;
+ }
+
+ public static Annotation[] convertAnnotations(Annotation[] out, IAnnotatable annotatable) {
+ IAnnotation[] in;
+
+ try {
+ in = annotatable.getAnnotations();
+ } catch (Exception e) {
+ return out;
+ }
+
+ if (out == null) return null;
+ int toWrite = 0;
+
+ for (int idx = 0; idx < out.length; idx++) {
+ String oName = new String(out[idx].type.getLastToken());
+ boolean found = false;
+ for (IAnnotation i : in) {
+ String name = i.getElementName();
+ int li = name.lastIndexOf('.');
+ if (li > -1) name = name.substring(li + 1);
+ if (name.equals(oName)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) out[idx] = null;
+ else toWrite++;
+ }
+
+ Annotation[] replace = out;
+ if (toWrite < out.length) {
+ replace = new Annotation[toWrite];
+ int idx = 0;
+ for (int i = 0; i < out.length; i++) {
+ if (out[i] == null) continue;
+ replace[idx++] = out[i];
+ }
+ }
+
+ return replace;
+ }
+ }
+}
diff --git a/src/launch/lombok/launch/ShadowClassLoader.java b/src/launch/lombok/launch/ShadowClassLoader.java
index 79809b2f..b883bd71 100644
--- a/src/launch/lombok/launch/ShadowClassLoader.java
+++ b/src/launch/lombok/launch/ShadowClassLoader.java
@@ -211,24 +211,26 @@ class ShadowClassLoader extends ClassLoader {
if (!location.isFile() || !location.canRead()) return null;
- String absolutePath; {
+ File absoluteFile; {
try {
- absolutePath = location.getCanonicalPath();
+ absoluteFile = location.getCanonicalFile();
} catch (Exception e) {
- absolutePath = location.getAbsolutePath();
+ absoluteFile = location.getAbsoluteFile();
}
}
- List jarContents = getOrMakeJarListing(absolutePath);
+ List jarContents = getOrMakeJarListing(absoluteFile.getAbsolutePath());
+
+ String absoluteUri = absoluteFile.toURI().toString();
try {
if (jarContents.contains(altName)) {
- return new URI("jar:file:" + absolutePath + "!/" + altName).toURL();
+ return new URI("jar:" + absoluteUri + "!/" + altName).toURL();
}
} catch (Exception e) {}
try {
if (jarContents.contains(name)) {
- return new URI("jar:file:" + absolutePath + "!/" + name).toURL();
+ return new URI("jar:" + absoluteUri + "!/" + name).toURL();
}
} catch(Exception e) {}
diff --git a/test/core/src/lombok/AbstractRunTests.java b/test/core/src/lombok/AbstractRunTests.java
index a1f535b4..85d4d4f3 100644
--- a/test/core/src/lombok/AbstractRunTests.java
+++ b/test/core/src/lombok/AbstractRunTests.java
@@ -75,7 +75,7 @@ public abstract class AbstractRunTests {
}
});
- transformCode(messages, writer, file);
+ transformCode(messages, writer, file, sourceDirectives.getSpecifiedEncoding());
compare(file.getName(), expected, writer.toString(), messages, params.printErrors(), sourceDirectives.isSkipCompareContent() || expected.isSkipCompareContent());
return true;
@@ -97,7 +97,7 @@ public abstract class AbstractRunTests {
return 8;
}
- protected abstract void transformCode(Collection messages, StringWriter result, File file) throws Throwable;
+ protected abstract void transformCode(Collection messages, StringWriter result, File file, String encoding) throws Throwable;
protected String readFile(File file) throws IOException {
BufferedReader reader;
diff --git a/test/core/src/lombok/LombokTestSource.java b/test/core/src/lombok/LombokTestSource.java
index 402001e9..f31d7be7 100644
--- a/test/core/src/lombok/LombokTestSource.java
+++ b/test/core/src/lombok/LombokTestSource.java
@@ -49,7 +49,8 @@ public class LombokTestSource {
private final boolean skipCompareContent;
private final int versionLowerLimit, versionUpperLimit;
private final ConfigurationResolver configuration;
-
+ private final String specifiedEncoding;
+
public boolean versionWithinLimit(int version) {
return version >= versionLowerLimit && version <= versionUpperLimit;
}
@@ -74,6 +75,10 @@ public class LombokTestSource {
return skipCompareContent;
}
+ public String getSpecifiedEncoding() {
+ return specifiedEncoding;
+ }
+
public ConfigurationResolver getConfiguration() {
return configuration;
}
@@ -123,6 +128,7 @@ public class LombokTestSource {
int versionUpper = Integer.MAX_VALUE;
boolean ignore = false;
boolean skipCompareContent = false;
+ String encoding = null;
for (String directive : directives) {
directive = directive.trim();
@@ -154,10 +160,15 @@ public class LombokTestSource {
continue;
}
+ if (lc.startsWith("encoding:")) {
+ encoding = directive.substring(9).trim();
+ continue;
+ }
+
Assert.fail("Directive line \"" + directive + "\" in '" + file.getAbsolutePath() + "' invalid: unrecognized directive.");
throw new RuntimeException();
}
-
+ this.specifiedEncoding = encoding;
this.versionLowerLimit = versionLower;
this.versionUpperLimit = versionUpper;
this.ignore = ignore;
@@ -194,13 +205,17 @@ public class LombokTestSource {
}
public static LombokTestSource read(File sourceFolder, File messagesFolder, String fileName) throws IOException {
+ return read0(sourceFolder, messagesFolder, fileName, "UTF-8");
+ }
+
+ private static LombokTestSource read0(File sourceFolder, File messagesFolder, String fileName, String encoding) throws IOException {
StringBuilder content = null;
List directives = new ArrayList();
File sourceFile = new File(sourceFolder, fileName);
if (sourceFile.exists()) {
@Cleanup val rawIn = new FileInputStream(sourceFile);
- BufferedReader in = new BufferedReader(new InputStreamReader(rawIn, "UTF-8"));
+ BufferedReader in = new BufferedReader(new InputStreamReader(rawIn, encoding));
for (String i = in.readLine(); i != null; i = in.readLine()) {
if (content != null) {
content.append(i).append("\n");
@@ -234,6 +249,15 @@ public class LombokTestSource {
}
}
- return new LombokTestSource(sourceFile, content.toString(), messages, directives);
+ LombokTestSource source = new LombokTestSource(sourceFile, content.toString(), messages, directives);
+ String specifiedEncoding = source.getSpecifiedEncoding();
+
+ // The source file has an 'encoding' header to test encoding issues. Of course, reading the encoding header
+ // requires knowing the encoding of the file first. In practice we get away with it, because UTF-8 and US-ASCII are compatible enough.
+ // The fix is therefore to read in as UTF-8 initially, and if the file requests that it should be read as another encoding, toss it all
+ // and reread that way.
+
+ if (specifiedEncoding == null || specifiedEncoding.equalsIgnoreCase(encoding)) return source;
+ return read0(sourceFolder, messagesFolder, fileName, specifiedEncoding);
}
}
diff --git a/test/core/src/lombok/RunTestsViaDelombok.java b/test/core/src/lombok/RunTestsViaDelombok.java
index 6a08642b..1482c865 100644
--- a/test/core/src/lombok/RunTestsViaDelombok.java
+++ b/test/core/src/lombok/RunTestsViaDelombok.java
@@ -34,10 +34,10 @@ public class RunTestsViaDelombok extends AbstractRunTests {
private Delombok delombok = new Delombok();
@Override
- public void transformCode(Collection messages, StringWriter result, final File file) throws Throwable {
+ public void transformCode(Collection messages, StringWriter result, final File file, String encoding) throws Throwable {
delombok.setVerbose(false);
delombok.setForceProcess(true);
- delombok.setCharset("UTF-8");
+ delombok.setCharset(encoding == null ? "UTF-8" : encoding);
delombok.setDiagnosticsListener(new CapturingDiagnosticListener(file, messages));
diff --git a/test/core/src/lombok/RunTestsViaEcj.java b/test/core/src/lombok/RunTestsViaEcj.java
index 90dabcde..2b83b296 100644
--- a/test/core/src/lombok/RunTestsViaEcj.java
+++ b/test/core/src/lombok/RunTestsViaEcj.java
@@ -93,7 +93,7 @@ public class RunTestsViaEcj extends AbstractRunTests {
}
@Override
- public void transformCode(Collection messages, StringWriter result, File file) throws Throwable {
+ public void transformCode(Collection messages, StringWriter result, File file, String encoding) throws Throwable {
final AtomicReference compilationResult_ = new AtomicReference();
final AtomicReference compilationUnit_ = new AtomicReference();
ICompilerRequestor bitbucketRequestor = new ICompilerRequestor() {
@@ -103,7 +103,7 @@ public class RunTestsViaEcj extends AbstractRunTests {
};
String source = readFile(file);
- final CompilationUnit sourceUnit = new CompilationUnit(source.toCharArray(), file.getName(), "UTF-8");
+ final CompilationUnit sourceUnit = new CompilationUnit(source.toCharArray(), file.getName(), encoding == null ? "UTF-8" : encoding);
Compiler ecjCompiler = new Compiler(createFileSystem(file), ecjErrorHandlingPolicy(), ecjCompilerOptions(), bitbucketRequestor, new DefaultProblemFactory(Locale.ENGLISH)) {
@Override protected synchronized void addCompilationUnit(ICompilationUnit inUnit, CompilationUnitDeclaration parsedUnit) {
diff --git a/test/transform/resource/after-delombok/EncodingUsAscii.java b/test/transform/resource/after-delombok/EncodingUsAscii.java
new file mode 100644
index 00000000..a9e6b1a5
--- /dev/null
+++ b/test/transform/resource/after-delombok/EncodingUsAscii.java
@@ -0,0 +1,9 @@
+//ENCODING: US-ASCII
+class EncodingUsAscii {
+ String foo\u0e51\u0e51 = "\016\t\b ";
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "EncodingUsAscii(foo\u0e51\u0e51=" + this.foo\u0e51\u0e51 + ")";
+ }
+}
\ No newline at end of file
diff --git a/test/transform/resource/after-delombok/EncodingUtf8.java b/test/transform/resource/after-delombok/EncodingUtf8.java
new file mode 100644
index 00000000..9ae3e30a
--- /dev/null
+++ b/test/transform/resource/after-delombok/EncodingUtf8.java
@@ -0,0 +1,8 @@
+class EncodingUtf8 {
+ String foo๑๑ = "\016\t\b ";
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "EncodingUtf8(foo๑๑=" + this.foo๑๑ + ")";
+ }
+}
\ No newline at end of file
diff --git a/test/transform/resource/after-ecj/EncodingUsAscii.java b/test/transform/resource/after-ecj/EncodingUsAscii.java
new file mode 100644
index 00000000..b66feb0c
--- /dev/null
+++ b/test/transform/resource/after-ecj/EncodingUsAscii.java
@@ -0,0 +1 @@
+//ignore: This test serves to check what happens with 'weird' characters when you use delombok. It's just not relevant for ecj.
diff --git a/test/transform/resource/after-ecj/EncodingUtf8.java b/test/transform/resource/after-ecj/EncodingUtf8.java
new file mode 100644
index 00000000..5a7a1644
--- /dev/null
+++ b/test/transform/resource/after-ecj/EncodingUtf8.java
@@ -0,0 +1,9 @@
+@lombok.ToString class EncodingUtf8 {
+ String foo๑๑ = "\t\b ";
+ EncodingUtf8() {
+ super();
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (("EncodingUtf8(foo๑๑=" + this.foo๑๑) + ")");
+ }
+}
\ No newline at end of file
diff --git a/test/transform/resource/before/EncodingUsAscii.java b/test/transform/resource/before/EncodingUsAscii.java
new file mode 100644
index 00000000..dbcd150b
--- /dev/null
+++ b/test/transform/resource/before/EncodingUsAscii.java
@@ -0,0 +1,5 @@
+//ENCODING: US-ASCII
+@lombok.ToString
+class EncodingUsAscii {
+ String foo\u0e51\u0e51 = "\u000e \10 ";
+}
\ No newline at end of file
diff --git a/test/transform/resource/before/EncodingUtf8.java b/test/transform/resource/before/EncodingUtf8.java
new file mode 100644
index 00000000..75b0ee00
--- /dev/null
+++ b/test/transform/resource/before/EncodingUtf8.java
@@ -0,0 +1,5 @@
+//ENCODING: UTF-8
+@lombok.ToString
+class EncodingUtf8 {
+ String foo\u0e51๑ = "\u000e \10 ";
+}
\ No newline at end of file