aboutsummaryrefslogtreecommitdiff
path: root/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java
diff options
context:
space:
mode:
authorReinier Zwitserloot <reinier@zwitserloot.com>2011-05-16 22:32:27 +0200
committerReinier Zwitserloot <reinier@zwitserloot.com>2011-05-16 22:32:27 +0200
commit75e81604f390b7d9d14b50a86d9f991de30eb1b3 (patch)
tree1653754f99d0c7cd2d2ddb9632b128b1fc803850 /src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java
parent8b2ee73cc46a8629d61abd0dde70ecb0748e2b0e (diff)
downloadlombok-75e81604f390b7d9d14b50a86d9f991de30eb1b3.tar.gz
lombok-75e81604f390b7d9d14b50a86d9f991de30eb1b3.tar.bz2
lombok-75e81604f390b7d9d14b50a86d9f991de30eb1b3.zip
Split up PatchVal into the ecj and eclipse bits; in ecj you'd just get NoSuchClassErrors.
Diffstat (limited to 'src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java')
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java264
1 files changed, 264 insertions, 0 deletions
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java
new file mode 100644
index 00000000..3fb407b9
--- /dev/null
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright © 2010-2011 Reinier Zwitserloot, Roel Spilker and Robbert Jan Grootjans.
+ *
+ * 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.
+ *
+ * Thanks to Stephen Haberman for a patch to solve some NPEs in Eclipse.
+ */
+package lombok.eclipse.agent;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import lombok.Lombok;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.IExtendedModifier;
+import org.eclipse.jdt.core.dom.MarkerAnnotation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.parser.Parser;
+
+public class PatchValEclipse {
+ public static void copyInitializationOfForEachIterable(Parser parser) {
+ ASTNode[] astStack;
+ int astPtr;
+ try {
+ astStack = (ASTNode[]) Reflection.astStackField.get(parser);
+ astPtr = (Integer) Reflection.astPtrField.get(parser);
+ } catch (Exception e) {
+ // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
+ return;
+ }
+
+ ForeachStatement foreachDecl = (ForeachStatement) astStack[astPtr];
+ ASTNode init = foreachDecl.collection;
+ if (init == null) return;
+ if (foreachDecl.elementVariable != null && foreachDecl.elementVariable.type instanceof SingleTypeReference) {
+ SingleTypeReference ref = (SingleTypeReference) foreachDecl.elementVariable.type;
+ if (ref.token == null || ref.token.length != 3 || ref.token[0] != 'v' || ref.token[1] != 'a' || ref.token[2] != 'l') return;
+ } else return;
+
+ try {
+ if (Reflection.iterableCopyField != null) Reflection.iterableCopyField.set(foreachDecl.elementVariable, init);
+ } catch (Exception e) {
+ // In ecj mode this field isn't there and we don't need the copy anyway, so, we ignore the exception.
+ }
+ }
+
+ public static void copyInitializationOfLocalDeclaration(Parser parser) {
+ ASTNode[] astStack;
+ int astPtr;
+ try {
+ astStack = (ASTNode[]) Reflection.astStackField.get(parser);
+ astPtr = (Integer)Reflection.astPtrField.get(parser);
+ } catch (Exception e) {
+ // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
+ return;
+ }
+ AbstractVariableDeclaration variableDecl = (AbstractVariableDeclaration) astStack[astPtr];
+ if (!(variableDecl instanceof LocalDeclaration)) return;
+ ASTNode init = variableDecl.initialization;
+ if (init == null) return;
+ if (variableDecl.type instanceof SingleTypeReference) {
+ SingleTypeReference ref = (SingleTypeReference) variableDecl.type;
+ if (ref.token == null || ref.token.length != 3 || ref.token[0] != 'v' || ref.token[1] != 'a' || ref.token[2] != 'l') return;
+ } else return;
+
+ try {
+ if (Reflection.initCopyField != null) Reflection.initCopyField.set(variableDecl, init);
+ } catch (Exception e) {
+ // In ecj mode this field isn't there and we don't need the copy anyway, so, we ignore the exception.
+ }
+ }
+
+ public static void addFinalAndValAnnotationToVariableDeclarationStatement(Object converter, VariableDeclarationStatement out, LocalDeclaration in) {
+ // First check that 'in' has the final flag on, and a @val / @lombok.val annotation.
+ if ((in.modifiers & ClassFileConstants.AccFinal) == 0) return;
+ if (in.annotations == null) return;
+ boolean found = false;
+ Annotation valAnnotation = null;
+
+ for (Annotation ann : in.annotations) {
+ if (ann.type instanceof SingleTypeReference) {
+ if (PatchVal.matches("val", ((SingleTypeReference)ann.type).token)) {
+ found = true;
+ valAnnotation = ann;
+ break;
+ }
+ }
+ if (ann.type instanceof QualifiedTypeReference) {
+ char[][] tokens = ((QualifiedTypeReference)ann.type).tokens;
+ if (tokens != null && tokens.length == 2 && PatchVal.matches("lombok", tokens[0]) && PatchVal.matches("val", tokens[1])) {
+ found = true;
+ valAnnotation = ann;
+ break;
+ }
+ }
+ }
+
+ if (!found) return;
+
+ // Now check that 'out' is missing either of these.
+
+ @SuppressWarnings("unchecked") List<IExtendedModifier> modifiers = out.modifiers();
+
+ if (modifiers == null) return; // This is null only if the project is 1.4 or less. Lombok doesn't work in that.
+ boolean finalIsPresent = false;
+ boolean valIsPresent = false;
+
+ for (Object present : modifiers) {
+ if (present instanceof Modifier) {
+ ModifierKeyword keyword = ((Modifier)present).getKeyword();
+ if (keyword == null) continue;
+ if (keyword.toFlagValue() == Modifier.FINAL) finalIsPresent = true;
+ }
+
+ if (present instanceof org.eclipse.jdt.core.dom.Annotation) {
+ Name typeName = ((org.eclipse.jdt.core.dom.Annotation) present).getTypeName();
+ if (typeName != null) {
+ String fullyQualifiedName = typeName.getFullyQualifiedName();
+ if ("val".equals(fullyQualifiedName) || "lombok.val".equals(fullyQualifiedName)) {
+ valIsPresent = true;
+ }
+ }
+ }
+ }
+
+ if (!finalIsPresent) {
+ modifiers.add(
+ createModifier(out.getAST(), ModifierKeyword.FINAL_KEYWORD, valAnnotation.sourceStart, valAnnotation.sourceEnd));
+ }
+
+ if (!valIsPresent) {
+ MarkerAnnotation newAnnotation = createValAnnotation(out.getAST(), valAnnotation, valAnnotation.sourceStart, valAnnotation.sourceEnd);
+ try {
+ Reflection.astConverterRecordNodes.invoke(converter, newAnnotation, valAnnotation);
+ Reflection.astConverterRecordNodes.invoke(converter, newAnnotation.getTypeName(), valAnnotation.type);
+ } catch (IllegalAccessException e) {
+ Lombok.sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ Lombok.sneakyThrow(e.getCause());
+ }
+ modifiers.add(newAnnotation);
+ }
+ }
+
+ public static Modifier createModifier(AST ast, ModifierKeyword keyword, int start, int end) {
+ Modifier modifier = null;
+ try {
+ modifier = Reflection.modifierConstructor.newInstance(ast);
+ } catch (InstantiationException e) {
+ Lombok.sneakyThrow(e);
+ } catch (IllegalAccessException e) {
+ Lombok.sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ Lombok.sneakyThrow(e);
+ }
+
+ if (modifier != null) {
+ modifier.setKeyword(keyword);
+ modifier.setSourceRange(start, end - start + 1);
+ }
+ return modifier;
+ }
+
+ public static MarkerAnnotation createValAnnotation(AST ast, Annotation original, int start, int end) {
+ MarkerAnnotation out = null;
+ try {
+ out = Reflection.markerAnnotationConstructor.newInstance(ast);
+ } catch (InstantiationException e) {
+ Lombok.sneakyThrow(e);
+ } catch (IllegalAccessException e) {
+ Lombok.sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ Lombok.sneakyThrow(e);
+ }
+
+ if (out != null) {
+ if (original.type instanceof SingleTypeReference) {
+ out.setTypeName(ast.newSimpleName("val"));
+ } else {
+ out.setTypeName(ast.newQualifiedName(ast.newSimpleName("lombok"), ast.newSimpleName("val")));
+ }
+ out.setSourceRange(start, end - start + 1);
+ }
+
+ return out;
+ }
+
+ public static final class Reflection {
+ private static final Field initCopyField, iterableCopyField;
+ private static final Field astStackField, astPtrField;
+ private static final Constructor<Modifier> modifierConstructor;
+ private static final Constructor<MarkerAnnotation> markerAnnotationConstructor;
+ private static final Method astConverterRecordNodes;
+
+ static {
+ Field a = null, b = null, c = null, d = null;
+ Constructor<Modifier> f = null;
+ Constructor<MarkerAnnotation> g = null;
+ Method h = null;
+
+ try {
+ a = LocalDeclaration.class.getDeclaredField("$initCopy");
+ b = LocalDeclaration.class.getDeclaredField("$iterableCopy");
+ } catch (Throwable t) {
+ //ignore - no $initCopy exists when running in ecj.
+ }
+
+ try {
+ c = Parser.class.getDeclaredField("astStack");
+ c.setAccessible(true);
+ d = Parser.class.getDeclaredField("astPtr");
+ d.setAccessible(true);
+ f = Modifier.class.getDeclaredConstructor(AST.class);
+ f.setAccessible(true);
+ g = MarkerAnnotation.class.getDeclaredConstructor(AST.class);
+ g.setAccessible(true);
+ Class<?> z = Class.forName("org.eclipse.jdt.core.dom.ASTConverter");
+ h = z.getDeclaredMethod("recordNodes", org.eclipse.jdt.core.dom.ASTNode.class, org.eclipse.jdt.internal.compiler.ast.ASTNode.class);
+ h.setAccessible(true);
+ } catch (Exception e) {
+ // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
+ }
+
+ initCopyField = a;
+ iterableCopyField = b;
+ astStackField = c;
+ astPtrField = d;
+ modifierConstructor = f;
+ markerAnnotationConstructor = g;
+ astConverterRecordNodes = h;
+ }
+ }
+}