aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/lombok/Data.java4
-rw-r--r--src/core/lombok/NonNull.java16
-rw-r--r--src/core/lombok/Value.java58
-rw-r--r--src/core/lombok/bytecode/PreventNullAnalysisRemover.java9
-rw-r--r--src/core/lombok/bytecode/SneakyThrowsRemover.java119
-rw-r--r--src/core/lombok/core/AST.java61
-rw-r--r--src/core/lombok/core/AnnotationProcessor.java7
-rw-r--r--src/core/lombok/core/AnnotationValues.java19
-rw-r--r--src/core/lombok/core/ImportList.java43
-rw-r--r--src/core/lombok/core/LombokInternalAliasing.java52
-rw-r--r--src/core/lombok/core/LombokNode.java74
-rw-r--r--src/core/lombok/core/PostCompiler.java4
-rw-r--r--src/core/lombok/core/TransformationsUtil.java25
-rw-r--r--src/core/lombok/core/TypeLibrary.java72
-rw-r--r--src/core/lombok/core/TypeResolver.java95
-rw-r--r--src/core/lombok/core/Version.java4
-rw-r--r--src/core/lombok/core/handlers/HandlerUtil.java48
-rw-r--r--src/core/lombok/eclipse/EclipseAST.java48
-rw-r--r--src/core/lombok/eclipse/EclipseAnnotationHandler.java2
-rw-r--r--src/core/lombok/eclipse/EclipseAstProblemView.java9
-rw-r--r--src/core/lombok/eclipse/EclipseImportList.java136
-rw-r--r--src/core/lombok/eclipse/HandlerLibrary.java41
-rw-r--r--src/core/lombok/eclipse/TransformEclipseAST.java2
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java94
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilder.java376
-rw-r--r--src/core/lombok/eclipse/handlers/HandleConstructor.java55
-rw-r--r--src/core/lombok/eclipse/handlers/HandleData.java3
-rw-r--r--src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java23
-rw-r--r--src/core/lombok/eclipse/handlers/HandleFieldDefaults.java10
-rw-r--r--src/core/lombok/eclipse/handlers/HandleGetter.java122
-rw-r--r--src/core/lombok/eclipse/handlers/HandleLog.java15
-rw-r--r--src/core/lombok/eclipse/handlers/HandleSetter.java4
-rw-r--r--src/core/lombok/eclipse/handlers/HandleSneakyThrows.java24
-rw-r--r--src/core/lombok/eclipse/handlers/HandleToString.java26
-rw-r--r--src/core/lombok/eclipse/handlers/HandleValue.java5
-rw-r--r--src/core/lombok/eclipse/handlers/HandleWither.java2
-rw-r--r--src/core/lombok/eclipse/handlers/NonNullHandler.java147
-rw-r--r--src/core/lombok/experimental/Builder.java137
-rw-r--r--src/core/lombok/experimental/Value.java2
-rw-r--r--src/core/lombok/extern/apachecommons/CommonsLog.java3
-rw-r--r--src/core/lombok/extern/java/Log.java3
-rw-r--r--src/core/lombok/extern/log4j/Log4j.java3
-rw-r--r--src/core/lombok/extern/log4j/Log4j2.java62
-rw-r--r--src/core/lombok/extern/slf4j/Slf4j.java3
-rw-r--r--src/core/lombok/extern/slf4j/XSlf4j.java3
-rw-r--r--src/core/lombok/javac/FindTypeVarScanner.java2
-rw-r--r--src/core/lombok/javac/HandlerLibrary.java32
-rw-r--r--src/core/lombok/javac/Javac6BasedLombokOptions.java3
-rw-r--r--src/core/lombok/javac/JavacAST.java89
-rw-r--r--src/core/lombok/javac/JavacAnnotationHandler.java2
-rw-r--r--src/core/lombok/javac/JavacImportList.java112
-rw-r--r--src/core/lombok/javac/JavacResolution.java4
-rw-r--r--src/core/lombok/javac/LombokOptions.java4
-rw-r--r--src/core/lombok/javac/apt/Processor.java4
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java330
-rw-r--r--src/core/lombok/javac/handlers/HandleCleanup.java1
-rw-r--r--src/core/lombok/javac/handlers/HandleConstructor.java38
-rw-r--r--src/core/lombok/javac/handlers/HandleData.java3
-rw-r--r--src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java22
-rw-r--r--src/core/lombok/javac/handlers/HandleFieldDefaults.java10
-rw-r--r--src/core/lombok/javac/handlers/HandleGetter.java73
-rw-r--r--src/core/lombok/javac/handlers/HandleLog.java17
-rw-r--r--src/core/lombok/javac/handlers/HandleSetter.java42
-rw-r--r--src/core/lombok/javac/handlers/HandleSneakyThrows.java29
-rw-r--r--src/core/lombok/javac/handlers/HandleToString.java26
-rw-r--r--src/core/lombok/javac/handlers/HandleValue.java12
-rw-r--r--src/core/lombok/javac/handlers/HandleWither.java5
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java217
-rw-r--r--src/core/lombok/javac/handlers/NonNullHandler.java148
-rw-r--r--src/delombok/lombok/delombok/LombokOptionsFactory.java15
-rw-r--r--src/delombok/lombok/delombok/PrettyCommentsPrinter.java67
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java2
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java6
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java4
-rw-r--r--src/installer/lombok/installer/IdeLocation.java6
-rw-r--r--src/installer/lombok/installer/eclipse/EclipseFinder.java22
-rw-r--r--src/installer/lombok/installer/eclipse/JbdsFinder.java70
-rw-r--r--src/installer/lombok/installer/eclipse/JbdsLocation.java45
-rw-r--r--src/installer/lombok/installer/eclipse/JbdsLocationProvider.java69
-rw-r--r--src/installer/lombok/installer/eclipse/STSFinder.java2
-rw-r--r--src/installer/lombok/installer/eclipse/jbds.pngbin0 -> 3470 bytes
-rw-r--r--src/javac-only-stubs/com/sun/tools/javac/util/Context.java (renamed from src/stubs/com/sun/tools/javac/util/Context.java)0
-rw-r--r--src/stubs/com/sun/tools/javac/parser/DocCommentScanner.java25
-rw-r--r--src/stubs/com/sun/tools/javac/parser/Scanner.java7
-rw-r--r--src/stubs/com/sun/tools/javadoc/DocCommentScanner.java25
-rw-r--r--src/utils/lombok/core/JavaIdentifiers.java57
-rw-r--r--src/utils/lombok/core/LombokImmutableList.java188
-rw-r--r--src/utils/lombok/eclipse/Eclipse.java64
-rw-r--r--src/utils/lombok/javac/CommentCatcher.java40
-rw-r--r--src/utils/lombok/javac/CommentInfo.java2
-rw-r--r--src/utils/lombok/javac/JCNoTypeFactory.java11
-rw-r--r--src/utils/lombok/javac/Javac.java367
-rw-r--r--src/utils/lombok/javac/java6/CommentCollectingParserFactory.java2
-rw-r--r--src/utils/lombok/javac/java6/CommentCollectingScanner.java5
-rw-r--r--src/utils/lombok/javac/java6/CommentCollectingScannerFactory.java37
-rw-r--r--src/utils/lombok/javac/java6/DocCommentScanner.java461
-rw-r--r--src/utils/lombok/javac/java7/CommentCollectingParserFactory.java4
-rw-r--r--src/utils/lombok/javac/java7/CommentCollectingScanner.java7
-rw-r--r--src/utils/lombok/javac/java7/CommentCollectingScannerFactory.java34
99 files changed, 4221 insertions, 718 deletions
diff --git a/src/core/lombok/Data.java b/src/core/lombok/Data.java
index ee6f2fcb..bbc8d920 100644
--- a/src/core/lombok/Data.java
+++ b/src/core/lombok/Data.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -39,7 +39,7 @@ import java.lang.annotation.Target;
* @see RequiredArgsConstructor
* @see ToString
* @see EqualsAndHashCode
- * @see lombok.experimental.Value
+ * @see lombok.Value
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
diff --git a/src/core/lombok/NonNull.java b/src/core/lombok/NonNull.java
index 5f5d8ed2..96813170 100644
--- a/src/core/lombok/NonNull.java
+++ b/src/core/lombok/NonNull.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -28,12 +28,14 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * Lombok is smart enough to translate any annotation named {@code @NonNull} in any casing and
- * with any package name to the return type of generated getters and the parameter of generated setters and constructors,
- * as well as generate the appropriate null checks in the setter and constructor.
- *
- * You can use this annotation for the purpose, though you can also use JSR305's annotation, findbugs's, pmd's, or IDEA's, or just
- * about anyone elses. As long as it is named {@code @NonNull}.
+ * If put on a parameter, lombok will insert a null-check at the start of the method / constructor's body, throwing a
+ * {@code NullPointerException} with the parameter's name as message. If put on a field, any generated method assigning
+ * a value to this field will also produce these nullchecks.
+ * <p>
+ * Note that any annotation named {@code NonNull} with any casing and any package will result in nullchecks produced for
+ * generated methods (and the annotation will be copied to the getter return type and any parameters of generated methods),
+ * but <em>only</em> this annotation, if present on a parameter, will result in a null check inserted into your otherwise
+ * handwritten method.
*
* WARNING: If the java community ever does decide on supporting a single {@code @NonNull} annotation (for example via JSR305), then
* this annotation will <strong>be deleted</strong> from the lombok package. If the need to update an import statement scares
diff --git a/src/core/lombok/Value.java b/src/core/lombok/Value.java
new file mode 100644
index 00000000..2cffe15b
--- /dev/null
+++ b/src/core/lombok/Value.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012-2013 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Generates a lot of code which fits with a class that is a representation of an immutable entity.
+ * <p>
+ * Equivalent to {@code @Getter @FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) @RequiredArgsConstructor @ToString @EqualsAndHashCode}.
+ * <p>
+ * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Value.html">the project lombok features page for &#64;Value</a>.
+ *
+ * @see lombok.Getter
+ * @see lombok.experimental.FieldDefaults
+ * @see lombok.RequiredArgsConstructor
+ * @see lombok.ToString
+ * @see lombok.EqualsAndHashCode
+ * @see lombok.Data
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.SOURCE)
+public @interface Value {
+ /**
+ * If you specify a static constructor name, then the generated constructor will be private, and
+ * instead a static factory method is created that other classes can use to create instances.
+ * We suggest the name: "of", like so:
+ *
+ * <pre>
+ * public @Data(staticConstructor = "of") class Point { final int x, y; }
+ * </pre>
+ *
+ * Default: No static constructor, instead the normal constructor is public.
+ */
+ String staticConstructor() default "";
+}
diff --git a/src/core/lombok/bytecode/PreventNullAnalysisRemover.java b/src/core/lombok/bytecode/PreventNullAnalysisRemover.java
index a3f18b40..751e691f 100644
--- a/src/core/lombok/bytecode/PreventNullAnalysisRemover.java
+++ b/src/core/lombok/bytecode/PreventNullAnalysisRemover.java
@@ -44,12 +44,12 @@ public class PreventNullAnalysisRemover implements PostCompilerTransformation {
byte[] fixedByteCode = fixJSRInlining(original);
ClassReader reader = new ClassReader(fixedByteCode);
- ClassWriter writer = new FixedClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+ ClassWriter writer = new FixedClassWriter(reader, 0);
final AtomicBoolean changesMade = new AtomicBoolean();
- class PreventNullanalysisVisitor extends MethodVisitor {
- PreventNullanalysisVisitor(MethodVisitor mv) {
+ class PreventNullAnalysisVisitor extends MethodVisitor {
+ PreventNullAnalysisVisitor(MethodVisitor mv) {
super(Opcodes.ASM4, mv);
}
@@ -61,6 +61,7 @@ public class PreventNullAnalysisRemover implements PostCompilerTransformation {
if (hit && !"(Ljava/lang/Object;)Ljava/lang/Object;".equals(desc)) hit = false;
if (hit) {
changesMade.set(true);
+ if (System.getProperty("lombok.debugAsmOnly", null) != null) super.visitMethodInsn(opcode, owner, name, desc); // DEBUG for issue 470!
} else {
super.visitMethodInsn(opcode, owner, name, desc);
}
@@ -69,7 +70,7 @@ public class PreventNullAnalysisRemover implements PostCompilerTransformation {
reader.accept(new ClassVisitor(Opcodes.ASM4, writer) {
@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
- return new PreventNullanalysisVisitor(super.visitMethod(access, name, desc, signature, exceptions));
+ return new PreventNullAnalysisVisitor(super.visitMethod(access, name, desc, signature, exceptions));
}
}, 0);
return changesMade.get() ? writer.toByteArray() : null;
diff --git a/src/core/lombok/bytecode/SneakyThrowsRemover.java b/src/core/lombok/bytecode/SneakyThrowsRemover.java
index 8143d860..914c313a 100644
--- a/src/core/lombok/bytecode/SneakyThrowsRemover.java
+++ b/src/core/lombok/bytecode/SneakyThrowsRemover.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2012 The Project Lombok Authors.
+ * Copyright (C) 2010-2013 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
@@ -32,19 +32,21 @@ import org.mangosdk.spi.ProviderFor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@ProviderFor(PostCompilerTransformation.class)
public class SneakyThrowsRemover implements PostCompilerTransformation {
- @Override public byte[] applyTransformations(byte[] original, String fileName, DiagnosticsReceiver diagnostics) {
+ @Override public byte[] applyTransformations(byte[] original, String fileName, final DiagnosticsReceiver diagnostics) {
if (!new ClassFileMetaData(original).usesMethod("lombok/Lombok", "sneakyThrow")) return null;
byte[] fixedByteCode = fixJSRInlining(original);
ClassReader reader = new ClassReader(fixedByteCode);
- ClassWriter writer = new FixedClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+ ClassWriter writer = new ClassWriter(reader, 0);
final AtomicBoolean changesMade = new AtomicBoolean();
@@ -53,6 +55,8 @@ public class SneakyThrowsRemover implements PostCompilerTransformation {
super(Opcodes.ASM4, mv);
}
+ private boolean methodInsnQueued = false;
+
@Override public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (
opcode == Opcodes.INVOKESTATIC &&
@@ -60,12 +64,117 @@ public class SneakyThrowsRemover implements PostCompilerTransformation {
"lombok/Lombok".equals(owner) &&
"(Ljava/lang/Throwable;)Ljava/lang/RuntimeException;".equals(desc)) {
- changesMade.set(true);
- super.visitInsn(Opcodes.ATHROW);
+ if (System.getProperty("lombok.debugAsmOnly", null) != null) {
+ super.visitMethodInsn(opcode, owner, name, desc); // DEBUG for issue 470!
+ } else {
+ methodInsnQueued = true;
+ }
} else {
super.visitMethodInsn(opcode, owner, name, desc);
}
}
+
+ private void handleQueue() {
+ if (!methodInsnQueued) return;
+ super.visitMethodInsn(Opcodes.INVOKESTATIC, "lombok/Lombok", "sneakyThrow", "(Ljava/lang/Throwable;)Ljava/lang/RuntimeException;");
+ methodInsnQueued = false;
+ diagnostics.addWarning("Proper usage is: throw lombok.Lombok.sneakyThrow(someException);. You did not 'throw' it. Because of this, the call to sneakyThrow " +
+ "remains in your classfile and you will need lombok.jar on the classpath at runtime.");
+ }
+
+ @Override public void visitInsn(int arg0) {
+ if (methodInsnQueued && arg0 == Opcodes.ATHROW) {
+ changesMade.set(true);
+ // As expected, the required ATHROW. We can now safely 'eat' the previous call.
+ methodInsnQueued = false;
+ }
+ handleQueue();
+ super.visitInsn(arg0);
+ }
+ @Override public void visitFrame(int arg0, int arg1, Object[] arg2, int arg3, Object[] arg4) {
+ handleQueue();
+ super.visitFrame(arg0, arg1, arg2, arg3, arg4);
+ }
+
+ @Override public void visitIincInsn(int arg0, int arg1) {
+ handleQueue();
+ super.visitIincInsn(arg0, arg1);
+ }
+
+ @Override public void visitFieldInsn(int arg0, String arg1, String arg2, String arg3) {
+ handleQueue();
+ super.visitFieldInsn(arg0, arg1, arg2, arg3);
+ }
+
+ @Override public void visitIntInsn(int arg0, int arg1) {
+ handleQueue();
+ super.visitIntInsn(arg0, arg1);
+ }
+
+ @Override public void visitEnd() {
+ handleQueue();
+ super.visitEnd();
+ }
+
+ @Override public void visitInvokeDynamicInsn(String arg0, String arg1, Handle arg2, Object... arg3) {
+ handleQueue();
+ super.visitInvokeDynamicInsn(arg0, arg1, arg2, arg3);
+ }
+
+ @Override public void visitLabel(Label arg0) {
+ handleQueue();
+ super.visitLabel(arg0);
+ }
+
+ @Override public void visitJumpInsn(int arg0, Label arg1) {
+ handleQueue();
+ super.visitJumpInsn(arg0, arg1);
+ }
+
+ @Override public void visitLdcInsn(Object arg0) {
+ handleQueue();
+ super.visitLdcInsn(arg0);
+ }
+
+ @Override public void visitLocalVariable(String arg0, String arg1, String arg2, Label arg3, Label arg4, int arg5) {
+ handleQueue();
+ super.visitLocalVariable(arg0, arg1, arg2, arg3, arg4, arg5);
+ }
+
+ @Override public void visitMaxs(int arg0, int arg1) {
+ handleQueue();
+ super.visitMaxs(arg0, arg1);
+ }
+
+ @Override public void visitLookupSwitchInsn(Label arg0, int[] arg1, Label[] arg2) {
+ handleQueue();
+ super.visitLookupSwitchInsn(arg0, arg1, arg2);
+ }
+
+ @Override public void visitMultiANewArrayInsn(String arg0, int arg1) {
+ handleQueue();
+ super.visitMultiANewArrayInsn(arg0, arg1);
+ }
+
+ @Override public void visitVarInsn(int arg0, int arg1) {
+ handleQueue();
+ super.visitVarInsn(arg0, arg1);
+ }
+
+ @Override public void visitTryCatchBlock(Label arg0, Label arg1, Label arg2, String arg3) {
+ handleQueue();
+ super.visitTryCatchBlock(arg0, arg1, arg2, arg3);
+ }
+
+ @Override public void visitTableSwitchInsn(int arg0, int arg1, Label arg2, Label... arg3) {
+ handleQueue();
+ super.visitTableSwitchInsn(arg0, arg1, arg2, arg3);
+ }
+
+ @Override public void visitTypeInsn(int arg0, String arg1) {
+ handleQueue();
+ super.visitTypeInsn(arg0, arg1);
+ }
}
reader.accept(new ClassVisitor(Opcodes.ASM4, writer) {
diff --git a/src/core/lombok/core/AST.java b/src/core/lombok/core/AST.java
index 68f36412..6fed0252 100644
--- a/src/core/lombok/core/AST.java
+++ b/src/core/lombok/core/AST.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -30,7 +30,6 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
@@ -54,15 +53,15 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
private L top;
private final String fileName;
private final String packageDeclaration;
- private final Collection<String> imports;
- Map<N, Void> identityDetector = new IdentityHashMap<N, Void>();
+ private final ImportList imports;
+ Map<N, N> identityDetector = new IdentityHashMap<N, N>();
private Map<N, L> nodeMap = new IdentityHashMap<N, L>();
private boolean changed = false;
- protected AST(String fileName, String packageDeclaration, Collection<String> imports) {
+ protected AST(String fileName, String packageDeclaration, ImportList imports) {
this.fileName = fileName == null ? "(unknown).java" : fileName;
this.packageDeclaration = packageDeclaration;
- this.imports = Collections.unmodifiableCollection(new ArrayList<String>(imports));
+ this.imports = imports;
}
public void setChanged() {
@@ -96,7 +95,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
*
* Example: "java.util.IOException".
*/
- public final Collection<String> getImportStatements() {
+ public final ImportList getImportList() {
return imports;
}
@@ -106,7 +105,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
*/
protected L putInMap(L node) {
nodeMap.put(node.get(), node);
- identityDetector.put(node.get(), null);
+ identityDetector.put(node.get(), node.get());
return node;
}
@@ -118,7 +117,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
/** Clears the registry that avoids endless loops, and empties the node map. The existing node map
* object is left untouched, and instead a new map is created. */
protected void clearState() {
- identityDetector = new IdentityHashMap<N, Void>();
+ identityDetector = new IdentityHashMap<N, N>();
nodeMap = new IdentityHashMap<N, L>();
}
@@ -128,9 +127,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
* case you should do nothing lest the AST build process loops endlessly.
*/
protected boolean setAndGetAsHandled(N node) {
- if (identityDetector.containsKey(node)) return true;
- identityDetector.put(node, null);
- return false;
+ return identityDetector.put(node, node) != null;
}
public String getFileName() {
@@ -147,6 +144,24 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
return nodeMap.get(node);
}
+ /**
+ * Returns the JLS spec version that the compiler uses to parse and compile this AST.
+ * For example, if -source 1.6 is on the command line, this will return {@code 6}.
+ */
+ public int getSourceVersion() {
+ return 6;
+ }
+
+ /**
+ * Returns the latest version of the java language specification supported by the host compiler.
+ * For example, if compiling with javac v1.7, this returns {@code 7}.
+ *
+ * NB: Even if -source (lower than maximum) is specified, this method still returns the maximum supported number.
+ */
+ public int getLatestJavaSpecSupported() {
+ return 6;
+ }
+
@SuppressWarnings({"unchecked", "rawtypes"})
L replaceNewWithExistingOld(Map<N, L> oldNodes, L newNode) {
L oldNode = oldNodes.get(newNode.get());
@@ -159,8 +174,8 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
oldChild.parent = targetNode;
}
- targetNode.children.clear();
- ((List)targetNode.children).addAll(children);
+ targetNode.children = LombokImmutableList.copyOf(children);
+
return targetNode;
}
@@ -217,14 +232,12 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
}
}
- for (Class<?> statementType : getStatementTypes()) {
- if (statementType.isAssignableFrom(t)) {
- f.setAccessible(true);
- fields.add(new FieldAccess(f, dim));
- break;
- }
+ if (shouldDrill(c, t, f.getName())) {
+ f.setAccessible(true);
+ fields.add(new FieldAccess(f, dim));
}
}
+
getFields(c.getSuperclass(), fields);
}
@@ -241,6 +254,14 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
* though some platforms (such as Eclipse) group these under one common supertype. */
protected abstract Collection<Class<? extends N>> getStatementTypes();
+ protected boolean shouldDrill(Class<?> parentType, Class<?> childType, String fieldName) {
+ for (Class<?> statementType : getStatementTypes()) {
+ if (statementType.isAssignableFrom(childType)) return true;
+ }
+
+ return false;
+ }
+
/**
* buildTree implementation that uses reflection to find all child nodes by way of inspecting
* the fields. */
diff --git a/src/core/lombok/core/AnnotationProcessor.java b/src/core/lombok/core/AnnotationProcessor.java
index 8c5f7fba..e9cf3891 100644
--- a/src/core/lombok/core/AnnotationProcessor.java
+++ b/src/core/lombok/core/AnnotationProcessor.java
@@ -164,12 +164,9 @@ public class AnnotationProcessor extends AbstractProcessor {
}
}
- boolean handled = false;
- for (ProcessorDescriptor proc : active) {
- handled |= proc.process(annotations, roundEnv);
- }
+ for (ProcessorDescriptor proc : active) proc.process(annotations, roundEnv);
- return handled;
+ return false;
}
/**
diff --git a/src/core/lombok/core/AnnotationValues.java b/src/core/lombok/core/AnnotationValues.java
index df056dd4..d04797cb 100644
--- a/src/core/lombok/core/AnnotationValues.java
+++ b/src/core/lombok/core/AnnotationValues.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -435,23 +435,16 @@ public class AnnotationValues<A extends Annotation> {
}
/* 2. Walk through non-star imports and search for a match. */ {
- for (String im : ast == null ? Collections.<String>emptyList() : ast.getImportStatements()) {
- if (im.endsWith(".*")) continue;
- int idx = im.lastIndexOf('.');
- String simple = idx == -1 ? im : im.substring(idx+1);
- if (simple.equals(prefix)) {
- return im + typeName.substring(prefix.length());
- }
+ if (prefix.equals(typeName)) {
+ String fqn = ast.getImportList().getFullyQualifiedNameForSimpleName(typeName);
+ if (fqn != null) return fqn;
}
}
/* 3. Walk through star imports and, if they start with "java.", use Class.forName based resolution. */ {
- List<String> imports = ast == null ? Collections.<String>emptyList() : new ArrayList<String>(ast.getImportStatements());
- imports.add("java.lang.*");
- for (String im : imports) {
- if (!im.endsWith(".*") || !im.startsWith("java.")) continue;
+ for (String potential : ast.getImportList().applyNameToStarImports("java", typeName)) {
try {
- Class<?> c = Class.forName(im.substring(0, im.length()-1) + typeName);
+ Class<?> c = Class.forName(potential);
if (c != null) return c.getName();
} catch (Throwable t) {
//Class.forName failed for whatever reason - it most likely does not exist, continue.
diff --git a/src/core/lombok/core/ImportList.java b/src/core/lombok/core/ImportList.java
new file mode 100644
index 00000000..95e266c4
--- /dev/null
+++ b/src/core/lombok/core/ImportList.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013 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.core;
+
+import java.util.Collection;
+
+public interface ImportList {
+ /**
+ * If there is an explicit import of the stated unqualified type name, return that. Otherwise, return null.
+ */
+ String getFullyQualifiedNameForSimpleName(String unqualified);
+
+ /**
+ * Returns true if the package name is explicitly star-imported, OR the packageName refers to this source file's own package name, OR packageName is 'java.lang'.
+ */
+ boolean hasStarImport(String packageName);
+
+ /**
+ * Takes all explicit non-static star imports whose first element is equal to {@code startsWith}, replaces the star with {@code unqualified}, and returns these.
+ */
+ Collection<String> applyNameToStarImports(String startsWith, String unqualified);
+
+ String applyUnqualifiedNameToPackage(String unqualified);
+}
diff --git a/src/core/lombok/core/LombokInternalAliasing.java b/src/core/lombok/core/LombokInternalAliasing.java
new file mode 100644
index 00000000..4fd7b29d
--- /dev/null
+++ b/src/core/lombok/core/LombokInternalAliasing.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 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.core;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class LombokInternalAliasing {
+ public static final Map<String, String> IMPLIED_EXTRA_STAR_IMPORTS;
+ public static final Map<String, String> ALIASES;
+
+ /**
+ * Provide a fully qualified name (FQN), and the canonical version of this is returned.
+ */
+ public static String processAliases(String in) {
+ if (in == null) return null;
+ for (Map.Entry<String, String> e : ALIASES.entrySet()) {
+ if (in.equals(e.getKey())) return e.getValue();
+ }
+ return in;
+ }
+
+ static {
+ Map<String, String> m = new HashMap<String, String>();
+ m.put("lombok.experimental", "lombok");
+ IMPLIED_EXTRA_STAR_IMPORTS = Collections.unmodifiableMap(m);
+
+ m = new HashMap<String, String>();
+ m.put("lombok.experimental.Value", "lombok.Value");
+ ALIASES = Collections.unmodifiableMap(m);
+ }
+}
diff --git a/src/core/lombok/core/LombokNode.java b/src/core/lombok/core/LombokNode.java
index eac59806..07c62151 100644
--- a/src/core/lombok/core/LombokNode.java
+++ b/src/core/lombok/core/LombokNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -42,7 +42,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
protected final A ast;
protected final Kind kind;
protected final N node;
- protected final List<L> children;
+ protected LombokImmutableList<L> children;
protected L parent;
/** structurally significant are those nodes that can be annotated in java 1.6 or are method-like toplevels,
@@ -62,7 +62,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
this.ast = ast;
this.kind = kind;
this.node = node;
- this.children = children == null ? new ArrayList<L>() : children;
+ this.children = children != null ? LombokImmutableList.copyOf(children) : LombokImmutableList.<L>of();
for (L child : this.children) {
child.parent = (L) this;
if (!child.isStructurallySignificant)
@@ -91,12 +91,12 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
}
/**
- * Convenient shortcut to the owning ast object's {@code getImportStatements} method.
+ * Convenient shortcut to the owning ast object's {@code getImportList} method.
*
- * @see AST#getImportStatements()
+ * @see AST#getImportList()
*/
- public Collection<String> getImportStatements() {
- return ast.getImportStatements();
+ public ImportList getImportList() {
+ return ast.getImportList();
}
/**
@@ -120,35 +120,6 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
return node;
}
- /**
- * Replaces the AST node represented by this node object with the provided node. This node must
- * have a parent, obviously, for this to work.
- *
- * Also affects the underlying (Eclipse/javac) AST.
- */
- @SuppressWarnings({"rawtypes", "unchecked"})
- public L replaceWith(N newN, Kind newNodeKind) {
- ast.setChanged();
- L newNode = ast.buildTree(newN, newNodeKind);
- newNode.parent = parent;
- for (int i = 0; i < parent.children.size(); i++) {
- if (parent.children.get(i) == this) ((List)parent.children).set(i, newNode);
- }
-
- parent.replaceChildNode(get(), newN);
- return newNode;
- }
-
- /**
- * Replaces the stated node with a new one. The old node must be a direct child of this node.
- *
- * Also affects the underlying (Eclipse/javac) AST.
- */
- public void replaceChildNode(N oldN, N newN) {
- ast.setChanged();
- ast.replaceStatementInNode(get(), oldN, newN);
- }
-
public Kind getKind() {
return kind;
}
@@ -204,12 +175,27 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
/**
* Returns all children nodes.
+ */
+ public LombokImmutableList<L> down() {
+ return children;
+ }
+
+ /**
+ * Convenient shortcut to the owning ast object's getLatestJavaSpecSupported method.
+ *
+ * @see AST#getLatestJavaSpecSupported()
+ */
+ public int getLatestJavaSpecSupported() {
+ return ast.getLatestJavaSpecSupported();
+ }
+
+ /**
+ * Convenient shortcut to the owning ast object's getSourceVersion method.
*
- * A copy is created, so changing the list has no effect. Also, while iterating through this list,
- * you may add, remove, or replace children without causing {@code ConcurrentModificationException}s.
+ * @see AST#getSourceVersion()
*/
- public Collection<L> down() {
- return new ArrayList<L>(children);
+ public int getSourceVersion() {
+ return ast.getSourceVersion();
}
/**
@@ -235,13 +221,13 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
*
* Does not change the underlying (javac/Eclipse) AST, only the wrapped view.
*/
- @SuppressWarnings({"rawtypes", "unchecked"})
+ @SuppressWarnings({"unchecked"})
public L add(N newChild, Kind newChildKind) {
ast.setChanged();
L n = ast.buildTree(newChild, newChildKind);
if (n == null) return null;
n.parent = (L) this;
- ((List)children).add(n);
+ children = children.append(n);
return n;
}
@@ -267,7 +253,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
for (LombokNode child : children) child.gatherAndRemoveChildren(map);
ast.identityDetector.remove(get());
map.put(get(), (L) this);
- children.clear();
+ children = LombokImmutableList.of();
ast.getNodeMap().remove(get());
}
@@ -278,7 +264,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
*/
public void removeChild(L child) {
ast.setChanged();
- children.remove(child);
+ children = children.removeElement(child);
}
/**
diff --git a/src/core/lombok/core/PostCompiler.java b/src/core/lombok/core/PostCompiler.java
index 1e40cca2..4a099862 100644
--- a/src/core/lombok/core/PostCompiler.java
+++ b/src/core/lombok/core/PostCompiler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Project Lombok Authors.
+ * Copyright (C) 2010-2013 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
@@ -33,6 +33,7 @@ public final class PostCompiler {
private static List<PostCompilerTransformation> transformations;
public static byte[] applyTransformations(byte[] original, String fileName, DiagnosticsReceiver diagnostics) {
+ if (System.getProperty("lombok.disablePostCompiler", null) != null) return original;
init(diagnostics);
byte[] previous = original;
for (PostCompilerTransformation transformation : transformations) {
@@ -59,6 +60,7 @@ public final class PostCompiler {
}
public static OutputStream wrapOutputStream(final OutputStream originalStream, final String fileName, final DiagnosticsReceiver diagnostics) throws IOException {
+ if (System.getProperty("lombok.disablePostCompiler", null) != null) return originalStream;
return new ByteArrayOutputStream() {
@Override public void close() throws IOException {
// no need to call super
diff --git a/src/core/lombok/core/TransformationsUtil.java b/src/core/lombok/core/TransformationsUtil.java
index 921c27d6..8959ad7a 100644
--- a/src/core/lombok/core/TransformationsUtil.java
+++ b/src/core/lombok/core/TransformationsUtil.java
@@ -22,13 +22,25 @@
package lombok.core;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import lombok.Value;
import lombok.experimental.Accessors;
+import lombok.experimental.FieldDefaults;
+import lombok.experimental.Wither;
/**
* Container for static utility methods useful for some of the standard lombok transformations, regardless of
@@ -39,6 +51,13 @@ public class TransformationsUtil {
//Prevent instantiation
}
+ @SuppressWarnings({"all", "unchecked", "deprecation"})
+ public static final List<Class<? extends java.lang.annotation.Annotation>> INVALID_ON_BUILDERS = Collections.unmodifiableList(
+ Arrays.<Class<? extends java.lang.annotation.Annotation>>asList(
+ Getter.class, Setter.class, Wither.class, ToString.class, EqualsAndHashCode.class,
+ RequiredArgsConstructor.class, AllArgsConstructor.class, NoArgsConstructor.class,
+ Data.class, Value.class, lombok.experimental.Value.class, FieldDefaults.class));
+
/**
* Given the name of a field, return the 'base name' of that field. For example, {@code fFoobar} becomes {@code foobar} if {@code f} is in the prefix list.
* For prefixes that end in a letter character, the next character must be a non-lowercase character (i.e. {@code hashCode} is not {@code ashCode} even if
@@ -159,12 +178,12 @@ public class TransformationsUtil {
if (fieldName.length() == 0) return null;
- Accessors ac = accessors.getInstance();
- fieldName = removePrefix(fieldName, ac.prefix());
+ Accessors ac = accessors == null ? null : accessors.getInstance();
+ fieldName = removePrefix(fieldName, ac == null ? new String[0] : ac.prefix());
if (fieldName == null) return null;
String fName = fieldName.toString();
- if (adhereToFluent && ac.fluent()) return fName;
+ if (adhereToFluent && ac != null && ac.fluent()) return fName;
if (isBoolean && fName.startsWith("is") && fieldName.length() > 2 && !Character.isLowerCase(fieldName.charAt(2))) {
// The field is for example named 'isRunning'.
diff --git a/src/core/lombok/core/TypeLibrary.java b/src/core/lombok/core/TypeLibrary.java
index 0e3f7bf4..c0e9dc43 100644
--- a/src/core/lombok/core/TypeLibrary.java
+++ b/src/core/lombok/core/TypeLibrary.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -21,12 +21,7 @@
*/
package lombok.core;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
/**
@@ -40,24 +35,23 @@ import java.util.Map;
* <ul><li>foo.Spork</li><li>Spork</li><li>foo.*</li></ul>
*/
public class TypeLibrary {
- private final Map<String, List<String>> keyToFqnMap;
- private final String singletonValue;
- private final List<String> singletonKeys;
+ private final Map<String, String> unqualifiedToQualifiedMap;
+ private final String unqualified, qualified;
public TypeLibrary() {
- keyToFqnMap = new HashMap<String, List<String>>();
- singletonKeys = null;
- singletonValue = null;
+ unqualifiedToQualifiedMap = new HashMap<String, String>();
+ unqualified = null;
+ qualified = null;
}
private TypeLibrary(String fqnSingleton) {
- keyToFqnMap = null;
- singletonValue = fqnSingleton;
+ unqualifiedToQualifiedMap = null;
+ qualified = fqnSingleton;
int idx = fqnSingleton.lastIndexOf('.');
if (idx == -1) {
- singletonKeys = Collections.singletonList(fqnSingleton);
+ unqualified = fqnSingleton;
} else {
- singletonKeys = Arrays.asList(fqnSingleton, fqnSingleton.substring(idx + 1), fqnSingleton.substring(0, idx) + ".*");
+ unqualified = fqnSingleton.substring(idx + 1);
}
}
@@ -71,42 +65,34 @@ public class TypeLibrary {
* @param fullyQualifiedTypeName the FQN type name, such as 'java.lang.String'.
*/
public void addType(String fullyQualifiedTypeName) {
- if (keyToFqnMap == null) throw new IllegalStateException("SingleType library");
+ fullyQualifiedTypeName = fullyQualifiedTypeName.replace("$", ".");
int idx = fullyQualifiedTypeName.lastIndexOf('.');
if (idx == -1) throw new IllegalArgumentException(
"Only fully qualified types are allowed (and stuff in the default package is not palatable to us either!)");
+ String unqualified = fullyQualifiedTypeName.substring(idx + 1);
+ if (unqualifiedToQualifiedMap == null) throw new IllegalStateException("SingleType library");
- fullyQualifiedTypeName = fullyQualifiedTypeName.replace("$", ".");
- final String simpleName = fullyQualifiedTypeName.substring(idx +1);
- final String packageName = fullyQualifiedTypeName.substring(0, idx);
-
- if (keyToFqnMap.put(fullyQualifiedTypeName, Collections.singletonList(fullyQualifiedTypeName)) != null) return;
-
- addToMap(simpleName, fullyQualifiedTypeName);
- addToMap(packageName + ".*", fullyQualifiedTypeName);
- }
-
- private TypeLibrary addToMap(String keyName, String fullyQualifiedTypeName) {
- List<String> list = keyToFqnMap.get(keyName);
- if (list == null) {
- list = new ArrayList<String>();
- keyToFqnMap.put(keyName, list);
+ unqualifiedToQualifiedMap.put(unqualified, fullyQualifiedTypeName);
+ unqualifiedToQualifiedMap.put(fullyQualifiedTypeName, fullyQualifiedTypeName);
+ for (Map.Entry<String, String> e : LombokInternalAliasing.ALIASES.entrySet()) {
+ if (fullyQualifiedTypeName.equals(e.getValue())) unqualifiedToQualifiedMap.put(e.getKey(), fullyQualifiedTypeName);
}
-
- list.add(fullyQualifiedTypeName);
- return this;
}
/**
- * Returns all items in the type library that may be a match to the provided type.
+ * Translates an unqualified name such as 'String' to 'java.lang.String', _if_ you added 'java.lang.String' to the library via the {@code addType} method.
+ * Also returns the input if it is equal to a fully qualified name added to this type library.
*
- * @param typeReference something like 'String', 'java.lang.String', or 'java.lang.*'.
- * @return A list of Fully Qualified Names for all types in the library that fit the reference.
+ * Returns null if it does not match any type in this type library.
*/
- public Collection<String> findCompatible(String typeReference) {
- if (singletonKeys != null) return singletonKeys.contains(typeReference) ? Collections.singletonList(singletonValue) : Collections.<String>emptyList();
-
- List<String> result = keyToFqnMap.get(typeReference);
- return result == null ? Collections.<String>emptyList() : Collections.unmodifiableList(result);
+ public String toQualified(String typeReference) {
+ if (unqualifiedToQualifiedMap == null) {
+ if (typeReference.equals(unqualified) || typeReference.equals(qualified)) return qualified;
+ for (Map.Entry<String, String> e : LombokInternalAliasing.ALIASES.entrySet()) {
+ if (e.getKey().equals(typeReference)) return e.getValue();
+ }
+ return null;
+ }
+ return unqualifiedToQualifiedMap.get(typeReference);
}
}
diff --git a/src/core/lombok/core/TypeResolver.java b/src/core/lombok/core/TypeResolver.java
index 27f0bfc1..287a085f 100644
--- a/src/core/lombok/core/TypeResolver.java
+++ b/src/core/lombok/core/TypeResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -21,11 +21,6 @@
*/
package lombok.core;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
import lombok.core.AST.Kind;
/**
@@ -35,60 +30,47 @@ import lombok.core.AST.Kind;
* and this importer also can't find inner types from superclasses/interfaces.
*/
public class TypeResolver {
- private Collection<String> imports;
+ private ImportList imports;
/**
* Creates a new TypeResolver that can be used to resolve types in a source file with the given package and import statements.
*/
- public TypeResolver(String packageString, Collection<String> importStrings) {
- this.imports = makeImportList(packageString, importStrings);
- }
-
- private static Collection<String> makeImportList(String packageString, Collection<String> importStrings) {
- Set<String> imports = new HashSet<String>();
- if (packageString != null) imports.add(packageString + ".*");
- imports.addAll(importStrings == null ? Collections.<String>emptySet() : importStrings);
- imports.add("java.lang.*");
- return imports;
+ public TypeResolver(ImportList importList) {
+ this.imports = importList;
}
public boolean typeMatches(LombokNode<?, ?, ?> context, String fqn, String typeRef) {
- return !findTypeMatches(context, TypeLibrary.createLibraryForSingleType(fqn), typeRef).isEmpty();
+ return typeRefToFullyQualifiedName(context, TypeLibrary.createLibraryForSingleType(fqn), typeRef) != null;
}
- /**
- * Finds type matches for the stated type reference. The provided context is scanned for local type names
- * that shadow type names listed in import statements. If such a shadowing occurs, no matches are returned
- * for any shadowed types, as you would expect.
- */
- public Collection<String> findTypeMatches(LombokNode<?, ?, ?> context, TypeLibrary library, String typeRef) {
+ public String typeRefToFullyQualifiedName(LombokNode<?, ?, ?> context, TypeLibrary library, String typeRef) {
+ typeRef = LombokInternalAliasing.processAliases(typeRef);
// When asking if 'Foo' could possibly be referring to 'bar.Baz', the answer is obviously no.
- Collection<String> potentialMatches = library.findCompatible(typeRef);
- if (potentialMatches.isEmpty()) return Collections.emptyList();
+ String qualified = library.toQualified(typeRef);
+ if (qualified == null) return null;
- // If input type appears to be fully qualified, we found a winner.
- int idx = typeRef.indexOf('.');
- if (idx > -1) return potentialMatches;
+ // When asking if 'lombok.Getter' could possibly be referring to 'lombok.Getter', the answer is obviously yes.
+ if (typeRef.equals(qualified)) return typeRef;
- // If there's an import statement that explicitly imports a 'Getter' that isn't any of our potentials, return no matches,
- // because if you want to know if 'Foo' could refer to 'bar.Foo' when 'baz.Foo' is explicitly imported, the answer is no.
- if (nameConflictInImportList(typeRef, potentialMatches)) return Collections.emptyList();
+ // When asking if 'Getter' could possibly be referring to 'lombok.Getter' if 'import lombok.Getter;' is in the source file, the answer is yes.
+ String fromExplicitImport = imports.getFullyQualifiedNameForSimpleName(typeRef);
+ if (fromExplicitImport != null) {
+ // ... and if 'import foobar.Getter;' is in the source file, the answer is no.
+ return fromExplicitImport.equals(qualified) ? qualified : null;
+ }
- // Check if any of our potentials are even imported in the first place. If not: no matches.
- // Note that (ourPackage.*) is added to the imports.
- potentialMatches = eliminateImpossibleMatches(potentialMatches, library);
- if (potentialMatches.isEmpty()) return Collections.emptyList();
+ // When asking if 'Getter' could possibly be referring to 'lombok.Getter' and 'import lombok.*; / package lombok;' isn't in the source file. the answer is no.
+ String pkgName = qualified.substring(0, qualified.length() - typeRef.length() - 1);
+ if (!imports.hasStarImport(pkgName)) return null;
- // Now the hard part - inner classes or method local classes in our own scope.
- // For method locals, this refers to any statements that are 'above' the type reference with the same name.
- // For inners, this refers to siblings of us or any parent node that are type declarations.
+ // Now the hard part: Given that there is a star import, 'Getter' most likely refers to 'lombok.Getter', but type shadowing may occur in which case it doesn't.
LombokNode<?, ?, ?> n = context;
mainLoop:
while (n != null) {
if (n.getKind() == Kind.TYPE && typeRef.equals(n.getName())) {
// Our own class or one of our outer classes is named 'typeRef' so that's what 'typeRef' is referring to, not one of our type library classes.
- return Collections.emptyList();
+ return null;
}
if (n.getKind() == Kind.STATEMENT || n.getKind() == Kind.LOCAL) {
@@ -99,7 +81,7 @@ public class TypeResolver {
for (LombokNode<?, ?, ?> child : newN.down()) {
// We found a method local with the same name above our code. That's the one 'typeRef' is referring to, not
// anything in the type library we're trying to find, so, no matches.
- if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return Collections.emptyList();
+ if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return null;
if (child == n) break;
}
}
@@ -110,41 +92,14 @@ public class TypeResolver {
if (n.getKind() == Kind.TYPE || n.getKind() == Kind.COMPILATION_UNIT) {
for (LombokNode<?, ?, ?> child : n.down()) {
// Inner class that's visible to us has 'typeRef' as name, so that's the one being referred to, not one of our type library classes.
- if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return Collections.emptyList();
+ if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return null;
}
}
n = n.directUp();
}
- // No class in this source file is a match, therefore the potential matches found via the import statements must be it. Return those.
- return potentialMatches;
- }
-
- private Collection<String> eliminateImpossibleMatches(Collection<String> potentialMatches, TypeLibrary library) {
- Set<String> results = new HashSet<String>();
-
- for (String importedType : imports) {
- Collection<String> reduced = new HashSet<String>(library.findCompatible(importedType));
- reduced.retainAll(potentialMatches);
- results.addAll(reduced);
- }
-
- return results;
- }
-
- private boolean nameConflictInImportList(String simpleName, Collection<String> potentialMatches) {
- for (String importedType : imports) {
- if (!toSimpleName(importedType).equals(simpleName)) continue;
- if (potentialMatches.contains(importedType)) continue;
- return true;
- }
- return false;
- }
-
- private static String toSimpleName(String typeName) {
- int idx = typeName.lastIndexOf('.');
- return idx == -1 ? typeName : typeName.substring(idx+1);
+ return qualified;
}
}
diff --git a/src/core/lombok/core/Version.java b/src/core/lombok/core/Version.java
index b9239978..bd83c1f8 100644
--- a/src/core/lombok/core/Version.java
+++ b/src/core/lombok/core/Version.java
@@ -28,8 +28,8 @@ public class Version {
// ** CAREFUL ** - this class must always compile with 0 dependencies (it must not refer to any other sources or libraries).
// Note: In 'X.Y.Z', if Z is odd, its a snapshot build built from the repository, so many different 0.10.3 versions can exist, for example.
// Official builds always end in an even number. (Since 0.10.2).
- private static final String VERSION = "0.11.7";
- private static final String RELEASE_NAME = "Dashing Kakapo";
+ private static final String VERSION = "0.12.1";
+ private static final String RELEASE_NAME = "Angry Butterfy";
private Version() {
//Prevent instantiation
diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java
new file mode 100644
index 00000000..3d386054
--- /dev/null
+++ b/src/core/lombok/core/handlers/HandlerUtil.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2013 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.core.handlers;
+
+import lombok.core.JavaIdentifiers;
+import lombok.core.LombokNode;
+
+public class HandlerUtil {
+ private HandlerUtil() {}
+
+ /** Checks if the given name is a valid identifier.
+ *
+ * If it is, this returns {@code true} and does nothing else.
+ * If it isn't, this returns {@code false} and adds an error message to the supplied node.
+ */
+ public static boolean checkName(String nameSpec, String identifier, LombokNode<?, ?, ?> errorNode) {
+ if (identifier.isEmpty()) {
+ errorNode.addError(nameSpec + " cannot be the empty string.");
+ return false;
+ }
+
+ if (!JavaIdentifiers.isValidJavaIdentifier(identifier)) {
+ errorNode.addError(nameSpec + " must be a valid java identifier.");
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java
index 8ab42140..370b40fc 100644
--- a/src/core/lombok/eclipse/EclipseAST.java
+++ b/src/core/lombok/eclipse/EclipseAST.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2010 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -24,12 +24,14 @@ package lombok.eclipse;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import org.eclipse.jdt.internal.compiler.CompilationResult;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import lombok.Lombok;
import lombok.core.AST;
+import lombok.core.LombokImmutableList;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
@@ -55,7 +57,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
* @param ast The compilation unit, which serves as the top level node in the tree to be built.
*/
public EclipseAST(CompilationUnitDeclaration ast) {
- super(toFileName(ast), packageDeclaration(ast), imports(ast));
+ super(toFileName(ast), packageDeclaration(ast), new EclipseImportList(ast));
this.compilationUnitDeclaration = ast;
setTop(buildCompilationUnit(ast));
this.completeParse = isComplete(ast);
@@ -67,16 +69,18 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
return pkg == null ? null : Eclipse.toQualifiedName(pkg.getImportName());
}
- private static Collection<String> imports(CompilationUnitDeclaration cud) {
- List<String> imports = new ArrayList<String>();
- if (cud.imports == null) return imports;
- for (ImportReference imp : cud.imports) {
- if (imp == null) continue;
- String qualifiedName = Eclipse.toQualifiedName(imp.getImportName());
- if ((imp.bits & ASTNode.OnDemand) != 0) qualifiedName += ".*";
- imports.add(qualifiedName);
- }
- return imports;
+ @Override public int getSourceVersion() {
+ long sl = compilationUnitDeclaration.problemReporter.options.sourceLevel;
+ long cl = compilationUnitDeclaration.problemReporter.options.complianceLevel;
+ sl >>= 16;
+ cl >>= 16;
+ if (sl == 0) sl = cl;
+ if (cl == 0) cl = sl;
+ return Math.min((int)(sl - 44), (int)(cl - 44));
+ }
+
+ @Override public int getLatestJavaSpecSupported() {
+ return Eclipse.getEcjCompilerVersion();
}
/**
@@ -88,8 +92,10 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
}
void traverseChildren(EclipseASTVisitor visitor, EclipseNode node) {
- for (EclipseNode child : node.down()) {
- child.traverse(visitor);
+ LombokImmutableList<EclipseNode> children = node.down();
+ int len = children.size();
+ for (int i = 0; i < len; i++) {
+ children.get(i).traverse(visitor);
}
}
@@ -118,7 +124,8 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
}
void addToCompilationResult() {
- addProblemToCompilationResult((CompilationUnitDeclaration) top().get(),
+ CompilationUnitDeclaration cud = (CompilationUnitDeclaration) top().get();
+ addProblemToCompilationResult(cud.getFileName(), cud.compilationResult,
isWarning, message, sourceStart, sourceEnd);
}
}
@@ -142,11 +149,10 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
* Adds a problem to the provided CompilationResult object so that it will show up
* in the Problems/Warnings view.
*/
- public static void addProblemToCompilationResult(CompilationUnitDeclaration ast,
+ public static void addProblemToCompilationResult(char[] fileNameArray, CompilationResult result,
boolean isWarning, String message, int sourceStart, int sourceEnd) {
- if (ast.compilationResult == null) return;
try {
- EcjReflectionCheck.addProblemToCompilationResult.invoke(null, ast, isWarning, message, sourceStart, sourceEnd);
+ EcjReflectionCheck.addProblemToCompilationResult.invoke(null, fileNameArray, result, isWarning, message, sourceStart, sourceEnd);
} catch (NoClassDefFoundError e) {
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
//do anything useful here.
@@ -163,7 +169,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
//do anything useful here.
}
}
-
+
private final CompilationUnitDeclaration compilationUnitDeclaration;
private boolean completeParse;
@@ -353,7 +359,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
}
private static class EcjReflectionCheck {
- private static final String CUD_TYPE = "org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration";
+ private static final String COMPILATIONRESULT_TYPE = "org.eclipse.jdt.internal.compiler.CompilationResult";
public static Method addProblemToCompilationResult;
public static final Throwable problem;
@@ -362,7 +368,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
Throwable problem_ = null;
Method m = null;
try {
- m = EclipseAstProblemView.class.getMethod("addProblemToCompilationResult", Class.forName(CUD_TYPE), boolean.class, String.class, int.class, int.class);
+ m = EclipseAstProblemView.class.getMethod("addProblemToCompilationResult", char[].class, Class.forName(COMPILATIONRESULT_TYPE), boolean.class, String.class, int.class, int.class);
} 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.
diff --git a/src/core/lombok/eclipse/EclipseAnnotationHandler.java b/src/core/lombok/eclipse/EclipseAnnotationHandler.java
index 84304339..ca9965f7 100644
--- a/src/core/lombok/eclipse/EclipseAnnotationHandler.java
+++ b/src/core/lombok/eclipse/EclipseAnnotationHandler.java
@@ -29,7 +29,7 @@ import lombok.core.SpiLoadUtil;
*
* You MUST replace 'T' with a specific annotation type, such as:
*
- * {@code public class HandleGetter implements EclipseAnnotationHandler<Getter>}
+ * {@code public class HandleGetter extends EclipseAnnotationHandler<Getter>}
*
* Because this generics parameter is inspected to figure out which class you're interested in.
*
diff --git a/src/core/lombok/eclipse/EclipseAstProblemView.java b/src/core/lombok/eclipse/EclipseAstProblemView.java
index a2d5b833..c1179666 100644
--- a/src/core/lombok/eclipse/EclipseAstProblemView.java
+++ b/src/core/lombok/eclipse/EclipseAstProblemView.java
@@ -3,7 +3,6 @@ package lombok.eclipse;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.internal.compiler.CompilationResult;
-import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.util.Util;
@@ -13,14 +12,12 @@ public class EclipseAstProblemView {
* Adds a problem to the provided CompilationResult object so that it will show up
* in the Problems/Warnings view.
*/
- public static void addProblemToCompilationResult(CompilationUnitDeclaration ast,
+ public static void addProblemToCompilationResult(char[] fileNameArray, CompilationResult result,
boolean isWarning, String message, int sourceStart, int sourceEnd) {
- if (ast.compilationResult == null) return;
- char[] fileNameArray = ast.getFileName();
+ if (result == null) return;
if (fileNameArray == null) fileNameArray = "(unknown).java".toCharArray();
int lineNumber = 0;
int columnNumber = 1;
- CompilationResult result = ast.compilationResult;
int[] lineEnds = null;
lineNumber = sourceStart >= 0
? Util.getLineNumber(sourceStart, lineEnds = result.getLineSeparatorPositions(), 0, lineEnds.length-1)
@@ -33,7 +30,7 @@ public class EclipseAstProblemView {
fileNameArray, message, 0, new String[0],
isWarning ? ProblemSeverities.Warning : ProblemSeverities.Error,
sourceStart, sourceEnd, lineNumber, columnNumber);
- ast.compilationResult.record(ecProblem, null);
+ result.record(ecProblem, null);
}
private static class LombokProblem extends DefaultProblem {
diff --git a/src/core/lombok/eclipse/EclipseImportList.java b/src/core/lombok/eclipse/EclipseImportList.java
new file mode 100644
index 00000000..69246b3c
--- /dev/null
+++ b/src/core/lombok/eclipse/EclipseImportList.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2013 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;
+
+import static lombok.eclipse.Eclipse.toQualifiedName;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import lombok.core.ImportList;
+import lombok.core.LombokInternalAliasing;
+
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ImportReference;
+
+public class EclipseImportList implements ImportList {
+ private ImportReference[] imports;
+ private ImportReference pkg;
+
+ public EclipseImportList(CompilationUnitDeclaration cud) {
+ this.pkg = cud.currentPackage;
+ this.imports = cud.imports;
+ }
+
+ @Override public String getFullyQualifiedNameForSimpleName(String unqualified) {
+ if (imports != null) {
+ outer:
+ for (ImportReference imp : imports) {
+ if ((imp.bits & ASTNode.OnDemand) != 0) continue;
+ char[][] tokens = imp.tokens;
+ char[] token = tokens.length == 0 ? new char[0] : tokens[tokens.length - 1];
+ int len = token.length;
+ if (len != unqualified.length()) continue;
+ for (int i = 0; i < len; i++) if (token[i] != unqualified.charAt(i)) continue outer;
+ return LombokInternalAliasing.processAliases(toQualifiedName(tokens));
+ }
+ }
+ return null;
+ }
+
+ @Override public boolean hasStarImport(String packageName) {
+ for (Map.Entry<String, String> e : LombokInternalAliasing.IMPLIED_EXTRA_STAR_IMPORTS.entrySet()) {
+ if (e.getValue().equals(packageName) && hasStarImport(e.getKey())) return true;
+ }
+ if (isEqual(packageName, pkg)) return true;
+ if ("java.lang".equals(packageName)) return true;
+ if (imports != null) for (ImportReference imp : imports) {
+ if ((imp.bits & ASTNode.OnDemand) == 0) continue;
+ if (imp.isStatic()) continue;
+ if (isEqual(packageName, imp)) return true;
+ }
+ return false;
+ }
+
+ private static boolean isEqual(String packageName, ImportReference pkgOrStarImport) {
+ if (pkgOrStarImport == null || pkgOrStarImport.tokens == null || pkgOrStarImport.tokens.length == 0) return packageName.isEmpty();
+ int pos = 0;
+ int len = packageName.length();
+ for (int i = 0; i < pkgOrStarImport.tokens.length; i++) {
+ if (i != 0) {
+ if (pos >= len) return false;
+ if (packageName.charAt(pos++) != '.') return false;
+ }
+ for (int j = 0; j < pkgOrStarImport.tokens[i].length; j++) {
+ if (pos >= len) return false;
+ if (packageName.charAt(pos++) != pkgOrStarImport.tokens[i][j]) return false;
+ }
+ }
+ return true;
+ }
+
+ @Override public Collection<String> applyNameToStarImports(String startsWith, String name) {
+ List<String> out = Collections.emptyList();
+
+ if (pkg != null && pkg.tokens != null && pkg.tokens.length != 0) {
+ char[] first = pkg.tokens[0];
+ int len = first.length;
+ boolean match = true;
+ if (startsWith.length() == len) {
+ for (int i = 0; match && i < len; i++) {
+ if (startsWith.charAt(i) != first[i]) match = false;
+ }
+ if (match) out.add(toQualifiedName(pkg.tokens) + "." + name);
+ }
+ }
+
+ if (imports != null) {
+ outer:
+ for (ImportReference imp : imports) {
+ if ((imp.bits & ASTNode.OnDemand) == 0) continue;
+ if (imp.isStatic()) continue;
+ if (imp.tokens == null || imp.tokens.length == 0) continue;
+ char[] firstToken = imp.tokens[0];
+ if (firstToken.length != startsWith.length()) continue;
+ for (int i = 0; i < firstToken.length; i++) if (startsWith.charAt(i) != firstToken[i]) continue outer;
+ String fqn = toQualifiedName(imp.tokens) + "." + name;
+ if (out.isEmpty()) out = Collections.singletonList(fqn);
+ else if (out.size() == 1) {
+ out = new ArrayList<String>(out);
+ out.add(fqn);
+ } else {
+ out.add(fqn);
+ }
+ }
+ }
+ return out;
+ }
+
+ @Override public String applyUnqualifiedNameToPackage(String unqualified) {
+ if (pkg == null || pkg.tokens == null || pkg.tokens.length == 0) return unqualified;
+ return toQualifiedName(pkg.tokens) + "." + unqualified;
+ }
+}
diff --git a/src/core/lombok/eclipse/HandlerLibrary.java b/src/core/lombok/eclipse/HandlerLibrary.java
index 56744793..242e923c 100644
--- a/src/core/lombok/eclipse/HandlerLibrary.java
+++ b/src/core/lombok/eclipse/HandlerLibrary.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -218,30 +218,27 @@ public class HandlerLibrary {
* @param annotation 'node.get()' - convenience parameter.
*/
public void handleAnnotation(CompilationUnitDeclaration ast, EclipseNode annotationNode, org.eclipse.jdt.internal.compiler.ast.Annotation annotation, long priority) {
- String pkgName = annotationNode.getPackageDeclaration();
- Collection<String> imports = annotationNode.getImportStatements();
-
- TypeResolver resolver = new TypeResolver(pkgName, imports);
+ TypeResolver resolver = new TypeResolver(annotationNode.getImportList());
TypeReference rawType = annotation.type;
if (rawType == null) return;
- for (String fqn : resolver.findTypeMatches(annotationNode, typeLibrary, toQualifiedName(annotation.type.getTypeName()))) {
- AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
- if (container == null) continue;
- if (priority != container.getPriority()) continue;
-
- if (!annotationNode.isCompleteParse() && container.deferUntilPostDiet()) {
- if (needsHandling(annotation)) container.preHandle(annotation, annotationNode);
- continue;
- }
-
- try {
- if (checkAndSetHandled(annotation)) container.handle(annotation, annotationNode);
- } catch (AnnotationValueDecodeFail fail) {
- fail.owner.setError(fail.getMessage(), fail.idx);
- } catch (Throwable t) {
- error(ast, String.format("Lombok annotation handler %s failed", container.handler.getClass()), t);
- }
+ String fqn = resolver.typeRefToFullyQualifiedName(annotationNode, typeLibrary, toQualifiedName(annotation.type.getTypeName()));
+ if (fqn == null) return;
+ AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
+ if (container == null) return;
+ if (priority != container.getPriority()) return;
+
+ if (!annotationNode.isCompleteParse() && container.deferUntilPostDiet()) {
+ if (needsHandling(annotation)) container.preHandle(annotation, annotationNode);
+ return;
+ }
+
+ try {
+ if (checkAndSetHandled(annotation)) container.handle(annotation, annotationNode);
+ } catch (AnnotationValueDecodeFail fail) {
+ fail.owner.setError(fail.getMessage(), fail.idx);
+ } catch (Throwable t) {
+ error(ast, String.format("Lombok annotation handler %s failed", container.handler.getClass()), t);
}
}
diff --git a/src/core/lombok/eclipse/TransformEclipseAST.java b/src/core/lombok/eclipse/TransformEclipseAST.java
index 47e620f6..11caf5c2 100644
--- a/src/core/lombok/eclipse/TransformEclipseAST.java
+++ b/src/core/lombok/eclipse/TransformEclipseAST.java
@@ -144,7 +144,7 @@ public class TransformEclipseAST {
try {
String message = "Lombok can't parse this source: " + t.toString();
- EclipseAST.addProblemToCompilationResult(ast, false, message, 0, 0);
+ EclipseAST.addProblemToCompilationResult(ast.getFileName(), ast.compilationResult, false, message, 0, 0);
t.printStackTrace();
} catch (Throwable t2) {
try {
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index 78780522..9bd634f7 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -22,6 +22,7 @@
package lombok.eclipse.handlers;
import static lombok.eclipse.Eclipse.*;
+import static lombok.core.TransformationsUtil.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@@ -61,6 +62,7 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
@@ -142,7 +144,7 @@ public class EclipseHandlerUtil {
} catch (NoClassDefFoundError e) { //standalone ecj does not jave Platform, ILog, IStatus, and friends.
new TerminalLogger().error(message, bundleName, error);
}
- if (cud != null) EclipseAST.addProblemToCompilationResult(cud, false, message + " - See error log.", 0, 0);
+ if (cud != null) EclipseAST.addProblemToCompilationResult(cud.getFileName(), cud.compilationResult, false, message + " - See error log.", 0, 0);
}
/**
@@ -290,11 +292,34 @@ public class EclipseHandlerUtil {
if (!lastPartA.equals(lastPartB)) return false;
String typeName = toQualifiedName(typeRef.getTypeName());
- TypeResolver resolver = new TypeResolver(node.getPackageDeclaration(), node.getImportStatements());
+ TypeResolver resolver = new TypeResolver(node.getImportList());
return resolver.typeMatches(node, type.getName(), typeName);
}
+ public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(EclipseNode typeNode, EclipseNode errorNode) {
+ List<String> disallowed = null;
+ for (EclipseNode child : typeNode.down()) {
+ for (Class<? extends java.lang.annotation.Annotation> annType : INVALID_ON_BUILDERS) {
+ if (annotationTypeMatches(annType, child)) {
+ if (disallowed == null) disallowed = new ArrayList<String>();
+ disallowed.add(annType.getSimpleName());
+ }
+ }
+ }
+
+ int size = disallowed == null ? 0 : disallowed.size();
+ if (size == 0) return;
+ if (size == 1) {
+ errorNode.addError("@" + disallowed.get(0) + " is not allowed on builder classes.");
+ return;
+ }
+ StringBuilder out = new StringBuilder();
+ for (String a : disallowed) out.append("@").append(a).append(", ");
+ out.setLength(out.length() - 2);
+ errorNode.addError(out.append(" are not allowed on builder classes.").toString());
+ }
+
public static Annotation copyAnnotation(Annotation annotation, ASTNode source) {
int pS = source.sourceStart, pE = source.sourceEnd;
@@ -358,6 +383,20 @@ public class EclipseHandlerUtil {
return out;
}
+ public static TypeReference namePlusTypeParamsToTypeReference(char[] typeName, TypeParameter[] params, long p) {
+ if (params != null && params.length > 0) {
+ TypeReference[] refs = new TypeReference[params.length];
+ int idx = 0;
+ for (TypeParameter param : params) {
+ TypeReference typeRef = new SingleTypeReference(param.name, p);
+ refs[idx++] = typeRef;
+ }
+ return new ParameterizedSingleTypeReference(typeName, refs, 0, p);
+ }
+
+ return new SingleTypeReference(typeName, p);
+ }
+
/**
* Convenience method that creates a new array and copies each TypeReference in the source array via
* {@link #copyType(TypeReference, ASTNode)}.
@@ -830,15 +869,20 @@ public class EclipseHandlerUtil {
private static final Object MARKER = new Object();
static void registerCreatedLazyGetter(FieldDeclaration field, char[] methodName, TypeReference returnType) {
- if (!nameEquals(returnType.getTypeName(), "boolean") || returnType.dimensions() > 0) return;
- generatedLazyGettersWithPrimitiveBoolean.put(field, MARKER);
+ if (isBoolean(returnType)) {
+ generatedLazyGettersWithPrimitiveBoolean.put(field, MARKER);
+ }
+ }
+
+ public static boolean isBoolean(TypeReference typeReference) {
+ return nameEquals(typeReference.getTypeName(), "boolean") && typeReference.dimensions() == 0;
}
private static GetterMethod findGetter(EclipseNode field) {
FieldDeclaration fieldDeclaration = (FieldDeclaration) field.get();
boolean forceBool = generatedLazyGettersWithPrimitiveBoolean.containsKey(fieldDeclaration);
TypeReference fieldType = fieldDeclaration.type;
- boolean isBoolean = forceBool || (nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0);
+ boolean isBoolean = forceBool || isBoolean(fieldType);
EclipseNode typeNode = field.up();
for (String potentialGetterName : toAllGetterNames(field, isBoolean)) {
@@ -1207,15 +1251,15 @@ public class EclipseHandlerUtil {
* Inserts a field into an existing type. The type must represent a {@code TypeDeclaration}.
* The field carries the &#64;{@link SuppressWarnings}("all") annotation.
*/
- public static void injectFieldSuppressWarnings(EclipseNode type, FieldDeclaration field) {
+ public static EclipseNode injectFieldSuppressWarnings(EclipseNode type, FieldDeclaration field) {
field.annotations = createSuppressWarningsAll(field, field.annotations);
- injectField(type, field);
+ return injectField(type, field);
}
/**
* Inserts a field into an existing type. The type must represent a {@code TypeDeclaration}.
*/
- public static void injectField(EclipseNode type, FieldDeclaration field) {
+ public static EclipseNode injectField(EclipseNode type, FieldDeclaration field) {
TypeDeclaration parent = (TypeDeclaration) type.get();
if (parent.fields == null) {
@@ -1242,7 +1286,7 @@ public class EclipseHandlerUtil {
}
}
- type.add(field, Kind.FIELD);
+ return type.add(field, Kind.FIELD);
}
private static boolean isEnumConstant(final FieldDeclaration field) {
@@ -1252,7 +1296,7 @@ public class EclipseHandlerUtil {
/**
* Inserts a method into an existing type. The type must represent a {@code TypeDeclaration}.
*/
- public static void injectMethod(EclipseNode type, AbstractMethodDeclaration method) {
+ public static EclipseNode injectMethod(EclipseNode type, AbstractMethodDeclaration method) {
method.annotations = createSuppressWarningsAll(method, method.annotations);
TypeDeclaration parent = (TypeDeclaration) type.get();
@@ -1285,7 +1329,29 @@ public class EclipseHandlerUtil {
parent.methods = newArray;
}
- type.add(method, Kind.METHOD);
+ return type.add(method, Kind.METHOD);
+ }
+
+ /**
+ * Adds an inner type (class, interface, enum) to the given type. Cannot inject top-level types.
+ *
+ * @param typeNode parent type to inject new type into
+ * @param type New type (class, interface, etc) to inject.
+ */
+ public static EclipseNode injectType(final EclipseNode typeNode, final TypeDeclaration type) {
+ type.annotations = createSuppressWarningsAll(type, type.annotations);
+ TypeDeclaration parent = (TypeDeclaration) typeNode.get();
+
+ if (parent.memberTypes == null) {
+ parent.memberTypes = new TypeDeclaration[] { type };
+ } else {
+ TypeDeclaration[] newArray = new TypeDeclaration[parent.memberTypes.length + 1];
+ System.arraycopy(parent.memberTypes, 0, newArray, 0, parent.memberTypes.length);
+ newArray[parent.memberTypes.length] = type;
+ parent.memberTypes = newArray;
+ }
+
+ return typeNode.add(type, Kind.TYPE);
}
private static final char[] ALL = "all".toCharArray();
@@ -1334,7 +1400,11 @@ public class EclipseHandlerUtil {
EqualExpression equalExpression = new EqualExpression(varName, nullLiteral, OperatorIds.EQUAL_EQUAL);
equalExpression.sourceStart = pS; equalExpression.statementEnd = equalExpression.sourceEnd = pE;
setGeneratedBy(equalExpression, source);
- IfStatement ifStatement = new IfStatement(equalExpression, throwStatement, 0, 0);
+ Block throwBlock = new Block(0);
+ throwBlock.statements = new Statement[] {throwStatement};
+ throwBlock.sourceStart = pS; throwBlock.sourceEnd = pE;
+ setGeneratedBy(throwBlock, source);
+ IfStatement ifStatement = new IfStatement(equalExpression, throwBlock, 0, 0);
setGeneratedBy(ifStatement, source);
return ifStatement;
}
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
new file mode 100644
index 00000000..70110a9c
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2013 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 static lombok.eclipse.Eclipse.*;
+import static lombok.core.handlers.HandlerUtil.*;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.AccessLevel;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.core.HandlerPriority;
+import lombok.core.TransformationsUtil;
+import lombok.eclipse.Eclipse;
+import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists;
+import lombok.experimental.Builder;
+import lombok.experimental.NonFinal;
+
+@ProviderFor(EclipseAnnotationHandler.class)
+@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
+public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
+ @Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) {
+ long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
+
+ Builder builderInstance = annotation.getInstance();
+ String builderMethodName = builderInstance.builderMethodName();
+ String buildMethodName = builderInstance.buildMethodName();
+ String builderClassName = builderInstance.builderClassName();
+
+ if (builderMethodName == null) builderMethodName = "builder";
+ if (buildMethodName == null) builderMethodName = "build";
+ if (builderClassName == null) builderClassName = "";
+
+ if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
+ if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
+ if (!builderClassName.isEmpty()) {
+ if (!checkName("builderClassName", builderClassName, annotationNode)) return;
+ }
+
+ EclipseNode parent = annotationNode.up();
+
+ List<TypeReference> typesOfParameters = new ArrayList<TypeReference>();
+ List<char[]> namesOfParameters = new ArrayList<char[]>();
+ TypeReference returnType;
+ TypeParameter[] typeParams;
+ TypeReference[] thrownExceptions;
+ char[] nameOfStaticBuilderMethod;
+ EclipseNode tdParent;
+
+ AbstractMethodDeclaration fillParametersFrom = null;
+
+ if (parent.get() instanceof TypeDeclaration) {
+ tdParent = parent;
+ TypeDeclaration td = (TypeDeclaration) tdParent.get();
+
+ List<EclipseNode> fields = new ArrayList<EclipseNode>();
+ @SuppressWarnings("deprecation")
+ boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
+ for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
+ FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
+ // final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes
+ // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves.
+ // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that.
+ if (fd.initialization != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue;
+ namesOfParameters.add(fd.name);
+ typesOfParameters.add(fd.type);
+ fields.add(fieldNode);
+ }
+
+ new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, fields, null, SkipIfConstructorExists.I_AM_BUILDER, true, Collections.<Annotation>emptyList(), ast);
+
+ returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p);
+ typeParams = td.typeParameters;
+ thrownExceptions = null;
+ nameOfStaticBuilderMethod = null;
+ if (builderClassName.isEmpty()) builderClassName = new String(td.name) + "Builder";
+ } else if (parent.get() instanceof ConstructorDeclaration) {
+ ConstructorDeclaration cd = (ConstructorDeclaration) parent.get();
+ if (cd.typeParameters != null && cd.typeParameters.length > 0) {
+ annotationNode.addError("@Builder is not supported on constructors with constructor type parameters.");
+ return;
+ }
+
+ tdParent = parent.up();
+ TypeDeclaration td = (TypeDeclaration) tdParent.get();
+ fillParametersFrom = cd;
+ returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p);
+ typeParams = td.typeParameters;
+ thrownExceptions = cd.thrownExceptions;
+ nameOfStaticBuilderMethod = null;
+ if (builderClassName.isEmpty()) builderClassName = new String(cd.selector) + "Builder";
+ } else if (parent.get() instanceof MethodDeclaration) {
+ MethodDeclaration md = (MethodDeclaration) parent.get();
+ tdParent = parent.up();
+ if (!md.isStatic()) {
+ annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
+ return;
+ }
+ fillParametersFrom = md;
+ returnType = copyType(md.returnType, ast);
+ typeParams = md.typeParameters;
+ thrownExceptions = md.thrownExceptions;
+ nameOfStaticBuilderMethod = md.selector;
+ if (builderClassName.isEmpty()) {
+ char[] token;
+ if (md.returnType instanceof QualifiedTypeReference) {
+ char[][] tokens = ((QualifiedTypeReference) md.returnType).tokens;
+ token = tokens[tokens.length - 1];
+ } else if (md.returnType instanceof SingleTypeReference) {
+ token = ((SingleTypeReference) md.returnType).token;
+ if (!(md.returnType instanceof ParameterizedSingleTypeReference) && typeParams != null) {
+ for (TypeParameter tp : typeParams) {
+ if (Arrays.equals(tp.name, token)) {
+ annotationNode.addError("@Builder requires specifying 'builderClassName' if used on methods with a type parameter as return type.");
+ return;
+ }
+ }
+ }
+ } else {
+ annotationNode.addError("Unexpected kind of return type on annotated method. Specify 'builderClassName' to solve this problem.");
+ return;
+ }
+
+ if (Character.isLowerCase(token[0])) {
+ char[] newToken = new char[token.length];
+ System.arraycopy(token, 1, newToken, 1, token.length - 1);
+ newToken[0] = Character.toTitleCase(token[0]);
+ token = newToken;
+ }
+
+ builderClassName = new String(token) + "Builder";
+ }
+ } else {
+ annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
+ return;
+ }
+
+ if (fillParametersFrom != null) {
+ if (fillParametersFrom.arguments != null) for (Argument a : fillParametersFrom.arguments) {
+ namesOfParameters.add(a.name);
+ typesOfParameters.add(a.type);
+ }
+ }
+
+ EclipseNode builderType = findInnerClass(tdParent, builderClassName);
+ if (builderType == null) {
+ builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
+ } else {
+ sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode);
+ }
+ List<EclipseNode> fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
+ List<AbstractMethodDeclaration> newMethods = new ArrayList<AbstractMethodDeclaration>();
+ for (EclipseNode fieldNode : fieldNodes) {
+ MethodDeclaration newMethod = makeSetterMethodForBuilder(builderType, fieldNode, ast, builderInstance.fluent(), builderInstance.chain());
+ if (newMethod != null) newMethods.add(newMethod);
+ }
+
+ if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
+ ConstructorDeclaration cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), true, ast, Collections.<Annotation>emptyList());
+ if (cd != null) injectMethod(builderType, cd);
+ }
+
+ for (AbstractMethodDeclaration newMethod : newMethods) injectMethod(builderType, newMethod);
+ if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
+ MethodDeclaration md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, namesOfParameters, builderType, ast, thrownExceptions);
+ if (md != null) injectMethod(builderType, md);
+ }
+
+ if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
+ MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD);
+ if (md != null) injectMethod(builderType, md);
+ }
+
+ if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
+ MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams, ast);
+ if (md != null) injectMethod(tdParent, md);
+ }
+ }
+
+ private MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) {
+ int pS = source.sourceStart, pE = source.sourceEnd;
+ long p = (long) pS << 32 | pE;
+
+ MethodDeclaration out = new MethodDeclaration(
+ ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ out.selector = builderMethodName.toCharArray();
+ out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
+ out.typeParameters = copyTypeParams(typeParams, source);
+ AllocationExpression invoke = new AllocationExpression();
+ invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
+ out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)};
+
+ out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
+ return out;
+ }
+
+ private MethodDeclaration generateBuildMethod(String name, char[] staticName, TypeReference returnType, List<char[]> fieldNames, EclipseNode type, ASTNode source, TypeReference[] thrownExceptions) {
+ int pS = source.sourceStart, pE = source.sourceEnd;
+ long p = (long) pS << 32 | pE;
+
+ MethodDeclaration out = new MethodDeclaration(
+ ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+
+ out.modifiers = ClassFileConstants.AccPublic;
+ TypeDeclaration typeDecl = (TypeDeclaration) type.get();
+ out.selector = name.toCharArray();
+ out.thrownExceptions = copyTypes(thrownExceptions, source);
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.returnType = returnType;
+
+ List<Expression> assigns = new ArrayList<Expression>();
+ for (char[] fieldName : fieldNames) {
+ SingleNameReference nameRef = new SingleNameReference(fieldName, p);
+ assigns.add(nameRef);
+ }
+
+ Statement statement;
+
+ if (staticName == null) {
+ AllocationExpression allocationStatement = new AllocationExpression();
+ allocationStatement.type = copyType(out.returnType, source);
+ allocationStatement.arguments = assigns.isEmpty() ? null : assigns.toArray(new Expression[assigns.size()]);
+ statement = new ReturnStatement(allocationStatement, (int)(p >> 32), (int)p);
+ } else {
+ MessageSend invoke = new MessageSend();
+ invoke.selector = staticName;
+ invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), p);
+ TypeParameter[] tps = ((TypeDeclaration) type.get()).typeParameters;
+ if (tps != null) {
+ TypeReference[] trs = new TypeReference[tps.length];
+ for (int i = 0; i < trs.length; i++) {
+ trs[i] = new SingleTypeReference(tps[i].name, p);
+ }
+ invoke.typeArguments = trs;
+ }
+ invoke.arguments = assigns.isEmpty() ? null : assigns.toArray(new Expression[assigns.size()]);
+ if (returnType instanceof SingleTypeReference && Arrays.equals(TypeBinding.VOID.simpleName, ((SingleTypeReference) returnType).token)) {
+ statement = invoke;
+ } else {
+ statement = new ReturnStatement(invoke, (int)(p >> 32), (int)p);
+ }
+ }
+
+ out.statements = new Statement[] { statement };
+
+ out.traverse(new SetGeneratedByVisitor(source), typeDecl.scope);
+ return out;
+ }
+
+ private List<EclipseNode> addFieldsToBuilder(EclipseNode builderType, List<char[]> namesOfParameters, List<TypeReference> typesOfParameters, ASTNode source) {
+ int len = namesOfParameters.size();
+ TypeDeclaration td = (TypeDeclaration) builderType.get();
+ FieldDeclaration[] existing = td.fields;
+ if (existing == null) existing = new FieldDeclaration[0];
+
+ List<EclipseNode> out = new ArrayList<EclipseNode>();
+
+ top:
+ for (int i = len - 1; i >= 0; i--) {
+ char[] name = namesOfParameters.get(i);
+ for (FieldDeclaration exists : existing) {
+ if (Arrays.equals(exists.name, name)) {
+ out.add(builderType.getNodeFor(exists));
+ continue top;
+ }
+ }
+ TypeReference fieldReference = copyType(typesOfParameters.get(i), source);
+ FieldDeclaration newField = new FieldDeclaration(name, 0, 0);
+ newField.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ newField.modifiers = ClassFileConstants.AccPrivate;
+ newField.type = fieldReference;
+ out.add(injectField(builderType, newField));
+ }
+
+ Collections.reverse(out);
+
+ return out;
+ }
+
+ private static final AbstractMethodDeclaration[] EMPTY = {};
+
+ private MethodDeclaration makeSetterMethodForBuilder(EclipseNode builderType, EclipseNode fieldNode, ASTNode source, boolean fluent, boolean chain) {
+ TypeDeclaration td = (TypeDeclaration) builderType.get();
+ AbstractMethodDeclaration[] existing = td.methods;
+ if (existing == null) existing = EMPTY;
+ int len = existing.length;
+ FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
+ char[] name = fd.name;
+
+ for (int i = 0; i < len; i++) {
+ if (!(existing[i] instanceof MethodDeclaration)) continue;
+ char[] existingName = existing[i].selector;
+ if (Arrays.equals(name, existingName)) return null;
+ }
+
+ boolean isBoolean = isBoolean(fd.type);
+ String setterName = fluent ? fieldNode.getName() : TransformationsUtil.toSetterName(null, fieldNode.getName(), isBoolean);
+
+ return HandleSetter.createSetter(td, fieldNode, setterName, chain, ClassFileConstants.AccPublic,
+ source, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
+ }
+
+ private EclipseNode findInnerClass(EclipseNode parent, String name) {
+ char[] c = name.toCharArray();
+ for (EclipseNode child : parent.down()) {
+ if (child.getKind() != Kind.TYPE) continue;
+ TypeDeclaration td = (TypeDeclaration) child.get();
+ if (Arrays.equals(td.name, c)) return child;
+ }
+ return null;
+ }
+
+ private EclipseNode makeBuilderClass(EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source) {
+ TypeDeclaration parent = (TypeDeclaration) tdParent.get();
+ TypeDeclaration builder = new TypeDeclaration(parent.compilationResult);
+ builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ builder.modifiers |= ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
+ builder.typeParameters = copyTypeParams(typeParams, source);
+ builder.name = builderClassName.toCharArray();
+ builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ return injectType(tdParent, builder);
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java
index 8ccad77f..1ae680d9 100644
--- a/src/core/lombok/eclipse/handlers/HandleConstructor.java
+++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java
@@ -39,6 +39,7 @@ import lombok.core.AnnotationValues;
import lombok.core.TransformationsUtil;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
+import lombok.experimental.Builder;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
@@ -53,18 +54,14 @@ import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
-import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
-import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.mangosdk.spi.ProviderFor;
@@ -82,7 +79,7 @@ public class HandleConstructor {
List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor=", annotationNode);
- new HandleConstructor().generateConstructor(typeNode, level, fields, staticName, false, false, onConstructor, ast);
+ new HandleConstructor().generateConstructor(typeNode, level, fields, staticName, SkipIfConstructorExists.NO, false, onConstructor, ast);
}
}
@@ -100,7 +97,7 @@ public class HandleConstructor {
List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor=", annotationNode);
- new HandleConstructor().generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, false, suppressConstructorProperties, onConstructor, ast);
+ new HandleConstructor().generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, onConstructor, ast);
}
}
@@ -117,7 +114,7 @@ public class HandleConstructor {
return fields;
}
- private static List<EclipseNode> findAllFields(EclipseNode typeNode) {
+ static List<EclipseNode> findAllFields(EclipseNode typeNode) {
List<EclipseNode> fields = new ArrayList<EclipseNode>();
for (EclipseNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD) continue;
@@ -146,7 +143,7 @@ public class HandleConstructor {
List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor=", annotationNode);
- new HandleConstructor().generateConstructor(typeNode, level, findAllFields(typeNode), staticName, false, suppressConstructorProperties, onConstructor, ast);
+ new HandleConstructor().generateConstructor(typeNode, level, findAllFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, onConstructor, ast);
}
}
@@ -164,25 +161,34 @@ public class HandleConstructor {
return true;
}
- public void generateRequiredArgsConstructor(EclipseNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists, List<Annotation> onConstructor, ASTNode source) {
+ public void generateRequiredArgsConstructor(EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, List<Annotation> onConstructor, ASTNode source) {
generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, skipIfConstructorExists, false, onConstructor, source);
}
- public void generateAllArgsConstructor(EclipseNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists, List<Annotation> onConstructor, ASTNode source) {
+ public void generateAllArgsConstructor(EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, List<Annotation> onConstructor, ASTNode source) {
generateConstructor(typeNode, level, findAllFields(typeNode), staticName, skipIfConstructorExists, false, onConstructor, source);
}
- public void generateConstructor(EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, String staticName, boolean skipIfConstructorExists, boolean suppressConstructorProperties, List<Annotation> onConstructor, ASTNode source) {
+ public enum SkipIfConstructorExists {
+ YES, NO, I_AM_BUILDER;
+ }
+
+ public void generateConstructor(EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, String staticName, SkipIfConstructorExists skipIfConstructorExists, boolean suppressConstructorProperties, List<Annotation> onConstructor, ASTNode source) {
boolean staticConstrRequired = staticName != null && !staticName.equals("");
- if (skipIfConstructorExists && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return;
- if (skipIfConstructorExists) {
+ if (skipIfConstructorExists != SkipIfConstructorExists.NO && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return;
+ if (skipIfConstructorExists != SkipIfConstructorExists.NO) {
for (EclipseNode child : typeNode.down()) {
if (child.getKind() == Kind.ANNOTATION) {
- if (annotationTypeMatches(NoArgsConstructor.class, child) ||
+ boolean skipGeneration = (annotationTypeMatches(NoArgsConstructor.class, child) ||
annotationTypeMatches(AllArgsConstructor.class, child) ||
- annotationTypeMatches(RequiredArgsConstructor.class, child)) {
-
+ annotationTypeMatches(RequiredArgsConstructor.class, child));
+
+ if (!skipGeneration && skipIfConstructorExists == SkipIfConstructorExists.YES) {
+ skipGeneration = annotationTypeMatches(Builder.class, child);
+ }
+
+ if (skipGeneration) {
if (staticConstrRequired) {
// @Data has asked us to generate a constructor, but we're going to skip this instruction, as an explicit 'make a constructor' annotation
// will take care of it. However, @Data also wants a specific static name; this will be ignored; the appropriate way to do this is to use
@@ -235,7 +241,7 @@ public class HandleConstructor {
return new Annotation[] { ann };
}
- private ConstructorDeclaration createConstructor(
+ static ConstructorDeclaration createConstructor(
AccessLevel level, EclipseNode type, Collection<EclipseNode> fields,
boolean suppressConstructorProperties, ASTNode source, List<Annotation> onConstructor) {
@@ -307,7 +313,7 @@ public class HandleConstructor {
return constructor;
}
- private boolean isLocalType(EclipseNode type) {
+ private static boolean isLocalType(EclipseNode type) {
Kind kind = type.up().getKind();
if (kind == Kind.COMPILATION_UNIT) return false;
if (kind == Kind.TYPE) return isLocalType(type.up());
@@ -321,18 +327,9 @@ public class HandleConstructor {
MethodDeclaration constructor = new MethodDeclaration(
((CompilationUnitDeclaration) type.top().get()).compilationResult);
- constructor.modifiers = toEclipseModifier(level) | Modifier.STATIC;
+ constructor.modifiers = toEclipseModifier(level) | ClassFileConstants.AccStatic;
TypeDeclaration typeDecl = (TypeDeclaration) type.get();
- if (typeDecl.typeParameters != null && typeDecl.typeParameters.length > 0) {
- TypeReference[] refs = new TypeReference[typeDecl.typeParameters.length];
- int idx = 0;
- for (TypeParameter param : typeDecl.typeParameters) {
- TypeReference typeRef = new SingleTypeReference(param.name, (long)param.sourceStart << 32 | param.sourceEnd);
- setGeneratedBy(typeRef, source);
- refs[idx++] = typeRef;
- }
- constructor.returnType = new ParameterizedSingleTypeReference(typeDecl.name, refs, 0, p);
- } else constructor.returnType = new SingleTypeReference(((TypeDeclaration)type.get()).name, p);
+ constructor.returnType = EclipseHandlerUtil.namePlusTypeParamsToTypeReference(typeDecl.name, typeDecl.typeParameters, p);
constructor.annotations = null;
constructor.selector = name.toCharArray();
constructor.thrownExceptions = null;
diff --git a/src/core/lombok/eclipse/handlers/HandleData.java b/src/core/lombok/eclipse/handlers/HandleData.java
index 3a43bd3f..aa309489 100644
--- a/src/core/lombok/eclipse/handlers/HandleData.java
+++ b/src/core/lombok/eclipse/handlers/HandleData.java
@@ -28,6 +28,7 @@ import lombok.Data;
import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
@@ -64,6 +65,6 @@ public class HandleData extends EclipseAnnotationHandler<Data> {
new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
new HandleToString().generateToStringForType(typeNode, annotationNode);
- new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), true, Collections.<Annotation>emptyList(), ast);
+ new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES, Collections.<Annotation>emptyList(), ast);
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
index 0c82b74c..6990e609 100644
--- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -204,18 +204,27 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
}
boolean isFinal = (typeDecl.modifiers & ClassFileConstants.AccFinal) != 0;
- boolean needsCanEqual = !isDirectDescendantOfObject || !isFinal;
- java.util.List<MemberExistsResult> existsResults = new ArrayList<MemberExistsResult>();
- existsResults.add(methodExists("equals", typeNode, 1));
- existsResults.add(methodExists("hashCode", typeNode, 0));
- existsResults.add(methodExists("canEqual", typeNode, 1));
- switch (Collections.max(existsResults)) {
+ boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject;
+ MemberExistsResult equalsExists = methodExists("equals", typeNode, 1);
+ MemberExistsResult hashCodeExists = methodExists("hashCode", typeNode, 0);
+ MemberExistsResult canEqualExists = methodExists("canEqual", typeNode, 1);
+ switch (Collections.max(Arrays.asList(equalsExists, hashCodeExists, canEqualExists))) {
case EXISTS_BY_LOMBOK:
return;
case EXISTS_BY_USER:
if (whineIfExists) {
String msg = String.format("Not generating equals%s: A method with one of those names already exists. (Either all or none of these methods will be generated).", needsCanEqual ? ", hashCode and canEquals" : " and hashCode");
errorNode.addWarning(msg);
+ } else if (equalsExists == MemberExistsResult.NOT_EXISTS || hashCodeExists == MemberExistsResult.NOT_EXISTS) {
+ // This means equals OR hashCode exists and not both (or neither, but canEqual is there).
+ // Even though we should suppress the message about not generating these, this is such a weird and surprising situation we should ALWAYS generate a warning.
+ // The user code couldn't possibly (barring really weird subclassing shenanigans) be in a shippable state anyway; the implementations of these 3 methods are
+ // all inter-related and should be written by the same entity.
+ String msg = String.format("Not generating %s: One of equals, hashCode, and canEqual exists. " +
+ "You should either write all of these are none of these (in the latter case, lombok generates them).",
+ equalsExists == MemberExistsResult.NOT_EXISTS && hashCodeExists == MemberExistsResult.NOT_EXISTS ? "equals and hashCode" :
+ equalsExists == MemberExistsResult.NOT_EXISTS ? "equals" : "hashCode");
+ errorNode.addWarning(msg);
}
return;
case NOT_EXISTS:
diff --git a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
index 0d21fc08..d6d839cc 100644
--- a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
+++ b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
@@ -43,7 +43,7 @@ import org.mangosdk.spi.ProviderFor;
* Handles the {@code lombok.FieldDefaults} annotation for eclipse.
*/
@ProviderFor(EclipseAnnotationHandler.class)
-@HandlerPriority(-512) //-2^9; to ensure @Setter and such pick up on messing with the fields' 'final' state, run earlier.
+@HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier.
public class HandleFieldDefaults extends EclipseAnnotationHandler<FieldDefaults> {
public boolean generateFieldDefaultsForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
if (checkForTypeLevelFieldDefaults) {
@@ -112,6 +112,14 @@ public class HandleFieldDefaults extends EclipseAnnotationHandler<FieldDefaults>
return;
}
+ if (level == AccessLevel.PACKAGE) {
+ annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field.");
+ }
+
+ if (!makeFinal && annotation.isExplicit("makeFinal")) {
+ annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field.");
+ }
+
if (node == null) return;
generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false);
diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java
index 7f788c5d..787f6f6c 100644
--- a/src/core/lombok/eclipse/handlers/HandleGetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleGetter.java
@@ -49,6 +49,8 @@ import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
import org.eclipse.jdt.internal.compiler.ast.Block;
+import org.eclipse.jdt.internal.compiler.ast.CastExpression;
+import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
@@ -67,6 +69,7 @@ import org.eclipse.jdt.internal.compiler.ast.SynchronizedStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.mangosdk.spi.ProviderFor;
/**
@@ -184,7 +187,7 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
}
TypeReference fieldType = copyType(field.type, source);
- boolean isBoolean = nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0;
+ boolean isBoolean = isBoolean(fieldType);
String getterName = toGetterName(fieldNode, isBoolean);
if (getterName == null) {
@@ -286,7 +289,6 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
}
private static final char[][] AR = fromQualifiedName("java.util.concurrent.atomic.AtomicReference");
- private static final TypeReference[][] AR_PARAMS = new TypeReference[5][];
private static final java.util.Map<String, char[][]> TYPE_MAP;
static {
@@ -305,41 +307,54 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
private static char[] valueName = "value".toCharArray();
private static char[] actualValueName = "actualValue".toCharArray();
+ private static final int PARENTHESIZED = (1 << ASTNode.ParenthesizedSHIFT) & ASTNode.ParenthesizedMASK;
+
private Statement[] createLazyGetterBody(ASTNode source, EclipseNode fieldNode) {
/*
- java.util.concurrent.atomic.AtomicReference<ValueType> value = this.fieldName.get();
+ java.lang.Object value = this.fieldName.get();
if (value == null) {
synchronized (this.fieldName) {
value = this.fieldName.get();
if (value == null) {
- final ValueType actualValue = new ValueType();
- value = new java.util.concurrent.atomic.AtomicReference<ValueType>(actualValue);
+ final RawValueType actualValue = INITIALIZER_EXPRESSION;
+ [IF PRIMITIVE]
+ value = actualValue;
+ [ELSE]
+ value = actualValue == null ? this.fieldName : actualValue;
+ [END IF]
this.fieldName.set(value);
}
}
}
- return value.get();
+ [IF PRIMITIVE]
+ return (BoxedValueType) value;
+ [ELSE]
+ return (BoxedValueType) (value == this.fieldName ? null : value);
+ [END IF]
*/
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
- TypeReference componentType = copyType(field.type, source);
+ TypeReference rawComponentType = copyType(field.type, source);
+ TypeReference boxedComponentType = null;
+ boolean isPrimitive = false;
if (field.type instanceof SingleTypeReference && !(field.type instanceof ArrayTypeReference)) {
char[][] newType = TYPE_MAP.get(new String(((SingleTypeReference)field.type).token));
if (newType != null) {
- componentType = new QualifiedTypeReference(newType, poss(source, 3));
+ boxedComponentType = new QualifiedTypeReference(newType, poss(source, 3));
+ isPrimitive = true;
}
}
+ if (boxedComponentType == null) boxedComponentType = copyType(field.type, source);
+ boxedComponentType.sourceStart = pS; boxedComponentType.sourceEnd = boxedComponentType.statementEnd = pE;
Statement[] statements = new Statement[3];
- /* java.util.concurrent.atomic.AtomicReference<ValueType> value = this.fieldName.get(); */ {
+ /* java.lang.Object value = this.fieldName.get(); */ {
LocalDeclaration valueDecl = new LocalDeclaration(valueName, pS, pE);
- TypeReference[][] typeParams = AR_PARAMS.clone();
- typeParams[4] = new TypeReference[] {copyType(componentType, source)};
- valueDecl.type = new ParameterizedQualifiedTypeReference(AR, typeParams, 0, poss(source, 5));
+ valueDecl.type = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(source, 3));
valueDecl.type.sourceStart = pS; valueDecl.type.sourceEnd = valueDecl.type.statementEnd = pE;
MessageSend getter = new MessageSend();
@@ -356,8 +371,12 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
synchronized (this.fieldName) {
value = this.fieldName.get();
if (value == null) {
- final ValueType actualValue = new ValueType();
- value = new java.util.concurrent.atomic.AtomicReference<ValueType>(actualValue);
+ final ValueType actualValue = INITIALIZER_EXPRESSION;
+ [IF PRIMITIVE]
+ value = actualValue;
+ [ELSE]
+ value = actualValue == null ? this.fieldName : actualValue;
+ [END IF]
this.fieldName.set(value);
}
}
@@ -383,28 +402,37 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
EqualExpression innerCond = new EqualExpression(
new SingleNameReference(valueName, p), new NullLiteral(pS, pE),
BinaryExpression.EQUAL_EQUAL);
+ innerCond.sourceStart = pS; innerCond.sourceEnd = innerCond.statementEnd = pE;
Block innerThen = new Block(0);
innerThen.statements = new Statement[3];
- /* final ValueType actualValue = new ValueType(); */ {
+ /* final ValueType actualValue = INITIALIZER_EXPRESSION */ {
LocalDeclaration actualValueDecl = new LocalDeclaration(actualValueName, pS, pE);
- actualValueDecl.type = copyType(field.type, source);
+ actualValueDecl.type = rawComponentType;
actualValueDecl.type.sourceStart = pS; actualValueDecl.type.sourceEnd = actualValueDecl.type.statementEnd = pE;
actualValueDecl.initialization = field.initialization;
actualValueDecl.modifiers = ClassFileConstants.AccFinal;
innerThen.statements[0] = actualValueDecl;
}
- /* value = new java.util.concurrent.atomic.AtomicReference<ValueType>(actualValue); */ {
- AllocationExpression create = new AllocationExpression();
- create.sourceStart = pS; create.sourceEnd = create.statementEnd = pE;
- TypeReference[][] typeParams = AR_PARAMS.clone();
- typeParams[4] = new TypeReference[] {copyType(componentType, source)};
- create.type = new ParameterizedQualifiedTypeReference(AR, typeParams, 0, poss(source, 5));
- create.type.sourceStart = pS; create.type.sourceEnd = create.type.statementEnd = pE;
- create.arguments = new Expression[] {new SingleNameReference(actualValueName, p)};
- Assignment innerAssign = new Assignment(new SingleNameReference(valueName, p), create, pE);
- innerAssign.sourceStart = pS; innerAssign.statementEnd = innerAssign.sourceEnd = pE;
-
- innerThen.statements[1] = innerAssign;
+ /* [IF PRIMITIVE] value = actualValue; */ {
+ if (isPrimitive) {
+ Assignment innerAssign = new Assignment(new SingleNameReference(valueName, p), new SingleNameReference(actualValueName, p), pE);
+ innerAssign.sourceStart = pS; innerAssign.statementEnd = innerAssign.sourceEnd = pE;
+ innerThen.statements[1] = innerAssign;
+ }
+ }
+ /* [ELSE] value = actualValue == null ? this.fieldName : actualValue; */ {
+ if (!isPrimitive) {
+ EqualExpression avIsNull = new EqualExpression(
+ new SingleNameReference(actualValueName, p), new NullLiteral(pS, pE),
+ BinaryExpression.EQUAL_EQUAL);
+ avIsNull.sourceStart = pS; avIsNull.sourceEnd = avIsNull.statementEnd = pE;
+ Expression fieldRef = createFieldAccessor(fieldNode, FieldAccess.ALWAYS_FIELD, source);
+ ConditionalExpression ternary = new ConditionalExpression(avIsNull, fieldRef, new SingleNameReference(actualValueName, p));
+ ternary.sourceStart = pS; ternary.sourceEnd = ternary.statementEnd = pE;
+ Assignment innerAssign = new Assignment(new SingleNameReference(valueName, p), ternary, pE);
+ innerAssign.sourceStart = pS; innerAssign.statementEnd = innerAssign.sourceEnd = pE;
+ innerThen.statements[1] = innerAssign;
+ }
}
/* this.fieldName.set(value); */ {
@@ -428,26 +456,34 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
statements[1] = ifStatement;
}
- /* return value.get(); */ {
- MessageSend getter = new MessageSend();
- getter.sourceStart = pS; getter.sourceEnd = getter.statementEnd = pE;
- getter.selector = new char[] {'g', 'e', 't'};
- getter.receiver = new SingleNameReference(valueName, p);
-
- statements[2] = new ReturnStatement(getter, pS, pE);
+ /* [IF PRIMITIVE] return (BoxedValueType)value; */ {
+ if (isPrimitive) {
+ CastExpression cast = makeCastExpression(new SingleNameReference(valueName, p), boxedComponentType, source);
+ statements[2] = new ReturnStatement(cast, pS, pE);
+ }
+ }
+ /* [ELSE] return (BoxedValueType)(value == this.fieldName ? null : value); */ {
+ if (!isPrimitive) {
+ EqualExpression vIsThisFieldName = new EqualExpression(
+ new SingleNameReference(valueName, p), createFieldAccessor(fieldNode, FieldAccess.ALWAYS_FIELD, source),
+ BinaryExpression.EQUAL_EQUAL);
+ vIsThisFieldName.sourceStart = pS; vIsThisFieldName.sourceEnd = vIsThisFieldName.statementEnd = pE;
+ ConditionalExpression ternary = new ConditionalExpression(vIsThisFieldName, new NullLiteral(pS, pE), new SingleNameReference(valueName, p));
+ ternary.sourceStart = pS; ternary.sourceEnd = ternary.statementEnd = pE;
+ ternary.bits |= PARENTHESIZED;
+ CastExpression cast = makeCastExpression(ternary, boxedComponentType, source);
+ statements[2] = new ReturnStatement(cast, pS, pE);
+ }
}
-
// update the field type and init last
- /* private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType> fieldName = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType>>(); */ {
-
- LocalDeclaration first = (LocalDeclaration) statements[0];
- TypeReference innerType = copyType(first.type, source);
-
- TypeReference[][] typeParams = AR_PARAMS.clone();
- typeParams[4] = new TypeReference[] {copyType(innerType, source)};
+ /* private final java.util.concurrent.atomic.AtomicReference<java.lang.Object> fieldName = new java.util.concurrent.atomic.AtomicReference<java.lang.Object>(); */ {
+ TypeReference innerType = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(source, 3));
+ TypeReference[][] typeParams = new TypeReference[5][];
+ typeParams[4] = new TypeReference[] {innerType};
TypeReference type = new ParameterizedQualifiedTypeReference(AR, typeParams, 0, poss(source, 5));
+
// Some magic here
type.sourceStart = -1; type.sourceEnd = -2;
diff --git a/src/core/lombok/eclipse/handlers/HandleLog.java b/src/core/lombok/eclipse/handlers/HandleLog.java
index bffe2d62..2e7b4475 100644
--- a/src/core/lombok/eclipse/handlers/HandleLog.java
+++ b/src/core/lombok/eclipse/handlers/HandleLog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2012 The Project Lombok Authors.
+ * Copyright (C) 2010-2013 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
@@ -178,6 +178,16 @@ public class HandleLog {
}
/**
+ * Handles the {@link lombok.extern.log4j.Log4j2} annotation for Eclipse.
+ */
+ @ProviderFor(EclipseAnnotationHandler.class)
+ public static class HandleLog4j2Log extends EclipseAnnotationHandler<lombok.extern.log4j.Log4j2> {
+ @Override public void handle(AnnotationValues<lombok.extern.log4j.Log4j2> annotation, Annotation source, EclipseNode annotationNode) {
+ processAnnotation(LoggingFramework.LOG4J2, annotation, source, annotationNode);
+ }
+ }
+
+ /**
* Handles the {@link lombok.extern.slf4j.Slf4j} annotation for Eclipse.
*/
@ProviderFor(EclipseAnnotationHandler.class)
@@ -224,6 +234,9 @@ public class HandleLog {
// private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(TargetType.class);
LOG4J("org.apache.log4j.Logger", "org.apache.log4j.Logger", "getLogger", "@Log4j"),
+ // private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(TargetType.class);
+ LOG4J2("org.apache.logging.log4j.Logger", "org.apache.logging.log4j.LogManager", "getLogger", "@Log4j2"),
+
// private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TargetType.class);
SLF4J("org.slf4j.Logger", "org.slf4j.LoggerFactory", "getLogger", "@Slf4j"),
diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java
index 9b46b704..3bfcc51c 100644
--- a/src/core/lombok/eclipse/handlers/HandleSetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleSetter.java
@@ -159,7 +159,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
TypeReference fieldType = copyType(field.type, source);
- boolean isBoolean = nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0;
+ boolean isBoolean = isBoolean(fieldType);
String setterName = toSetterName(fieldNode, isBoolean);
boolean shouldReturnThis = shouldReturnThis(fieldNode);
@@ -192,7 +192,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
injectMethod(fieldNode.up(), method);
}
- private MethodDeclaration createSetter(TypeDeclaration parent, EclipseNode fieldNode, String name, boolean shouldReturnThis, int modifier, ASTNode source, List<Annotation> onMethod, List<Annotation> onParam) {
+ static MethodDeclaration createSetter(TypeDeclaration parent, EclipseNode fieldNode, String name, boolean shouldReturnThis, int modifier, ASTNode source, List<Annotation> onMethod, List<Annotation> onParam) {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
diff --git a/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java b/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java
index b7c8a5d8..aa78ca3b 100644
--- a/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java
+++ b/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java
@@ -40,6 +40,8 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.Block;
+import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
@@ -147,7 +149,21 @@ public class HandleSneakyThrows extends EclipseAnnotationHandler<SneakyThrows> {
return;
}
- if (method.statements == null) return;
+ if (method.statements == null || method.statements.length == 0) {
+ boolean hasConstructorCall = false;
+ if (method instanceof ConstructorDeclaration) {
+ ExplicitConstructorCall constructorCall = ((ConstructorDeclaration) method).constructorCall;
+ hasConstructorCall = constructorCall != null && !constructorCall.isImplicitSuper() && !constructorCall.isImplicitThis();
+ }
+
+ if (hasConstructorCall) {
+ annotation.addWarning("Calls to sibling / super constructors are always excluded from @SneakyThrows; @SneakyThrows has been ignored because there is no other code in this constructor.");
+ } else {
+ annotation.addWarning("This method or constructor is empty; @SneakyThrows has been ignored.");
+ }
+
+ return;
+ }
Statement[] contents = method.statements;
@@ -160,9 +176,9 @@ public class HandleSneakyThrows extends EclipseAnnotationHandler<SneakyThrows> {
}
private Statement buildTryCatchBlock(Statement[] contents, DeclaredException exception, ASTNode source, AbstractMethodDeclaration method) {
- int methodStart = method.bodyStart;
- int methodEnd = method.bodyEnd;
- long methodPosEnd = methodEnd << 32 | (methodEnd & 0xFFFFFFFFL);
+ int methodStart = method.bodyStart;
+ int methodEnd = method.bodyEnd;
+ long methodPosEnd = ((long) methodEnd) << 32 | (methodEnd & 0xFFFFFFFFL);
TryStatement tryStatement = new TryStatement();
setGeneratedBy(tryStatement, source);
diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java
index 75d4acef..1193af31 100644
--- a/src/core/lombok/eclipse/handlers/HandleToString.java
+++ b/src/core/lombok/eclipse/handlers/HandleToString.java
@@ -170,7 +170,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
}
}
- private MethodDeclaration createToString(EclipseNode type, Collection<EclipseNode> fields,
+ static MethodDeclaration createToString(EclipseNode type, Collection<EclipseNode> fields,
boolean includeFieldNames, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
String typeName = getTypeName(type);
char[] suffix = ")".toCharArray();
@@ -209,21 +209,25 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
}
for (EclipseNode field : fields) {
- TypeReference fType = getFieldType(field, fieldAccess);
+ TypeReference fieldType = getFieldType(field, fieldAccess);
Expression fieldAccessor = createFieldAccessor(field, fieldAccess, source);
+ // The distinction between primitive and object will be useful if we ever add a 'hideNulls' option.
+ boolean fieldBaseTypeIsPrimitive = BUILT_IN_TYPES.contains(new String(fieldType.getLastToken()));
+ boolean fieldIsPrimitive = fieldType.dimensions() == 0 && fieldBaseTypeIsPrimitive;
+ boolean fieldIsPrimitiveArray = fieldType.dimensions() == 1 && fieldBaseTypeIsPrimitive;
+ boolean fieldIsObjectArray = fieldType.dimensions() > 0 && !fieldIsPrimitiveArray;
+ @SuppressWarnings("unused")
+ boolean fieldIsObject = !fieldIsPrimitive && !fieldIsPrimitiveArray && !fieldIsObjectArray;
+
Expression ex;
- if (fType.dimensions() > 0) {
+ if (fieldIsPrimitiveArray || fieldIsObjectArray) {
MessageSend arrayToString = new MessageSend();
arrayToString.sourceStart = pS; arrayToString.sourceEnd = pE;
arrayToString.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Arrays".toCharArray());
arrayToString.arguments = new Expression[] { fieldAccessor };
setGeneratedBy(arrayToString.arguments[0], source);
- if (fType.dimensions() > 1 || !BUILT_IN_TYPES.contains(new String(fType.getLastToken()))) {
- arrayToString.selector = "deepToString".toCharArray();
- } else {
- arrayToString.selector = "toString".toCharArray();
- }
+ arrayToString.selector = (fieldIsObjectArray ? "deepToString" : "toString").toCharArray();
ex = arrayToString;
} else {
ex = fieldAccessor;
@@ -278,7 +282,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
return method;
}
- private String getTypeName(EclipseNode type) {
+ private static String getTypeName(EclipseNode type) {
String typeName = getSingleTypeName(type);
EclipseNode upType = type.up();
while (upType.getKind() == Kind.TYPE) {
@@ -288,7 +292,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
return typeName;
}
- private String getSingleTypeName(EclipseNode type) {
+ private static String getSingleTypeName(EclipseNode type) {
TypeDeclaration typeDeclaration = (TypeDeclaration)type.get();
char[] rawTypeName = typeDeclaration.name;
return rawTypeName == null ? "" : new String(rawTypeName);
@@ -297,7 +301,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
private static final Set<String> BUILT_IN_TYPES = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
"byte", "short", "int", "long", "char", "boolean", "double", "float")));
- private NameReference generateQualifiedNameRef(ASTNode source, char[]... varNames) {
+ private static NameReference generateQualifiedNameRef(ASTNode source, char[]... varNames) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
NameReference ref;
diff --git a/src/core/lombok/eclipse/handlers/HandleValue.java b/src/core/lombok/eclipse/handlers/HandleValue.java
index b69b1669..0607137b 100644
--- a/src/core/lombok/eclipse/handlers/HandleValue.java
+++ b/src/core/lombok/eclipse/handlers/HandleValue.java
@@ -30,8 +30,9 @@ import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists;
import lombok.experimental.NonFinal;
-import lombok.experimental.Value;
+import lombok.Value;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
@@ -78,6 +79,6 @@ public class HandleValue extends EclipseAnnotationHandler<Value> {
new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
new HandleToString().generateToStringForType(typeNode, annotationNode);
- new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), true, Collections.<Annotation>emptyList(), ast);
+ new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES, Collections.<Annotation>emptyList(), ast);
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleWither.java b/src/core/lombok/eclipse/handlers/HandleWither.java
index 9d74cbd1..27fbc635 100644
--- a/src/core/lombok/eclipse/handlers/HandleWither.java
+++ b/src/core/lombok/eclipse/handlers/HandleWither.java
@@ -160,7 +160,7 @@ public class HandleWither extends EclipseAnnotationHandler<Wither> {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
TypeReference fieldType = copyType(field.type, source);
- boolean isBoolean = nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0;
+ boolean isBoolean = isBoolean(fieldType);
String witherName = toWitherName(fieldNode, isBoolean);
if (witherName == null) {
diff --git a/src/core/lombok/eclipse/handlers/NonNullHandler.java b/src/core/lombok/eclipse/handlers/NonNullHandler.java
new file mode 100644
index 00000000..5c58069c
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/NonNullHandler.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2013 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.Arrays;
+
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.Block;
+import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.IfStatement;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
+import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThrowStatement;
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.NonNull;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.eclipse.DeferUntilPostDiet;
+import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.EclipseNode;
+
+import static lombok.eclipse.Eclipse.*;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+@DeferUntilPostDiet
+@ProviderFor(EclipseAnnotationHandler.class)
+public class NonNullHandler extends EclipseAnnotationHandler<NonNull> {
+ @Override public void handle(AnnotationValues<NonNull> annotation, Annotation ast, EclipseNode annotationNode) {
+ if (annotationNode.up().getKind() == Kind.FIELD) {
+ // This is meaningless unless the field is used to generate a method (@Setter, @RequiredArgsConstructor, etc),
+ // but in that case those handlers will take care of it. However, we DO check if the annotation is applied to
+ // a primitive, because those handlers trigger on any annotation named @NonNull and we only want the warning
+ // behaviour on _OUR_ 'lombok.NonNull'.
+
+ try {
+ if (isPrimitive(((AbstractVariableDeclaration) annotationNode.up().get()).type)) {
+ annotationNode.addWarning("@NonNull is meaningless on a primitive.");
+ }
+ } catch (Exception ignore) {}
+
+ return;
+ }
+
+ if (annotationNode.up().getKind() != Kind.ARGUMENT) return;
+
+ Argument arg;
+ AbstractMethodDeclaration declaration;
+
+ try {
+ arg = (Argument) annotationNode.up().get();
+ declaration = (AbstractMethodDeclaration) annotationNode.up().up().get();
+ } catch (Exception e) {
+ return;
+ }
+
+ if (isGenerated(declaration)) return;
+
+ // Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter,
+ // and if they exist, create a new method in the class: 'private static <T> T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and
+ // wrap all references to it in the super/this to a call to this method.
+
+ Statement nullCheck = generateNullCheck(arg, ast);
+
+ if (nullCheck == null) {
+ // @NonNull applied to a primitive. Kinda pointless. Let's generate a warning.
+ annotationNode.addWarning("@NonNull is meaningless on a primitive.");
+ return;
+ }
+
+ if (declaration.statements == null) {
+ declaration.statements = new Statement[] {nullCheck};
+ } else {
+ char[] expectedName = arg.name;
+ for (Statement stat : declaration.statements) {
+ char[] varNameOfNullCheck = returnVarNameIfNullCheck(stat);
+ if (varNameOfNullCheck == null) break;
+ if (Arrays.equals(expectedName, varNameOfNullCheck)) return;
+ }
+
+ Statement[] newStatements = new Statement[declaration.statements.length + 1];
+ int skipOver = 0;
+ for (Statement stat : declaration.statements) {
+ if (isGenerated(stat)) skipOver++;
+ else break;
+ }
+ System.arraycopy(declaration.statements, 0, newStatements, 0, skipOver);
+ System.arraycopy(declaration.statements, skipOver, newStatements, skipOver + 1, declaration.statements.length - skipOver);
+ newStatements[skipOver] = nullCheck;
+ declaration.statements = newStatements;
+ }
+ annotationNode.up().up().rebuild();
+ }
+
+ private char[] returnVarNameIfNullCheck(Statement stat) {
+ if (!(stat instanceof IfStatement)) return null;
+
+ /* Check that the if's statement is a throw statement, possibly in a block. */ {
+ Statement then = ((IfStatement) stat).thenStatement;
+ if (then instanceof Block) {
+ Statement[] blockStatements = ((Block) then).statements;
+ if (blockStatements == null || blockStatements.length == 0) return null;
+ then = blockStatements[0];
+ }
+
+ if (!(then instanceof ThrowStatement)) return null;
+ }
+
+ /* Check that the if's conditional is like 'x == null'. Return from this method (don't generate
+ a nullcheck) if 'x' is equal to our own variable's name: There's already a nullcheck here. */ {
+ Expression cond = ((IfStatement) stat).condition;
+ if (!(cond instanceof EqualExpression)) return null;
+ EqualExpression bin = (EqualExpression) cond;
+ int operatorId = ((bin.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT);
+ if (operatorId != OperatorIds.EQUAL_EQUAL) return null;
+ if (!(bin.left instanceof SingleNameReference)) return null;
+ if (!(bin.right instanceof NullLiteral)) return null;
+ return ((SingleNameReference) bin.left).token;
+ }
+ }
+}
diff --git a/src/core/lombok/experimental/Builder.java b/src/core/lombok/experimental/Builder.java
new file mode 100644
index 00000000..1300e7d3
--- /dev/null
+++ b/src/core/lombok/experimental/Builder.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2013 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.experimental;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * The builder annotation creates a so-called 'builder' aspect to the class that is annotated or the class
+ * that contains a member which is annotated with {@code @Builder}.
+ * <p>
+ * If a member is annotated, it must be either a constructor or a static method. If a class is annotated,
+ * then a private constructor is generated with all fields as arguments
+ * (as if {@code @AllArgsConstructor(AccessLevel.PRIVATE)} is present
+ * on the class), and it is as if this constructor has been annotated with {@code @Builder} instead.
+ * <p>
+ * The effect of {@code @Builder} is that an inner class is generated named <code><strong>T</strong>Builder</code>,
+ * with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the static
+ * method named {@code builder()} which is also generated for you in the class itself (not in the builder class).
+ * <p>
+ * The <code><strong>T</strong>Builder</code> class contains 1 method for each parameter of the annotated
+ * constructor / static method (each field, when annotating a class), which returns the builder itself.
+ * The builder also has a <code>build()</code> method which returns a completed instance of the original type,
+ * created by passing all parameters as set via the various other methods in the builder to the constructor
+ * or static method that was annotated with {@code @Builder}. The return type of this method will be the same
+ * as the relevant class, unless a static method has been annotated, in which case it'll be equal to the
+ * return type of that method.
+ * <p>
+ * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Builder.html">the project lombok features page for &#64;Builder</a>.
+ * <p>
+ * <p>
+ * Before:
+ *
+ * <pre>
+ * &#064;Builder
+ * class Example {
+ * private int foo;
+ * private final String bar;
+ * }
+ * </pre>
+ *
+ * After:
+ *
+ * <pre>
+ * class Example&lt;T&gt; {
+ * private T foo;
+ * private final String bar;
+ *
+ * private Example(T foo, String bar) {
+ * this.foo = foo;
+ * this.bar = bar;
+ * }
+ *
+ * public static &lt;T&gt; ExampleBuilder&lt;T&gt; builder() {
+ * return new ExampleBuilder&lt;T&gt;();
+ * }
+ *
+ * public static class ExampleBuilder&lt;T&gt; {
+ * private T foo;
+ * private String bar;
+ *
+ * private ExampleBuilder() {}
+ *
+ * public ExampleBuilder foo(T foo) {
+ * this.foo = foo;
+ * return this;
+ * }
+ *
+ * public ExampleBuilder bar(String bar) {
+ * this.bar = bar;
+ * return this;
+ * }
+ *
+ * &#064;java.lang.Override public String toString() {
+ * return "ExampleBuilder(foo = " + foo + ", bar = " + bar + ")";
+ * }
+ *
+ * public Example build() {
+ * return new Example(foo, bar);
+ * }
+ * }
+ * }
+ * </pre>
+ */
+@Target({TYPE, METHOD, CONSTRUCTOR})
+@Retention(SOURCE)
+public @interface Builder {
+ /** Name of the static method that creates a new builder instance. Default: {@code builder}. */
+ String builderMethodName() default "builder";
+
+ /** Name of the instance method in the builder class that creates an instance of your {@code @Builder}-annotated class. */
+ String buildMethodName() default "build";
+
+ /** Name of the builder class.
+ * Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}.
+ * Default for {@code @Builder} on static methods: {@code (ReturnTypeName)Builder}.
+ */
+ String builderClassName() default "";
+
+ /**
+ * Normally the builder's 'set' methods are fluent, meaning, they have the same name as the field. Set this
+ * to {@code false} to name the setter method for field {@code someField}: {@code setSomeField}.
+ * <p>
+ * <strong>Default: true</strong>
+ */
+ boolean fluent() default true;
+
+ /**
+ * Normally the builder's 'set' methods are chaining, meaning, they return the builder so that you can chain
+ * calls to set methods. Set this to {@code false} to have these 'set' methods return {@code void} instead.
+ * <p>
+ * <strong>Default: true</strong>
+ */
+ boolean chain() default true;
+}
diff --git a/src/core/lombok/experimental/Value.java b/src/core/lombok/experimental/Value.java
index 048066df..b7700bb5 100644
--- a/src/core/lombok/experimental/Value.java
+++ b/src/core/lombok/experimental/Value.java
@@ -39,9 +39,11 @@ import java.lang.annotation.Target;
* @see lombok.ToString
* @see lombok.EqualsAndHashCode
* @see lombok.Data
+ * @deprecated {@link lombok.Value} has been promoted to the main package, so use that one instead.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
+@Deprecated
public @interface Value {
/**
* If you specify a static constructor name, then the generated constructor will be private, and
diff --git a/src/core/lombok/extern/apachecommons/CommonsLog.java b/src/core/lombok/extern/apachecommons/CommonsLog.java
index f178ae05..024e3744 100644
--- a/src/core/lombok/extern/apachecommons/CommonsLog.java
+++ b/src/core/lombok/extern/apachecommons/CommonsLog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2012 The Project Lombok Authors.
+ * Copyright (C) 2010-2013 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
@@ -52,6 +52,7 @@ import java.lang.annotation.Target;
* @see org.apache.commons.logging.LogFactory#getLog(java.lang.Class) org.apache.commons.logging.LogFactory.getLog(Class target)
* @see lombok.extern.java.Log &#64;Log
* @see lombok.extern.log4j.Log4j &#64;Log4j
+ * @see lombok.extern.log4j.Log4j2 &#64;Log4j2
* @see lombok.extern.slf4j.Slf4j &#64;Slf4j
* @see lombok.extern.slf4j.XSlf4j &#64;XSlf4j
*/
diff --git a/src/core/lombok/extern/java/Log.java b/src/core/lombok/extern/java/Log.java
index 90c62956..7ae4e07b 100644
--- a/src/core/lombok/extern/java/Log.java
+++ b/src/core/lombok/extern/java/Log.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2012 The Project Lombok Authors.
+ * Copyright (C) 2010-2013 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
@@ -51,6 +51,7 @@ import java.lang.annotation.Target;
* @see java.util.logging.Logger#getLogger(java.lang.String) java.util.logging.Logger.getLogger(String name)
* @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
* @see lombok.extern.log4j.Log4j &#64;Log4j
+ * @see lombok.extern.log4j.Log4j2 &#64;Log4j2
* @see lombok.extern.slf4j.Slf4j &#64;Slf4j
* @see lombok.extern.slf4j.XSlf4j &#64;XSlf4j
*/
diff --git a/src/core/lombok/extern/log4j/Log4j.java b/src/core/lombok/extern/log4j/Log4j.java
index 9cfc5839..29e1b27c 100644
--- a/src/core/lombok/extern/log4j/Log4j.java
+++ b/src/core/lombok/extern/log4j/Log4j.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2012 The Project Lombok Authors.
+ * Copyright (C) 2010-2013 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
@@ -50,6 +50,7 @@ import java.lang.annotation.Target;
*
* @see org.apache.log4j.Logger org.apache.log4j.Logger
* @see org.apache.log4j.Logger#getLogger(java.lang.Class) org.apache.log4j.Logger.getLogger(Class target)
+ * @see lombok.extern.log4j.Log4j2 &#64;Log4j2
* @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
* @see lombok.extern.java.Log &#64;Log
* @see lombok.extern.slf4j.Slf4j &#64;Slf4j
diff --git a/src/core/lombok/extern/log4j/Log4j2.java b/src/core/lombok/extern/log4j/Log4j2.java
new file mode 100644
index 00000000..2a0f09e1
--- /dev/null
+++ b/src/core/lombok/extern/log4j/Log4j2.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 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.extern.log4j;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Causes lombok to generate a logger field.
+ * <p>
+ * Complete documentation is found at <a href="http://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
+ * <p>
+ * Example:
+ * <pre>
+ * &#64;Log4j2
+ * public class LogExample {
+ * }
+ * </pre>
+ *
+ * will generate:
+ *
+ * <pre>
+ * public class LogExample {
+ * private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.Logger.getLogger(LogExample.class);
+ * }
+ * </pre>
+ *
+ * This annotation is valid for classes and enumerations.<br />
+ *
+ * @see org.apache.logging.log4j.Logger org.apache.logging.log4j.Logger
+ * @see org.apache.logging.log4j.LogManager#getLogger(java.lang.Class) org.apache.logging.log4j.LogManager.getLogger(Class target)
+ * @see lombok.extern.log4j.Log4j &#64;Log4j
+ * @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
+ * @see lombok.extern.java.Log &#64;Log
+ * @see lombok.extern.slf4j.Slf4j &#64;Slf4j
+ * @see lombok.extern.slf4j.XSlf4j &#64;XSlf4j
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.TYPE)
+public @interface Log4j2 {
+} \ No newline at end of file
diff --git a/src/core/lombok/extern/slf4j/Slf4j.java b/src/core/lombok/extern/slf4j/Slf4j.java
index 14dbcba6..45942971 100644
--- a/src/core/lombok/extern/slf4j/Slf4j.java
+++ b/src/core/lombok/extern/slf4j/Slf4j.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2012 The Project Lombok Authors.
+ * Copyright (C) 2010-2013 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
@@ -51,6 +51,7 @@ import java.lang.annotation.Target;
* @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
* @see lombok.extern.java.Log &#64;Log
* @see lombok.extern.log4j.Log4j &#64;Log4j
+ * @see lombok.extern.log4j.Log4j2 &#64;Log4j2
*/
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
diff --git a/src/core/lombok/extern/slf4j/XSlf4j.java b/src/core/lombok/extern/slf4j/XSlf4j.java
index bdf8a62c..599c68ab 100644
--- a/src/core/lombok/extern/slf4j/XSlf4j.java
+++ b/src/core/lombok/extern/slf4j/XSlf4j.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Project Lombok Authors.
+ * Copyright (C) 2012-2013 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
@@ -51,6 +51,7 @@ import java.lang.annotation.Target;
* @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
* @see lombok.extern.java.Log &#64;Log
* @see lombok.extern.log4j.Log4j &#64;Log4j
+ * @see lombok.extern.log4j.Log4j2 &#64;Log4j2
* @see lombok.extern.slf4j.Slf4j &#64;Slf4j
*/
@Retention(RetentionPolicy.SOURCE)
diff --git a/src/core/lombok/javac/FindTypeVarScanner.java b/src/core/lombok/javac/FindTypeVarScanner.java
index b1b8e525..7c7d9d50 100644
--- a/src/core/lombok/javac/FindTypeVarScanner.java
+++ b/src/core/lombok/javac/FindTypeVarScanner.java
@@ -88,7 +88,7 @@ public class FindTypeVarScanner extends AbstractTypeVisitor6<Void, Void> {
@Override public Void visitTypeVariable(TypeVariable t, Void p) {
Name name = null;
try {
- name = ((Type)t).tsym.name;
+ name = ((Type) t).tsym.name;
} catch (NullPointerException e) {}
if (name != null) typeVariables.add(name.toString());
subVisit(t.getLowerBound());
diff --git a/src/core/lombok/javac/HandlerLibrary.java b/src/core/lombok/javac/HandlerLibrary.java
index 2be84355..4306b5f2 100644
--- a/src/core/lombok/javac/HandlerLibrary.java
+++ b/src/core/lombok/javac/HandlerLibrary.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -231,23 +231,23 @@ public class HandlerLibrary {
* @param annotation 'node.get()' - convenience parameter.
*/
public void handleAnnotation(JCCompilationUnit unit, JavacNode node, JCAnnotation annotation, long priority) {
- TypeResolver resolver = new TypeResolver(node.getPackageDeclaration(), node.getImportStatements());
+ TypeResolver resolver = new TypeResolver(node.getImportList());
String rawType = annotation.annotationType.toString();
- for (String fqn : resolver.findTypeMatches(node, typeLibrary, rawType)) {
- AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
- if (container == null) continue;
-
- try {
- if (container.getPriority() == priority) {
- if (checkAndSetHandled(annotation)) container.handle(node);
- }
- } catch (AnnotationValueDecodeFail fail) {
- fail.owner.setError(fail.getMessage(), fail.idx);
- } catch (Throwable t) {
- String sourceName = "(unknown).java";
- if (unit != null && unit.sourcefile != null) sourceName = unit.sourcefile.getName();
- javacError(String.format("Lombok annotation handler %s failed on " + sourceName, container.handler.getClass()), t);
+ String fqn = resolver.typeRefToFullyQualifiedName(node, typeLibrary, rawType);
+ if (fqn == null) return;
+ AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
+ if (container == null) return;
+
+ try {
+ if (container.getPriority() == priority) {
+ if (checkAndSetHandled(annotation)) container.handle(node);
}
+ } catch (AnnotationValueDecodeFail fail) {
+ fail.owner.setError(fail.getMessage(), fail.idx);
+ } catch (Throwable t) {
+ String sourceName = "(unknown).java";
+ if (unit != null && unit.sourcefile != null) sourceName = unit.sourcefile.getName();
+ javacError(String.format("Lombok annotation handler %s failed on " + sourceName, container.handler.getClass()), t);
}
}
diff --git a/src/core/lombok/javac/Javac6BasedLombokOptions.java b/src/core/lombok/javac/Javac6BasedLombokOptions.java
index 4bb2bdc3..871e41c4 100644
--- a/src/core/lombok/javac/Javac6BasedLombokOptions.java
+++ b/src/core/lombok/javac/Javac6BasedLombokOptions.java
@@ -26,7 +26,6 @@ import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Options;
public class Javac6BasedLombokOptions extends LombokOptions {
-
public static Javac6BasedLombokOptions replaceWithDelombokOptions(Context context) {
Options options = Options.instance(context);
context.put(optionsKey, (Options)null);
@@ -38,7 +37,7 @@ public class Javac6BasedLombokOptions extends LombokOptions {
private Javac6BasedLombokOptions(Context context) {
super(context);
}
-
+
@Override public void putJavacOption(String optionName, String value) {
put(OptionName.valueOf(optionName), value);
}
diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java
index 71c17538..36c51210 100644
--- a/src/core/lombok/javac/JavacAST.java
+++ b/src/core/lombok/javac/JavacAST.java
@@ -24,6 +24,7 @@ package lombok.javac;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import javax.annotation.processing.Messager;
@@ -34,10 +35,12 @@ import javax.tools.JavaFileObject;
import lombok.core.AST;
import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.Source;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCCatch;
+import com.sun.tools.javac.tree.JCTree.JCTry;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
@@ -46,7 +49,6 @@ import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
-import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
@@ -78,7 +80,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
* @param top The compilation unit, which serves as the top level node in the tree to be built.
*/
public JavacAST(Messager messager, Context context, JCCompilationUnit top) {
- super(sourceName(top), packageDeclaration(top), imports(top));
+ super(sourceName(top), packageDeclaration(top), new JavacImportList(top));
setTop(buildCompilationUnit(top));
this.context = context;
this.messager = messager;
@@ -98,16 +100,6 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
return (cu.pid instanceof JCFieldAccess || cu.pid instanceof JCIdent) ? cu.pid.toString() : null;
}
- private static Collection<String> imports(JCCompilationUnit cu) {
- List<String> imports = new ArrayList<String>();
- for (JCTree def : cu.defs) {
- if (def instanceof JCImport) {
- imports.add(((JCImport)def).qualid.toString());
- }
- }
- return imports;
- }
-
public Context getContext() {
return context;
}
@@ -121,9 +113,20 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
}
void traverseChildren(JavacASTVisitor visitor, JavacNode node) {
- for (JavacNode child : new ArrayList<JavacNode>(node.down())) {
- child.traverse(visitor);
- }
+ for (JavacNode child : node.down()) child.traverse(visitor);
+ }
+
+ @Override public int getSourceVersion() {
+ try {
+ String nm = Source.instance(context).name();
+ int underscoreIdx = nm.indexOf('_');
+ if (underscoreIdx > -1) return Integer.parseInt(nm.substring(underscoreIdx + 1));
+ } catch (Exception ignore) {}
+ return 6;
+ }
+
+ @Override public int getLatestJavaSpecSupported() {
+ return Javac.getJavaCompilerVersion();
}
/** @return A Name object generated for the proper name table belonging to this AST. */
@@ -223,6 +226,46 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
return putInMap(new JavacNode(this, local, childNodes, kind));
}
+ private static boolean JCTRY_RESOURCES_FIELD_INITIALIZED;
+ private static Field JCTRY_RESOURCES_FIELD;
+
+ @SuppressWarnings("unchecked")
+ private static List<JCTree> getResourcesForTryNode(JCTry tryNode) {
+ if (!JCTRY_RESOURCES_FIELD_INITIALIZED) {
+ try {
+ JCTRY_RESOURCES_FIELD = JCTry.class.getField("resources");
+ } catch (NoSuchFieldException ignore) {
+ // Java 1.6 or lower won't have this at all.
+ } catch (Exception ignore) {
+ // Shouldn't happen. Best thing we can do is just carry on and break on try/catch.
+ }
+ JCTRY_RESOURCES_FIELD_INITIALIZED = true;
+ }
+
+ if (JCTRY_RESOURCES_FIELD == null) return Collections.emptyList();
+ Object rv = null;
+ try {
+ rv = JCTRY_RESOURCES_FIELD.get(tryNode);
+ } catch (Exception ignore) {}
+
+ if (rv instanceof List) return (List<JCTree>) rv;
+ return Collections.emptyList();
+ }
+
+ private JavacNode buildTry(JCTry tryNode) {
+ if (setAndGetAsHandled(tryNode)) return null;
+ List<JavacNode> childNodes = new ArrayList<JavacNode>();
+ for (JCTree varDecl : getResourcesForTryNode(tryNode)) {
+ if (varDecl instanceof JCVariableDecl) {
+ addIfNotNull(childNodes, buildLocalVar((JCVariableDecl) varDecl, Kind.LOCAL));
+ }
+ }
+ addIfNotNull(childNodes, buildStatement(tryNode.body));
+ for (JCCatch jcc : tryNode.catchers) addIfNotNull(childNodes, buildTree(jcc, Kind.STATEMENT));
+ addIfNotNull(childNodes, buildStatement(tryNode.finalizer));
+ return putInMap(new JavacNode(this, tryNode, childNodes, Kind.STATEMENT));
+ }
+
private JavacNode buildInitializer(JCBlock initializer) {
if (setAndGetAsHandled(initializer)) return null;
List<JavacNode> childNodes = new ArrayList<JavacNode>();
@@ -264,6 +307,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
if (statement instanceof JCAnnotation) return null;
if (statement instanceof JCClassDecl) return buildType((JCClassDecl)statement);
if (statement instanceof JCVariableDecl) return buildLocalVar((JCVariableDecl)statement, Kind.LOCAL);
+ if (statement instanceof JCTry) return buildTry((JCTry) statement);
if (setAndGetAsHandled(statement)) return null;
@@ -271,9 +315,18 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
}
private JavacNode drill(JCTree statement) {
- List<JavacNode> childNodes = new ArrayList<JavacNode>();
- for (FieldAccess fa : fieldsOf(statement.getClass())) childNodes.addAll(buildWithField(JavacNode.class, statement, fa));
- return putInMap(new JavacNode(this, statement, childNodes, Kind.STATEMENT));
+ try {
+ List<JavacNode> childNodes = new ArrayList<JavacNode>();
+ for (FieldAccess fa : fieldsOf(statement.getClass())) childNodes.addAll(buildWithField(JavacNode.class, statement, fa));
+ return putInMap(new JavacNode(this, statement, childNodes, Kind.STATEMENT));
+ } catch (OutOfMemoryError oome) {
+ String msg = oome.getMessage();
+ if (msg == null) msg = "(no original message)";
+ OutOfMemoryError newError = new OutOfMemoryError(getFileName() + "@pos" + statement.getPreferredPosition() + ": " + msg);
+ // We could try to set the stack trace of the new exception to the same one as the old exception, but this costs memory,
+ // and we're already in an extremely fragile situation in regards to remaining heap space, so let's not do that.
+ throw newError;
+ }
}
/** For javac, both JCExpression and JCStatement are considered as valid children types. */
diff --git a/src/core/lombok/javac/JavacAnnotationHandler.java b/src/core/lombok/javac/JavacAnnotationHandler.java
index 169e2026..a86aa6c6 100644
--- a/src/core/lombok/javac/JavacAnnotationHandler.java
+++ b/src/core/lombok/javac/JavacAnnotationHandler.java
@@ -33,7 +33,7 @@ import com.sun.tools.javac.tree.JCTree.JCAnnotation;
*
* You MUST replace 'T' with a specific annotation type, such as:
*
- * {@code public class HandleGetter implements JavacAnnotationHandler<Getter>}
+ * {@code public class HandleGetter extends JavacAnnotationHandler<Getter>}
*
* Because this generics parameter is inspected to figure out which class you're interested in.
*
diff --git a/src/core/lombok/javac/JavacImportList.java b/src/core/lombok/javac/JavacImportList.java
new file mode 100644
index 00000000..d5d7460a
--- /dev/null
+++ b/src/core/lombok/javac/JavacImportList.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 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.javac;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
+import com.sun.tools.javac.tree.JCTree.JCImport;
+import com.sun.tools.javac.util.List;
+
+import lombok.core.ImportList;
+import lombok.core.LombokInternalAliasing;
+
+public class JavacImportList implements ImportList {
+ private final JCExpression pkg;
+ private final List<JCTree> defs;
+
+ public JavacImportList(JCCompilationUnit cud) {
+ this.pkg = cud.pid;
+ this.defs = cud.defs;
+ }
+
+ @Override public String getFullyQualifiedNameForSimpleName(String unqualified) {
+ for (JCTree def : defs) {
+ if (!(def instanceof JCImport)) continue;
+ JCTree qual = ((JCImport) def).qualid;
+ if (!(qual instanceof JCFieldAccess)) continue;
+ String simpleName = ((JCFieldAccess) qual).name.toString();
+ if (simpleName.equals(unqualified)) {
+ return LombokInternalAliasing.processAliases(qual.toString());
+ }
+ }
+
+ return null;
+ }
+
+ @Override public boolean hasStarImport(String packageName) {
+ for (Map.Entry<String, String> e : LombokInternalAliasing.IMPLIED_EXTRA_STAR_IMPORTS.entrySet()) {
+ if (e.getValue().equals(packageName) && hasStarImport(e.getKey())) return true;
+ }
+ if (pkg != null && pkg.toString().equals(packageName)) return true;
+ if ("java.lang".equals(packageName)) return true;
+
+ for (JCTree def : defs) {
+ if (!(def instanceof JCImport)) continue;
+ if (((JCImport) def).staticImport) continue;
+ JCTree qual = ((JCImport) def).qualid;
+ if (!(qual instanceof JCFieldAccess)) continue;
+ String simpleName = ((JCFieldAccess) qual).name.toString();
+ if (!"*".equals(simpleName)) continue;
+ if (packageName.equals(((JCFieldAccess) qual).selected.toString())) return true;
+ }
+
+ return false;
+ }
+
+ @Override public Collection<String> applyNameToStarImports(String startsWith, String name) {
+ ArrayList<String> out = new ArrayList<String>();
+
+ if (pkg != null && topLevelName(pkg).equals(startsWith)) out.add(pkg.toString() + "." + name);
+
+ for (JCTree def : defs) {
+ if (!(def instanceof JCImport)) continue;
+ if (((JCImport) def).staticImport) continue;
+ JCTree qual = ((JCImport) def).qualid;
+ if (!(qual instanceof JCFieldAccess)) continue;
+ String simpleName = ((JCFieldAccess) qual).name.toString();
+ if (!"*".equals(simpleName)) continue;
+
+ String topLevelName = topLevelName(qual);
+ if (topLevelName.equals(startsWith)) {
+ out.add(((JCFieldAccess) qual).selected.toString() + "." + name);
+ }
+ }
+
+ return out;
+ }
+
+ private String topLevelName(JCTree tree) {
+ while (tree instanceof JCFieldAccess) tree = ((JCFieldAccess) tree).selected;
+ return tree.toString();
+ }
+
+ @Override public String applyUnqualifiedNameToPackage(String unqualified) {
+ if (pkg == null) return unqualified;
+ return pkg.toString() + "." + unqualified;
+ }
+}
diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java
index e5d8ed38..82ce0cb8 100644
--- a/src/core/lombok/javac/JavacResolution.java
+++ b/src/core/lombok/javac/JavacResolution.java
@@ -433,8 +433,8 @@ public class JavacResolution {
if (symbol.name.length() == 0) {
// Anonymous inner class
if (type instanceof ClassType) {
- List<Type> ifaces = ((ClassType)type).interfaces_field;
- Type supertype = ((ClassType)type).supertype_field;
+ List<Type> ifaces = ((ClassType) type).interfaces_field;
+ Type supertype = ((ClassType) type).supertype_field;
if (ifaces != null && ifaces.length() == 1) {
return typeToJCTree(ifaces.get(0), ast, allowCompound, allowVoid);
}
diff --git a/src/core/lombok/javac/LombokOptions.java b/src/core/lombok/javac/LombokOptions.java
index 66e42fe7..f1567e9d 100644
--- a/src/core/lombok/javac/LombokOptions.java
+++ b/src/core/lombok/javac/LombokOptions.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Project Lombok Authors.
+ * Copyright (C) 2010-2013 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
@@ -49,6 +49,6 @@ public abstract class LombokOptions extends Options {
protected LombokOptions(Context context) {
super(context);
}
-
+
public abstract void putJavacOption(String optionName, String value);
}
diff --git a/src/core/lombok/javac/apt/Processor.java b/src/core/lombok/javac/apt/Processor.java
index 96150b06..110acaad 100644
--- a/src/core/lombok/javac/apt/Processor.java
+++ b/src/core/lombok/javac/apt/Processor.java
@@ -62,10 +62,6 @@ import com.sun.tools.javac.util.Context;
/**
* This Annotation Processor is the standard injection mechanism for lombok-enabling the javac compiler.
*
- * Due to lots of changes in the core javac code, as well as lombok's heavy usage of non-public API, this
- * code only works for the javac v1.6 compiler; it definitely won't work for javac v1.5, and it probably
- * won't work for javac v1.7 without modifications.
- *
* To actually enable lombok in a javac compilation run, this class should be in the classpath when
* running javac; that's the only requirement.
*/
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
new file mode 100644
index 00000000..e60819da
--- /dev/null
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2013 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.javac.handlers;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+import org.mangosdk.spi.ProviderFor;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeApply;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+import lombok.AccessLevel;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.core.HandlerPriority;
+import lombok.core.TransformationsUtil;
+import lombok.experimental.Builder;
+import lombok.experimental.NonFinal;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
+import static lombok.javac.Javac.*;
+import static lombok.core.handlers.HandlerUtil.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+@ProviderFor(JavacAnnotationHandler.class)
+@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
+public class HandleBuilder extends JavacAnnotationHandler<Builder> {
+ @Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) {
+ Builder builderInstance = annotation.getInstance();
+ String builderMethodName = builderInstance.builderMethodName();
+ String buildMethodName = builderInstance.buildMethodName();
+ String builderClassName = builderInstance.builderClassName();
+
+ if (builderMethodName == null) builderMethodName = "builder";
+ if (buildMethodName == null) buildMethodName = "build";
+ if (builderClassName == null) builderClassName = "";
+
+ if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
+ if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
+ if (!builderClassName.isEmpty()) {
+ if (!checkName("builderClassName", builderClassName, annotationNode)) return;
+ }
+
+ deleteAnnotationIfNeccessary(annotationNode, Builder.class);
+ deleteImportFromCompilationUnit(annotationNode, "lombok.experimental.Builder");
+
+ JavacNode parent = annotationNode.up();
+
+ java.util.List<JCExpression> typesOfParameters = new ArrayList<JCExpression>();
+ java.util.List<Name> namesOfParameters = new ArrayList<Name>();
+ JCExpression returnType;
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrownExceptions = List.nil();
+ Name nameOfStaticBuilderMethod;
+ JavacNode tdParent;
+
+ JCMethodDecl fillParametersFrom = parent.get() instanceof JCMethodDecl ? ((JCMethodDecl) parent.get()) : null;
+
+ if (parent.get() instanceof JCClassDecl) {
+ tdParent = parent;
+ JCClassDecl td = (JCClassDecl) tdParent.get();
+ ListBuffer<JavacNode> allFields = ListBuffer.lb();
+ @SuppressWarnings("deprecation")
+ boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
+ for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
+ JCVariableDecl fd = (JCVariableDecl) fieldNode.get();
+ // final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes
+ // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves.
+ // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that.
+ if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue;
+ namesOfParameters.add(fd.name);
+ typesOfParameters.add(fd.vartype);
+ allFields.append(fieldNode);
+ }
+
+ new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), null, SkipIfConstructorExists.I_AM_BUILDER, true, annotationNode);
+
+ returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
+ typeParams = td.typarams;
+ thrownExceptions = List.nil();
+ nameOfStaticBuilderMethod = null;
+ if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
+ } else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("<init>")) {
+ if (!fillParametersFrom.typarams.isEmpty()) {
+ annotationNode.addError("@Builder is not supported on constructors with constructor type parameters.");
+ return;
+ }
+ tdParent = parent.up();
+ JCClassDecl td = (JCClassDecl) tdParent.get();
+ returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
+ typeParams = td.typarams;
+ thrownExceptions = fillParametersFrom.thrown;
+ nameOfStaticBuilderMethod = null;
+ if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
+ } else if (fillParametersFrom != null) {
+ tdParent = parent.up();
+ JCClassDecl td = (JCClassDecl) tdParent.get();
+ if ((fillParametersFrom.mods.flags & Flags.STATIC) == 0) {
+ annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
+ return;
+ }
+ returnType = fillParametersFrom.restype;
+ typeParams = fillParametersFrom.typarams;
+ thrownExceptions = fillParametersFrom.thrown;
+ nameOfStaticBuilderMethod = fillParametersFrom.name;
+ if (builderClassName.isEmpty()) {
+ if (returnType instanceof JCTypeApply) {
+ returnType = ((JCTypeApply) returnType).clazz;
+ }
+ if (returnType instanceof JCFieldAccess) {
+ builderClassName = ((JCFieldAccess) returnType).name.toString() + "Builder";
+ } else if (returnType instanceof JCIdent) {
+ Name n = ((JCIdent) returnType).name;
+
+ for (JCTypeParameter tp : typeParams) {
+ if (tp.name.equals(n)) {
+ annotationNode.addError("@Builder requires specifying 'builderClassName' if used on methods with a type parameter as return type.");
+ return;
+ }
+ }
+ builderClassName = n.toString() + "Builder";
+ } else if (returnType instanceof JCPrimitiveTypeTree) {
+ builderClassName = returnType.toString() + "Builder";
+ if (Character.isLowerCase(builderClassName.charAt(0))) {
+ builderClassName = Character.toTitleCase(builderClassName.charAt(0)) + builderClassName.substring(1);
+ }
+
+ } else {
+ // This shouldn't happen.
+ System.err.println("Lombok bug ID#20140614-1651: javac HandleBuilder: return type to name conversion failed: " + returnType.getClass());
+ builderClassName = td.name.toString() + "Builder";
+ }
+ }
+ } else {
+ annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
+ return;
+ }
+
+ if (fillParametersFrom != null) {
+ for (JCVariableDecl param : fillParametersFrom.params) {
+ namesOfParameters.add(param.name);
+ typesOfParameters.add(param.vartype);
+ }
+ }
+
+ JavacNode builderType = findInnerClass(tdParent, builderClassName);
+ if (builderType == null) {
+ builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
+ } else {
+ sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode);
+ }
+ java.util.List<JavacNode> fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
+ java.util.List<JCMethodDecl> newMethods = new ArrayList<JCMethodDecl>();
+ for (JavacNode fieldNode : fieldNodes) {
+ JCMethodDecl newMethod = makeSetterMethodForBuilder(builderType, fieldNode, ast, builderInstance.fluent(), builderInstance.chain());
+ if (newMethod != null) newMethods.add(newMethod);
+ }
+
+ if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
+ JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), true, ast);
+ if (cd != null) injectMethod(builderType, cd);
+ }
+
+ for (JCMethodDecl newMethod : newMethods) injectMethod(builderType, newMethod);
+
+ if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
+ JCMethodDecl md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, namesOfParameters, builderType, thrownExceptions);
+ if (md != null) injectMethod(builderType, md);
+ }
+
+ if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
+ JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast);
+ if (md != null) injectMethod(builderType, md);
+ }
+
+ if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
+ JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams);
+ if (md != null) injectMethod(tdParent, md);
+ }
+ }
+
+ private JCMethodDecl generateBuildMethod(String name, Name staticName, JCExpression returnType, java.util.List<Name> fieldNames, JavacNode type, List<JCExpression> thrownExceptions) {
+ TreeMaker maker = type.getTreeMaker();
+
+ JCExpression call;
+ JCStatement statement;
+
+ ListBuffer<JCExpression> args = ListBuffer.lb();
+ for (Name n : fieldNames) {
+ args.append(maker.Ident(n));
+ }
+
+ if (staticName == null) {
+ call = maker.NewClass(null, List.<JCExpression>nil(), returnType, args.toList(), null);
+ statement = maker.Return(call);
+ } else {
+ ListBuffer<JCExpression> typeParams = ListBuffer.lb();
+ for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) {
+ typeParams.append(maker.Ident(tp.name));
+ }
+
+ JCExpression fn = maker.Select(maker.Ident(((JCClassDecl) type.up().get()).name), staticName);
+ call = maker.Apply(typeParams.toList(), fn, args.toList());
+ if (returnType instanceof JCPrimitiveTypeTree && compareCTC(getTypeTag((JCPrimitiveTypeTree) returnType), CTC_VOID)) {
+ statement = maker.Exec(call);
+ } else {
+ statement = maker.Return(call);
+ }
+ }
+
+ JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+
+ return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(name), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
+ }
+
+ private JCMethodDecl generateBuilderMethod(String builderMethodName, String builderClassName, JavacNode type, List<JCTypeParameter> typeParams) {
+ TreeMaker maker = type.getTreeMaker();
+
+ ListBuffer<JCExpression> typeArgs = ListBuffer.lb();
+ for (JCTypeParameter typeParam : typeParams) {
+ typeArgs.append(maker.Ident(typeParam.name));
+ }
+
+ JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.<JCExpression>nil(), null);
+ JCStatement statement = maker.Return(call);
+
+ JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+ return maker.MethodDef(maker.Modifiers(Flags.STATIC | Flags.PUBLIC), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(maker, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ }
+
+ private java.util.List<JavacNode> addFieldsToBuilder(JavacNode builderType, java.util.List<Name> namesOfParameters, java.util.List<JCExpression> typesOfParameters, JCTree source) {
+ int len = namesOfParameters.size();
+ java.util.List<JavacNode> existing = new ArrayList<JavacNode>();
+ for (JavacNode child : builderType.down()) {
+ if (child.getKind() == Kind.FIELD) existing.add(child);
+ }
+
+ java.util.List<JavacNode>out = new ArrayList<JavacNode>();
+
+ top:
+ for (int i = len - 1; i >= 0; i--) {
+ Name name = namesOfParameters.get(i);
+ for (JavacNode exists : existing) {
+ Name n = ((JCVariableDecl) exists.get()).name;
+ if (n.equals(name)) {
+ out.add(exists);
+ continue top;
+ }
+ }
+ TreeMaker maker = builderType.getTreeMaker();
+ JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
+ JCVariableDecl newField = maker.VarDef(mods, name, cloneType(maker, typesOfParameters.get(i), source), null);
+ out.add(injectField(builderType, newField));
+ }
+
+ Collections.reverse(out);
+ return out;
+ }
+
+
+ private JCMethodDecl makeSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JCTree source, boolean fluent, boolean chain) {
+ Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
+
+ for (JavacNode child : builderType.down()) {
+ if (child.getKind() != Kind.METHOD) continue;
+ Name existingName = ((JCMethodDecl) child.get()).name;
+ if (existingName.equals(fieldName)) return null;
+ }
+
+ boolean isBoolean = isBoolean(fieldNode);
+ String setterName = fluent ? fieldNode.getName() : TransformationsUtil.toSetterName(null, fieldNode.getName(), isBoolean);
+
+ TreeMaker maker = builderType.getTreeMaker();
+ return HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
+ }
+
+ private JavacNode findInnerClass(JavacNode parent, String name) {
+ for (JavacNode child : parent.down()) {
+ if (child.getKind() != Kind.TYPE) continue;
+ JCClassDecl td = (JCClassDecl) child.get();
+ if (td.name.contentEquals(name)) return child;
+ }
+ return null;
+ }
+
+ private JavacNode makeBuilderClass(JavacNode tdParent, String builderClassName, List<JCTypeParameter> typeParams, JCAnnotation ast) {
+ TreeMaker maker = tdParent.getTreeMaker();
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC | Flags.STATIC);
+ JCClassDecl builder = ClassDef(maker, mods, tdParent.toName(builderClassName), copyTypeParams(maker, typeParams), null, List.<JCExpression>nil(), List.<JCTree>nil());
+ return injectType(tdParent, builder);
+ }
+}
diff --git a/src/core/lombok/javac/handlers/HandleCleanup.java b/src/core/lombok/javac/handlers/HandleCleanup.java
index c75256a5..790d8964 100644
--- a/src/core/lombok/javac/handlers/HandleCleanup.java
+++ b/src/core/lombok/javac/handlers/HandleCleanup.java
@@ -32,7 +32,6 @@ import lombok.javac.JavacNode;
import org.mangosdk.spi.ProviderFor;
-import com.sun.tools.javac.code.TypeTags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCAssign;
diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java
index bb883ca4..ecd982e9 100644
--- a/src/core/lombok/javac/handlers/HandleConstructor.java
+++ b/src/core/lombok/javac/handlers/HandleConstructor.java
@@ -29,6 +29,7 @@ import lombok.RequiredArgsConstructor;
import lombok.core.AnnotationValues;
import lombok.core.TransformationsUtil;
import lombok.core.AST.Kind;
+import lombok.experimental.Builder;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
@@ -66,7 +67,7 @@ public class HandleConstructor {
String staticName = ann.staticName();
if (level == AccessLevel.NONE) return;
List<JavacNode> fields = List.nil();
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, staticName, false, false, annotationNode);
+ new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, staticName, SkipIfConstructorExists.NO, false, annotationNode);
}
}
@@ -84,7 +85,7 @@ public class HandleConstructor {
@SuppressWarnings("deprecation")
boolean suppressConstructorProperties = ann.suppressConstructorProperties();
if (level == AccessLevel.NONE) return;
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), staticName, false, suppressConstructorProperties, annotationNode);
+ new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
}
}
@@ -119,11 +120,11 @@ public class HandleConstructor {
@SuppressWarnings("deprecation")
boolean suppressConstructorProperties = ann.suppressConstructorProperties();
if (level == AccessLevel.NONE) return;
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), staticName, false, suppressConstructorProperties, annotationNode);
+ new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
}
}
- private static List<JavacNode> findAllFields(JavacNode typeNode) {
+ static List<JavacNode> findAllFields(JavacNode typeNode) {
ListBuffer<JavacNode> fields = ListBuffer.lb();
for (JavacNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD) continue;
@@ -154,25 +155,34 @@ public class HandleConstructor {
return true;
}
- public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists, JavacNode source) {
+ public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findRequiredFields(typeNode), staticName, skipIfConstructorExists, false, source);
}
- public void generateAllArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists, JavacNode source) {
+ public enum SkipIfConstructorExists {
+ YES, NO, I_AM_BUILDER;
+ }
+
+ public void generateAllArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findAllFields(typeNode), staticName, skipIfConstructorExists, false, source);
}
- public void generateConstructor(JavacNode typeNode, AccessLevel level, List<JCAnnotation> onConstructor, List<JavacNode> fields, String staticName, boolean skipIfConstructorExists, boolean suppressConstructorProperties, JavacNode source) {
+ public void generateConstructor(JavacNode typeNode, AccessLevel level, List<JCAnnotation> onConstructor, List<JavacNode> fields, String staticName, SkipIfConstructorExists skipIfConstructorExists, boolean suppressConstructorProperties, JavacNode source) {
boolean staticConstrRequired = staticName != null && !staticName.equals("");
- if (skipIfConstructorExists && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return;
- if (skipIfConstructorExists) {
+ if (skipIfConstructorExists != SkipIfConstructorExists.NO && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return;
+ if (skipIfConstructorExists != SkipIfConstructorExists.NO) {
for (JavacNode child : typeNode.down()) {
if (child.getKind() == Kind.ANNOTATION) {
- if (annotationTypeMatches(NoArgsConstructor.class, child) ||
+ boolean skipGeneration = annotationTypeMatches(NoArgsConstructor.class, child) ||
annotationTypeMatches(AllArgsConstructor.class, child) ||
- annotationTypeMatches(RequiredArgsConstructor.class, child)) {
-
+ annotationTypeMatches(RequiredArgsConstructor.class, child);
+
+ if (!skipGeneration && skipIfConstructorExists == SkipIfConstructorExists.YES) {
+ skipGeneration = annotationTypeMatches(Builder.class, child);
+ }
+
+ if (skipGeneration) {
if (staticConstrRequired) {
// @Data has asked us to generate a constructor, but we're going to skip this instruction, as an explicit 'make a constructor' annotation
// will take care of it. However, @Data also wants a specific static name; this will be ignored; the appropriate way to do this is to use
@@ -207,7 +217,7 @@ public class HandleConstructor {
mods.annotations = mods.annotations.append(annotation);
}
- private JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, boolean suppressConstructorProperties, JCTree source) {
+ static JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, boolean suppressConstructorProperties, JCTree source) {
TreeMaker maker = typeNode.getTreeMaker();
boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0;
@@ -243,7 +253,7 @@ public class HandleConstructor {
null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source);
}
- private boolean isLocalType(JavacNode type) {
+ private static boolean isLocalType(JavacNode type) {
Kind kind = type.up().getKind();
if (kind == Kind.COMPILATION_UNIT) return false;
if (kind == Kind.TYPE) return isLocalType(type.up());
diff --git a/src/core/lombok/javac/handlers/HandleData.java b/src/core/lombok/javac/handlers/HandleData.java
index 62183a15..858fb543 100644
--- a/src/core/lombok/javac/handlers/HandleData.java
+++ b/src/core/lombok/javac/handlers/HandleData.java
@@ -27,6 +27,7 @@ import lombok.Data;
import lombok.core.AnnotationValues;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
+import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
import org.mangosdk.spi.ProviderFor;
@@ -50,7 +51,7 @@ public class HandleData extends JavacAnnotationHandler<Data> {
String staticConstructorName = annotation.getInstance().staticConstructor();
// TODO move this to the end OR move it to the top in eclipse.
- new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, true, annotationNode);
+ new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode);
new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
index 39edb143..741e7e21 100644
--- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -25,6 +25,7 @@ import static lombok.javac.Javac.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import lombok.EqualsAndHashCode;
@@ -178,17 +179,26 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
boolean isFinal = (((JCClassDecl)typeNode.get()).mods.flags & Flags.FINAL) != 0;
boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject;
- java.util.List<MemberExistsResult> existsResults = new ArrayList<MemberExistsResult>();
- existsResults.add(methodExists("equals", typeNode, 1));
- existsResults.add(methodExists("hashCode", typeNode, 0));
- existsResults.add(methodExists("canEqual", typeNode, 1));
- switch (Collections.max(existsResults)) {
+ MemberExistsResult equalsExists = methodExists("equals", typeNode, 1);
+ MemberExistsResult hashCodeExists = methodExists("hashCode", typeNode, 0);
+ MemberExistsResult canEqualExists = methodExists("canEqual", typeNode, 1);
+ switch (Collections.max(Arrays.asList(equalsExists, hashCodeExists, canEqualExists))) {
case EXISTS_BY_LOMBOK:
return;
case EXISTS_BY_USER:
if (whineIfExists) {
String msg = String.format("Not generating equals%s: A method with one of those names already exists. (Either all or none of these methods will be generated).", needsCanEqual ? ", hashCode and canEquals" : " and hashCode");
source.addWarning(msg);
+ } else if (equalsExists == MemberExistsResult.NOT_EXISTS || hashCodeExists == MemberExistsResult.NOT_EXISTS) {
+ // This means equals OR hashCode exists and not both (or neither, but canEqual is there).
+ // Even though we should suppress the message about not generating these, this is such a weird and surprising situation we should ALWAYS generate a warning.
+ // The user code couldn't possibly (barring really weird subclassing shenanigans) be in a shippable state anyway; the implementations of these 3 methods are
+ // all inter-related and should be written by the same entity.
+ String msg = String.format("Not generating %s: One of equals, hashCode, and canEqual exists. " +
+ "You should either write all of these are none of these (in the latter case, lombok generates them).",
+ equalsExists == MemberExistsResult.NOT_EXISTS && hashCodeExists == MemberExistsResult.NOT_EXISTS ? "equals and hashCode" :
+ equalsExists == MemberExistsResult.NOT_EXISTS ? "equals" : "hashCode");
+ source.addWarning(msg);
}
return;
case NOT_EXISTS:
diff --git a/src/core/lombok/javac/handlers/HandleFieldDefaults.java b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
index d32446c3..038f3e3f 100644
--- a/src/core/lombok/javac/handlers/HandleFieldDefaults.java
+++ b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
@@ -44,7 +44,7 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
* Handles the {@code lombok.FieldDefaults} annotation for eclipse.
*/
@ProviderFor(JavacAnnotationHandler.class)
-@HandlerPriority(-512) //-2^9; to ensure @Setter and such pick up on messing with the fields' 'final' state, run earlier.
+@HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier.
public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
public boolean generateFieldDefaultsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
if (checkForTypeLevelFieldDefaults) {
@@ -108,6 +108,14 @@ public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
return;
}
+ if (level == AccessLevel.PACKAGE) {
+ annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field.");
+ }
+
+ if (!makeFinal && annotation.isExplicit("makeFinal")) {
+ annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field.");
+ }
+
if (node == null) return;
generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false);
diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java
index 2fa2a755..c5ec6f60 100644
--- a/src/core/lombok/javac/handlers/HandleGetter.java
+++ b/src/core/lombok/javac/handlers/HandleGetter.java
@@ -53,11 +53,9 @@ import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCIf;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
-import com.sun.tools.javac.tree.JCTree.JCNewClass;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCSynchronized;
-import com.sun.tools.javac.tree.JCTree.JCTypeApply;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeMaker;
@@ -256,6 +254,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
if (toClearOfMarkers != null) recursiveSetGeneratedBy(toClearOfMarkers, null);
decl.mods.annotations = decl.mods.annotations.appendList(delegates);
+ copyJavadoc(field, decl, CopyJavadoc.GETTER);
return decl;
}
@@ -287,6 +286,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
}
private static final String AR = "java.util.concurrent.atomic.AtomicReference";
+ private static final String JLO = "java.lang.Object";
private static final List<JCExpression> NIL_EXPRESSION = List.nil();
private static final java.util.Map<Object, String> TYPE_MAP;
@@ -305,37 +305,50 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
private List<JCStatement> createLazyGetterBody(TreeMaker maker, JavacNode fieldNode, JCTree source) {
/*
- java.util.concurrent.atomic.AtomicReference<ValueType> value = this.fieldName.get();
+ java.lang.Object value = this.fieldName.get();
if (value == null) {
synchronized (this.fieldName) {
value = this.fieldName.get();
- if (value == null) {
- final ValueType actualValue = new ValueType();
- value = new java.util.concurrent.atomic.AtomicReference<ValueType>(actualValue);
+ if (value == null) {
+ final RawValueType actualValue = INITIALIZER_EXPRESSION;
+ [IF PRIMITIVE]
+ value = actualValue;
+ [ELSE]
+ value = actualValue == null ? this.fieldName : actualValue;
+ [END IF]
this.fieldName.set(value);
}
}
}
- return value.get();
+ [IF PRIMITIVE]
+ return (BoxedValueType) value;
+ [ELSE]
+ return (BoxedValueType) (value == this.fieldName ? null : value);
+ [END IF]
*/
ListBuffer<JCStatement> statements = ListBuffer.lb();
JCVariableDecl field = (JCVariableDecl) fieldNode.get();
JCExpression copyOfRawFieldType = copyType(maker, field);
+ JCExpression copyOfBoxedFieldType = null;
field.type = null;
+ boolean isPrimitive = false;
if (field.vartype instanceof JCPrimitiveTypeTree) {
String boxed = TYPE_MAP.get(((JCPrimitiveTypeTree)field.vartype).typetag);
if (boxed != null) {
+ isPrimitive = true;
field.vartype = chainDotsString(fieldNode, boxed);
+ copyOfBoxedFieldType = chainDotsString(fieldNode, boxed);
}
}
+ if (copyOfBoxedFieldType == null) copyOfBoxedFieldType = copyType(maker, field);
Name valueName = fieldNode.toName("value");
Name actualValueName = fieldNode.toName("actualValue");
- /* java.util.concurrent.atomic.AtomicReference<ValueType> value = this.fieldName.get();*/ {
- JCTypeApply valueVarType = maker.TypeApply(chainDotsString(fieldNode, AR), List.of(copyType(maker, field)));
+ /* java.lang.Object value = this.fieldName.get();*/ {
+ JCExpression valueVarType = chainDotsString(fieldNode, JLO);
statements.append(maker.VarDef(maker.Modifiers(0), valueName, valueVarType, callGet(fieldNode, createFieldAccessor(maker, fieldNode, FieldAccess.ALWAYS_FIELD))));
}
@@ -350,15 +363,23 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
/* if (value == null) { */ {
ListBuffer<JCStatement> innerIfStatements = ListBuffer.lb();
- /* ValueType actualValue = new ValueType(); */ {
+ /* final RawValueType actualValue = INITIALIZER_EXPRESSION; */ {
innerIfStatements.append(maker.VarDef(maker.Modifiers(Flags.FINAL), actualValueName, copyOfRawFieldType, field.init));
}
- /* value = new java.util.concurrent.atomic.AtomicReference<ValueType>(actualValue);*/ {
- JCTypeApply valueVarType = maker.TypeApply(chainDotsString(fieldNode, AR), List.of(copyType(maker, field)));
- JCNewClass newInstance = maker.NewClass(null, NIL_EXPRESSION, valueVarType, List.<JCExpression>of(maker.Ident(actualValueName)), null);
-
- JCStatement statement = maker.Exec(maker.Assign(maker.Ident(valueName), newInstance));
- innerIfStatements.append(statement);
+ /* [IF primitive] value = actualValue; */ {
+ if (isPrimitive) {
+ JCStatement statement = maker.Exec(maker.Assign(maker.Ident(valueName), maker.Ident(actualValueName)));
+ innerIfStatements.append(statement);
+ }
+ }
+ /* [ELSE] value = actualValue == null ? this.fieldName : actualValue; */ {
+ if (!isPrimitive) {
+ JCExpression actualValueIsNull = Javac.makeBinary(maker, CTC_EQUAL, maker.Ident(actualValueName), Javac.makeLiteral(maker, CTC_BOT, null));
+ JCExpression thisDotFieldName = createFieldAccessor(maker, fieldNode, FieldAccess.ALWAYS_FIELD);
+ JCExpression ternary = maker.Conditional(actualValueIsNull, thisDotFieldName, maker.Ident(actualValueName));
+ JCStatement statement = maker.Exec(maker.Assign(maker.Ident(valueName), ternary));
+ innerIfStatements.append(statement);
+ }
}
/* this.fieldName.set(value); */ {
JCStatement statement = callSet(fieldNode, createFieldAccessor(maker, fieldNode, FieldAccess.ALWAYS_FIELD), maker.Ident(valueName));
@@ -377,15 +398,25 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
JCIf ifStatement = maker.If(isNull, maker.Block(0, List.<JCStatement>of(synchronizedStatement)), null);
statements.append(ifStatement);
}
- /* return value.get(); */
- statements.append(maker.Return(callGet(fieldNode, maker.Ident(valueName))));
+ /* [IF PRIMITIVE] return (BoxedValueType) value; */ {
+ if (isPrimitive) {
+ statements.append(maker.Return(maker.TypeCast(copyOfBoxedFieldType, maker.Ident(valueName))));
+ }
+ }
+ /* [ELSE] return (BoxedValueType) (value == this.fieldName ? null : value); */ {
+ if (!isPrimitive) {
+ JCExpression valueEqualsSelf = Javac.makeBinary(maker, CTC_EQUAL, maker.Ident(valueName), createFieldAccessor(maker, fieldNode, FieldAccess.ALWAYS_FIELD));
+ JCExpression ternary = maker.Conditional(valueEqualsSelf, Javac.makeLiteral(maker, CTC_BOT, null), maker.Ident(valueName));
+ JCExpression typeCast = maker.TypeCast(copyOfBoxedFieldType, maker.Parens(ternary));
+ statements.append(maker.Return(typeCast));
+ }
+ }
// update the field type and init last
- /* private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType> fieldName = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType>>(); */ {
+ /* private final java.util.concurrent.atomic.AtomicReference<Object> fieldName = new java.util.concurrent.atomic.AtomicReference<Object>(); */ {
field.vartype = recursiveSetGeneratedBy(
- maker.TypeApply(chainDotsString(fieldNode, AR), List.<JCExpression>of(maker.TypeApply(chainDotsString(fieldNode, AR), List.of(copyType(maker, field))))),
- source);
+ maker.TypeApply(chainDotsString(fieldNode, AR), List.<JCExpression>of(chainDotsString(fieldNode, JLO))), source);
field.init = recursiveSetGeneratedBy(maker.NewClass(null, NIL_EXPRESSION, copyType(maker, field), NIL_EXPRESSION, null), source);
}
diff --git a/src/core/lombok/javac/handlers/HandleLog.java b/src/core/lombok/javac/handlers/HandleLog.java
index 62a55c44..35a32be5 100644
--- a/src/core/lombok/javac/handlers/HandleLog.java
+++ b/src/core/lombok/javac/handlers/HandleLog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2012 The Project Lombok Authors.
+ * Copyright (C) 2010-2013 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
@@ -82,7 +82,7 @@ public class HandleLog {
private static boolean createField(LoggingFramework framework, JavacNode typeNode, JCFieldAccess loggingType, JCTree source) {
TreeMaker maker = typeNode.getTreeMaker();
- // private static final <loggerType> log = <factoryMethod>(<parameter>);
+ // private static final <loggerType> log = <factoryMethod>(<parameter>);
JCExpression loggerType = chainDotsString(typeNode, framework.getLoggerTypeName());
JCExpression factoryMethod = chainDotsString(typeNode, framework.getLoggerFactoryMethodName());
@@ -128,6 +128,16 @@ public class HandleLog {
}
/**
+ * Handles the {@link lombok.extern.log4j.Log4j2} annotation for javac.
+ */
+ @ProviderFor(JavacAnnotationHandler.class)
+ public static class HandleLog4j2Log extends JavacAnnotationHandler<lombok.extern.log4j.Log4j2> {
+ @Override public void handle(AnnotationValues<lombok.extern.log4j.Log4j2> annotation, JCAnnotation ast, JavacNode annotationNode) {
+ processAnnotation(LoggingFramework.LOG4J2, annotation, annotationNode);
+ }
+ }
+
+ /**
* Handles the {@link lombok.extern.slf4j.Slf4j} annotation for javac.
*/
@ProviderFor(JavacAnnotationHandler.class)
@@ -163,6 +173,9 @@ public class HandleLog {
// private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(TargetType.class);
LOG4J(lombok.extern.log4j.Log4j.class, "org.apache.log4j.Logger", "org.apache.log4j.Logger.getLogger"),
+ // private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(TargetType.class);
+ LOG4J2(lombok.extern.log4j.Log4j2.class, "org.apache.logging.log4j.Logger", "org.apache.logging.log4j.LogManager.getLogger"),
+
// private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TargetType.class);
SLF4J(lombok.extern.slf4j.Slf4j.class, "org.slf4j.Logger", "org.slf4j.LoggerFactory.getLogger"),
diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java
index 2136024e..c3ee10a3 100644
--- a/src/core/lombok/javac/handlers/HandleSetter.java
+++ b/src/core/lombok/javac/handlers/HandleSetter.java
@@ -26,10 +26,6 @@ import static lombok.javac.handlers.JavacHandlerUtil.*;
import java.util.Collection;
-import javax.lang.model.type.NoType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeVisitor;
-
import lombok.AccessLevel;
import lombok.Setter;
import lombok.core.AST.Kind;
@@ -43,7 +39,6 @@ import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
-import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCAssign;
@@ -195,9 +190,13 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
injectMethod(fieldNode.up(), createdSetter);
}
- private JCMethodDecl createSetter(long access, JavacNode field, TreeMaker treeMaker, JCTree source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
+ static JCMethodDecl createSetter(long access, JavacNode field, TreeMaker treeMaker, JCTree source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
String setterName = toSetterName(field);
boolean returnThis = shouldReturnThis(field);
+ return createSetter(access, field, treeMaker, setterName, returnThis, source, onMethod, onParam);
+ }
+
+ static JCMethodDecl createSetter(long access, JavacNode field, TreeMaker treeMaker, String setterName, boolean shouldReturnThis, JCTree source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
if (setterName == null) return null;
JCVariableDecl fieldDecl = (JCVariableDecl) field.get();
@@ -223,17 +222,17 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
}
JCExpression methodType = null;
- if (returnThis) {
+ if (shouldReturnThis) {
methodType = cloneSelfType(field);
}
if (methodType == null) {
//WARNING: Do not use field.getSymbolTable().voidType - that field has gone through non-backwards compatible API changes within javac1.6.
- methodType = treeMaker.Type(new JCNoType(CTC_VOID));
- returnThis = false;
+ methodType = treeMaker.Type(Javac.createVoidType(treeMaker, CTC_VOID));
+ shouldReturnThis = false;
}
- if (returnThis) {
+ if (shouldReturnThis) {
JCReturn returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this")));
statements.append(returnStatement);
}
@@ -249,26 +248,9 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(chainDots(field, "java", "lang", "Deprecated"), List.<JCExpression>nil()));
}
- return recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType,
+ JCMethodDecl decl = recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType,
methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source);
- }
-
- private static class JCNoType extends Type implements NoType {
- public JCNoType(Object tag) {
- //FIX
- super(1, null);
- }
-
- @Override
- public TypeKind getKind() {
- if (Javac.compareCTC(tag, CTC_VOID)) return TypeKind.VOID;
- if (Javac.compareCTC(tag, CTC_NONE)) return TypeKind.NONE;
- throw new AssertionError("Unexpected tag: " + tag);
- }
-
- @Override
- public <R, P> R accept(TypeVisitor<R, P> v, P p) {
- return v.visitNoType(this, p);
- }
+ copyJavadoc(field, decl, CopyJavadoc.SETTER);
+ return decl;
}
}
diff --git a/src/core/lombok/javac/handlers/HandleSneakyThrows.java b/src/core/lombok/javac/handlers/HandleSneakyThrows.java
index a5bd74e7..c818f630 100644
--- a/src/core/lombok/javac/handlers/HandleSneakyThrows.java
+++ b/src/core/lombok/javac/handlers/HandleSneakyThrows.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -36,8 +36,6 @@ import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
-import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
-import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
@@ -84,13 +82,20 @@ public class HandleSneakyThrows extends JavacAnnotationHandler<SneakyThrows> {
return;
}
- if (method.body == null) return;
- if (method.body.stats.isEmpty()) return;
+ if (method.body == null || method.body.stats.isEmpty()) {
+ generateEmptyBlockWarning(methodNode, annotation, false);
+ return;
+ }
final JCStatement constructorCall = method.body.stats.get(0);
final boolean isConstructorCall = isConstructorCall(constructorCall);
List<JCStatement> contents = isConstructorCall ? method.body.stats.tail : method.body.stats;
+ if (contents == null || contents.isEmpty()) {
+ generateEmptyBlockWarning(methodNode, annotation, true);
+ return;
+ }
+
for (String exception : exceptions) {
contents = List.of(buildTryCatchBlock(methodNode, contents, exception, annotation.get()));
}
@@ -99,14 +104,14 @@ public class HandleSneakyThrows extends JavacAnnotationHandler<SneakyThrows> {
methodNode.rebuild();
}
- private boolean isConstructorCall(final JCStatement supect) {
- if (!(supect instanceof JCExpressionStatement)) return false;
- final JCExpression supectExpression = ((JCExpressionStatement) supect).expr;
- if (!(supectExpression instanceof JCMethodInvocation)) return false;
- final String methodName = ((JCMethodInvocation) supectExpression).meth.toString();
- return "super".equals(methodName) || "this".equals(methodName);
+ private void generateEmptyBlockWarning(JavacNode methodNode, JavacNode annotation, boolean hasConstructorCall) {
+ if (hasConstructorCall) {
+ annotation.addWarning("Calls to sibling / super constructors are always excluded from @SneakyThrows; @SneakyThrows has been ignored because there is no other code in this constructor.");
+ } else {
+ annotation.addWarning("This method or constructor is empty; @SneakyThrows has been ignored.");
+ }
}
-
+
private JCStatement buildTryCatchBlock(JavacNode node, List<JCStatement> contents, String exception, JCTree source) {
TreeMaker maker = node.getTreeMaker();
diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java
index c37fe149..8297a4db 100644
--- a/src/core/lombok/javac/handlers/HandleToString.java
+++ b/src/core/lombok/javac/handlers/HandleToString.java
@@ -24,6 +24,8 @@ package lombok.javac.handlers;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import static lombok.javac.Javac.*;
+import java.util.Collection;
+
import lombok.ToString;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
@@ -165,7 +167,7 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
}
}
- private JCMethodDecl createToString(JavacNode typeNode, List<JavacNode> fields, boolean includeFieldNames, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
+ static JCMethodDecl createToString(JavacNode typeNode, Collection<JavacNode> fields, boolean includeFieldNames, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
TreeMaker maker = typeNode.getTreeMaker();
JCAnnotation overrideAnnotation = maker.Annotation(chainDots(typeNode, "java", "lang", "Override"), List.<JCExpression>nil());
@@ -199,18 +201,22 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
}
for (JavacNode fieldNode : fields) {
- JCVariableDecl field = (JCVariableDecl) fieldNode.get();
JCExpression expr;
JCExpression fieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess);
- if (getFieldType(fieldNode, fieldAccess) instanceof JCArrayTypeTree) {
- boolean multiDim = ((JCArrayTypeTree)field.vartype).elemtype instanceof JCArrayTypeTree;
- boolean primitiveArray = ((JCArrayTypeTree)field.vartype).elemtype instanceof JCPrimitiveTypeTree;
- boolean useDeepTS = multiDim || !primitiveArray;
-
- JCExpression hcMethod = chainDots(typeNode, "java", "util", "Arrays", useDeepTS ? "deepToString" : "toString");
- expr = maker.Apply(List.<JCExpression>nil(), hcMethod, List.<JCExpression>of(fieldAccessor));
+ JCExpression fieldType = getFieldType(fieldNode, fieldAccess);
+
+ // The distinction between primitive and object will be useful if we ever add a 'hideNulls' option.
+ boolean fieldIsPrimitive = fieldType instanceof JCPrimitiveTypeTree;
+ boolean fieldIsPrimitiveArray = fieldType instanceof JCArrayTypeTree && ((JCArrayTypeTree) fieldType).elemtype instanceof JCPrimitiveTypeTree;
+ boolean fieldIsObjectArray = !fieldIsPrimitiveArray && fieldType instanceof JCArrayTypeTree;
+ @SuppressWarnings("unused")
+ boolean fieldIsObject = !fieldIsPrimitive && !fieldIsPrimitiveArray && !fieldIsObjectArray;
+
+ if (fieldIsPrimitiveArray || fieldIsObjectArray) {
+ JCExpression tsMethod = chainDots(typeNode, "java", "util", "Arrays", fieldIsObjectArray ? "deepToString" : "toString");
+ expr = maker.Apply(List.<JCExpression>nil(), tsMethod, List.<JCExpression>of(fieldAccessor));
} else expr = fieldAccessor;
if (first) {
@@ -238,7 +244,7 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source);
}
- private String getTypeName(JavacNode typeNode) {
+ private static String getTypeName(JavacNode typeNode) {
String typeName = ((JCClassDecl) typeNode.get()).name.toString();
JavacNode upType = typeNode.up();
while (upType.getKind() == Kind.TYPE) {
diff --git a/src/core/lombok/javac/handlers/HandleValue.java b/src/core/lombok/javac/handlers/HandleValue.java
index f5b10bc1..15fb4781 100644
--- a/src/core/lombok/javac/handlers/HandleValue.java
+++ b/src/core/lombok/javac/handlers/HandleValue.java
@@ -22,13 +22,17 @@
package lombok.javac.handlers;
import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.lang.annotation.Annotation;
+
import lombok.AccessLevel;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.experimental.NonFinal;
-import lombok.experimental.Value;
+import lombok.Value;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
+import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
import org.mangosdk.spi.ProviderFor;
@@ -44,7 +48,9 @@ import com.sun.tools.javac.tree.JCTree.JCModifiers;
@HandlerPriority(-512) //-2^9; to ensure @EqualsAndHashCode and such pick up on this handler making the class final and messing with the fields' access levels, run earlier.
public class HandleValue extends JavacAnnotationHandler<Value> {
@Override public void handle(AnnotationValues<Value> annotation, JCAnnotation ast, JavacNode annotationNode) {
- deleteAnnotationIfNeccessary(annotationNode, Value.class);
+ @SuppressWarnings("deprecation")
+ Class<? extends Annotation> oldExperimentalValue = lombok.experimental.Value.class;
+ deleteAnnotationIfNeccessary(annotationNode, Value.class, oldExperimentalValue);
JavacNode typeNode = annotationNode.up();
boolean notAClass = !isClass(typeNode);
@@ -65,7 +71,7 @@ public class HandleValue extends JavacAnnotationHandler<Value> {
new HandleFieldDefaults().generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true);
// TODO move this to the end OR move it to the top in eclipse.
- new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, true, annotationNode);
+ new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode);
new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
new HandleToString().generateToStringForType(typeNode, annotationNode);
diff --git a/src/core/lombok/javac/handlers/HandleWither.java b/src/core/lombok/javac/handlers/HandleWither.java
index 62ed63f1..514e27dd 100644
--- a/src/core/lombok/javac/handlers/HandleWither.java
+++ b/src/core/lombok/javac/handlers/HandleWither.java
@@ -34,6 +34,7 @@ import lombok.experimental.Wither;
import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
+import lombok.javac.handlers.JavacHandlerUtil.CopyJavadoc;
import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import org.mangosdk.spi.ProviderFor;
@@ -265,7 +266,9 @@ public class HandleWither extends JavacAnnotationHandler<Wither> {
if (isFieldDeprecated(field)) {
annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(chainDots(field, "java", "lang", "Deprecated"), List.<JCExpression>nil()));
}
- return recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, returnType,
+ JCMethodDecl decl = recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, returnType,
methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source);
+ copyJavadoc(field, decl, CopyJavadoc.WITHER);
+ return decl;
}
}
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index 178b82a2..211f5d36 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -21,6 +21,7 @@
*/
package lombok.javac.handlers;
+import static lombok.core.TransformationsUtil.INVALID_ON_BUILDERS;
import static lombok.javac.Javac.*;
import java.lang.annotation.Annotation;
@@ -31,6 +32,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.AccessLevel;
@@ -51,9 +53,11 @@ import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import com.sun.tools.javac.tree.JCTree.JCAssign;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCImport;
@@ -122,6 +126,7 @@ public class JavacHandlerUtil {
}
public static <T extends JCTree> T recursiveSetGeneratedBy(T node, JCTree source) {
+ if (node == null) return null;
setGeneratedBy(node, source);
node.accept(new MarkingScanner(source));
@@ -186,7 +191,7 @@ public class JavacHandlerUtil {
public static boolean typeMatches(Class<?> type, JavacNode node, JCTree typeNode) {
String typeName = typeNode.toString();
- TypeResolver resolver = new TypeResolver(node.getPackageDeclaration(), node.getImportStatements());
+ TypeResolver resolver = new TypeResolver(node.getImportList());
return resolver.typeMatches(node, type.getName(), typeName);
}
@@ -278,7 +283,22 @@ public class JavacHandlerUtil {
* then removes any import statement that imports this exact annotation (not star imports).
* Only does this if the DeleteLombokAnnotations class is in the context.
*/
+ @SuppressWarnings("unchecked")
public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType) {
+ deleteAnnotationIfNeccessary0(annotation, annotationType);
+ }
+
+ /**
+ * Removes the annotation from javac's AST (it remains in lombok's AST),
+ * then removes any import statement that imports this exact annotation (not star imports).
+ * Only does this if the DeleteLombokAnnotations class is in the context.
+ */
+ @SuppressWarnings("unchecked")
+ public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType1, Class<? extends Annotation> annotationType2) {
+ deleteAnnotationIfNeccessary0(annotation, annotationType1, annotationType2);
+ }
+
+ private static void deleteAnnotationIfNeccessary0(JavacNode annotation, Class<? extends Annotation>... annotationTypes) {
if (inNetbeansEditor(annotation)) return;
if (!annotation.shouldDeleteLombokAnnotations()) return;
JavacNode parentNode = annotation.directUp();
@@ -306,7 +326,10 @@ public class JavacHandlerUtil {
return;
}
- deleteImportFromCompilationUnit(annotation, annotationType.getName());
+ parentNode.getAst().setChanged();
+ for (Class<?> annotationType : annotationTypes) {
+ deleteImportFromCompilationUnit(annotation, annotationType.getName());
+ }
}
public static void deleteImportFromCompilationUnit(JavacNode node, String name) {
@@ -425,8 +448,12 @@ public class JavacHandlerUtil {
}
}
- private static boolean isBoolean(JavacNode field) {
+ public static boolean isBoolean(JavacNode field) {
JCExpression varType = ((JCVariableDecl) field.get()).vartype;
+ return isBoolean(varType);
+ }
+
+ public static boolean isBoolean(JCExpression varType) {
return varType != null && varType.toString().equals("boolean");
}
@@ -543,6 +570,23 @@ public class JavacHandlerUtil {
return MemberExistsResult.NOT_EXISTS;
}
+ public static boolean isConstructorCall(final JCStatement statement) {
+ if (!(statement instanceof JCExpressionStatement)) return false;
+ JCExpression expr = ((JCExpressionStatement) statement).expr;
+ if (!(expr instanceof JCMethodInvocation)) return false;
+ JCExpression invocation = ((JCMethodInvocation) expr).meth;
+ String name;
+ if (invocation instanceof JCFieldAccess) {
+ name = ((JCFieldAccess) invocation).name.toString();
+ } else if (invocation instanceof JCIdent) {
+ name = ((JCIdent) invocation).name.toString();
+ } else {
+ name = "";
+ }
+
+ return "super".equals(name) || "this".equals(name);
+ }
+
/**
* Turns an {@code AccessLevel} instance into the flag bit used by javac.
*/
@@ -710,11 +754,11 @@ public class JavacHandlerUtil {
*
* Also takes care of updating the JavacAST.
*/
- public static void injectField(JavacNode typeNode, JCVariableDecl field) {
- injectField(typeNode, field, false);
+ public static JavacNode injectField(JavacNode typeNode, JCVariableDecl field) {
+ return injectField(typeNode, field, false);
}
- private static void injectField(JavacNode typeNode, JCVariableDecl field, boolean addSuppressWarnings) {
+ private static JavacNode injectField(JavacNode typeNode, JCVariableDecl field, boolean addSuppressWarnings) {
JCClassDecl type = (JCClassDecl) typeNode.get();
if (addSuppressWarnings) addSuppressWarningsAll(field.mods, typeNode, field.pos, getGeneratedBy(field));
@@ -740,7 +784,7 @@ public class JavacHandlerUtil {
insertAfter.tail = fieldEntry;
}
- typeNode.add(field, Kind.FIELD);
+ return typeNode.add(field, Kind.FIELD);
}
private static boolean isEnumConstant(final JCVariableDecl field) {
@@ -781,6 +825,20 @@ public class JavacHandlerUtil {
typeNode.add(method, Kind.METHOD);
}
+ /**
+ * Adds an inner type (class, interface, enum) to the given type. Cannot inject top-level types.
+ *
+ * @param typeNode parent type to inject new type into
+ * @param type New type (class, interface, etc) to inject.
+ * @return
+ */
+ public static JavacNode injectType(final JavacNode typeNode, final JCClassDecl type) {
+ JCClassDecl typeDecl = (JCClassDecl) typeNode.get();
+ addSuppressWarningsAll(type.mods, typeNode, type.pos, getGeneratedBy(type));
+ typeDecl.defs = typeDecl.defs.append(type);
+ return typeNode.add(type, Kind.TYPE);
+ }
+
private static void addSuppressWarningsAll(JCModifiers mods, JavacNode node, int pos, JCTree source) {
TreeMaker maker = node.getTreeMaker();
JCExpression suppressWarningsType = chainDots(node, "java", "lang", "SuppressWarnings");
@@ -890,7 +948,8 @@ public class JavacHandlerUtil {
JCExpression npe = chainDots(variable, "java", "lang", "NullPointerException");
JCTree exception = maker.NewClass(null, List.<JCExpression>nil(), npe, List.<JCExpression>of(maker.Literal(fieldName.toString())), null);
JCStatement throwStatement = maker.Throw(exception);
- return maker.If(Javac.makeBinary(maker, CTC_EQUAL, maker.Ident(fieldName), Javac.makeLiteral(maker, CTC_BOT, null)), throwStatement, null);
+ JCBlock throwBlock = maker.Block(0, List.of(throwStatement));
+ return maker.If(Javac.makeBinary(maker, CTC_EQUAL, maker.Ident(fieldName), Javac.makeLiteral(maker, CTC_BOT, null)), throwBlock, null);
}
/**
@@ -991,6 +1050,49 @@ public class JavacHandlerUtil {
return result.toList();
}
+ public static List<JCTypeParameter> copyTypeParams(TreeMaker maker, List<JCTypeParameter> params) {
+ if (params == null || params.isEmpty()) return params;
+ ListBuffer<JCTypeParameter> out = ListBuffer.lb();
+ for (JCTypeParameter tp : params) out.append(maker.TypeParameter(tp.name, tp.bounds));
+ return out.toList();
+ }
+
+ public static JCExpression namePlusTypeParamsToTypeReference(TreeMaker maker, Name typeName, List<JCTypeParameter> params) {
+ ListBuffer<JCExpression> typeArgs = ListBuffer.lb();
+
+ if (!params.isEmpty()) {
+ for (JCTypeParameter param : params) {
+ typeArgs.append(maker.Ident(param.name));
+ }
+
+ return maker.TypeApply(maker.Ident(typeName), typeArgs.toList());
+ }
+
+ return maker.Ident(typeName);
+ }
+
+ public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(JavacNode typeNode, JavacNode errorNode) {
+ List<String> disallowed = List.nil();
+ for (JavacNode child : typeNode.down()) {
+ for (Class<? extends java.lang.annotation.Annotation> annType : INVALID_ON_BUILDERS) {
+ if (annotationTypeMatches(annType, child)) {
+ disallowed = disallowed.append(annType.getSimpleName());
+ }
+ }
+ }
+
+ int size = disallowed.size();
+ if (size == 0) return;
+ if (size == 1) {
+ errorNode.addError("@" + disallowed.head + " is not allowed on builder classes.");
+ return;
+ }
+ StringBuilder out = new StringBuilder();
+ for (String a : disallowed) out.append("@").append(a).append(", ");
+ out.setLength(out.length() - 2);
+ errorNode.addError(out.append(" are not allowed on builder classes.").toString());
+ }
+
static List<JCAnnotation> copyAnnotations(List<? extends JCExpression> in) {
ListBuffer<JCAnnotation> out = ListBuffer.lb();
for (JCExpression expr : in) {
@@ -1090,4 +1192,103 @@ public class JavacHandlerUtil {
// This is somewhat unsafe, but it's better than outright throwing an exception here. Returning null will just cause an exception down the pipeline.
return (JCExpression) in;
}
+
+ private static final Pattern SECTION_FINDER = Pattern.compile("^\\s*\\**\\s*[-*][-*]+\\s*([GS]ETTER|WITHER)\\s*[-*][-*]+\\s*\\**\\s*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
+
+ private static String stripLinesWithTagFromJavadoc(String javadoc, String regexpFragment) {
+ Pattern p = Pattern.compile("^\\s*\\**\\s*" + regexpFragment + "\\s*\\**\\s*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
+ Matcher m = p.matcher(javadoc);
+ return m.replaceAll("");
+ }
+
+ private static String[] splitJavadocOnSectionIfPresent(String javadoc, String sectionName) {
+ Matcher m = SECTION_FINDER.matcher(javadoc);
+ int getterSectionHeaderStart = -1;
+ int getterSectionStart = -1;
+ int getterSectionEnd = -1;
+ while (m.find()) {
+ if (m.group(1).equalsIgnoreCase(sectionName)) {
+ getterSectionStart = m.end() + 1;
+ getterSectionHeaderStart = m.start();
+ } else if (getterSectionStart != -1) {
+ getterSectionEnd = m.start();
+ }
+ }
+
+ if (getterSectionStart != -1) {
+ if (getterSectionEnd != -1) {
+ return new String[] {javadoc.substring(getterSectionStart, getterSectionEnd), javadoc.substring(0, getterSectionHeaderStart) + javadoc.substring(getterSectionEnd)};
+ } else {
+ return new String[] {javadoc.substring(getterSectionStart), javadoc.substring(0, getterSectionHeaderStart)};
+ }
+ }
+
+ return null;
+ }
+
+ public static enum CopyJavadoc {
+ VERBATIM, GETTER {
+ @Override public String[] split(String javadoc) {
+ // step 1: Check if there is a 'GETTER' section. If yes, that becomes the new method's javadoc and we strip that from the original.
+ String[] out = splitJavadocOnSectionIfPresent(javadoc, "GETTER");
+ if (out != null) return out;
+ // failing that, create a copy, but strip @return from the original and @param from the copy.
+ String copy = javadoc;
+ javadoc = stripLinesWithTagFromJavadoc(javadoc, "@returns?\\s+.*");
+ copy = stripLinesWithTagFromJavadoc(copy, "@param(?:eter)?\\s+.*");
+ return new String[] {copy, javadoc};
+ }
+ },
+ SETTER {
+ @Override public String[] split(String javadoc) {
+ return splitForSetters(javadoc, "SETTER");
+ }
+ },
+ WITHER {
+ @Override public String[] split(String javadoc) {
+ return splitForSetters(javadoc, "WITHER");
+ }
+ };
+
+ private static String[] splitForSetters(String javadoc, String sectionName) {
+ // step 1: Check if there is a 'SETTER' section. If yes, that becomes the new one and we strip that from the original.
+ String[] out = splitJavadocOnSectionIfPresent(javadoc, sectionName);
+ if (out != null) return out;
+ // failing that, create a copy, but strip @param from the original and @return from the copy.
+ String copy = javadoc;
+ javadoc = stripLinesWithTagFromJavadoc(javadoc, "@param(?:eter)?\\s+.*");
+ copy = stripLinesWithTagFromJavadoc(copy, "@returns?\\s+.*");
+ return new String[] {copy, javadoc};
+ }
+
+ /** Splits the javadoc into the section to be copied (ret[0]) and the section to replace the original with (ret[1]) */
+ public String[] split(String javadoc) {
+ return new String[] {javadoc, javadoc};
+ }
+ }
+
+ /**
+ * Copies javadoc on one node to the other.
+ *
+ * in 'GETTER' copyMode, first a 'GETTER' segment is searched for. If it exists, that will become the javadoc for the 'to' node, and this section is
+ * stripped out of the 'from' node. If no 'GETTER' segment is found, then the entire javadoc is taken minus any {@code @param} lines. any {@code @return} lines
+ * are stripped from 'from'.
+ *
+ * in 'SETTER' mode, stripping works similarly to 'GETTER' mode, except {@code param} are copied and stripped from the original and {@code @return} are skipped.
+ */
+ public static void copyJavadoc(JavacNode from, JCTree to, CopyJavadoc copyMode) {
+ if (copyMode == null) copyMode = CopyJavadoc.VERBATIM;
+ try {
+ JCCompilationUnit cu = ((JCCompilationUnit) from.top().get());
+ if (cu.docComments != null) {
+ String javadoc = cu.docComments.get(from.get());
+
+ if (javadoc != null) {
+ String[] filtered = copyMode.split(javadoc);
+ cu.docComments.put(to, filtered[0]);
+ cu.docComments.put(from.get(), filtered[1]);
+ }
+ }
+ } catch (Exception ignore) {}
+ }
}
diff --git a/src/core/lombok/javac/handlers/NonNullHandler.java b/src/core/lombok/javac/handlers/NonNullHandler.java
new file mode 100644
index 00000000..d74fb55d
--- /dev/null
+++ b/src/core/lombok/javac/handlers/NonNullHandler.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2013 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.javac.handlers;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import org.mangosdk.spi.ProviderFor;
+
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCBinary;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCIf;
+import com.sun.tools.javac.tree.JCTree.JCLiteral;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCParens;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCThrow;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+
+import lombok.NonNull;
+import lombok.core.AnnotationValues;
+import lombok.core.AST.Kind;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+
+@ProviderFor(JavacAnnotationHandler.class)
+public class NonNullHandler extends JavacAnnotationHandler<NonNull> {
+ @Override public void handle(AnnotationValues<NonNull> annotation, JCAnnotation ast, JavacNode annotationNode) {
+ if (annotationNode.up().getKind() == Kind.FIELD) {
+ // This is meaningless unless the field is used to generate a method (@Setter, @RequiredArgsConstructor, etc),
+ // but in that case those handlers will take care of it. However, we DO check if the annotation is applied to
+ // a primitive, because those handlers trigger on any annotation named @NonNull and we only want the warning
+ // behaviour on _OUR_ 'lombok.NonNull'.
+
+ try {
+ if (isPrimitive(((JCVariableDecl) annotationNode.up().get()).vartype)) {
+ annotationNode.addWarning("@NonNull is meaningless on a primitive.");
+ }
+ } catch (Exception ignore) {}
+
+ return;
+ }
+
+ if (annotationNode.up().getKind() != Kind.ARGUMENT) return;
+
+ JCMethodDecl declaration;
+
+ try {
+ declaration = (JCMethodDecl) annotationNode.up().up().get();
+ } catch (Exception e) {
+ return;
+ }
+
+ if (JavacHandlerUtil.isGenerated(declaration)) return;
+
+ // Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter,
+ // and if they exist, create a new method in the class: 'private static <T> T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and
+ // wrap all references to it in the super/this to a call to this method.
+
+ JCStatement nullCheck = recursiveSetGeneratedBy(generateNullCheck(annotationNode.getTreeMaker(), annotationNode.up()), ast);
+
+ if (nullCheck == null) {
+ // @NonNull applied to a primitive. Kinda pointless. Let's generate a warning.
+ annotationNode.addWarning("@NonNull is meaningless on a primitive.");
+ return;
+ }
+
+ List<JCStatement> statements = declaration.body.stats;
+
+ String expectedName = annotationNode.up().getName();
+ for (JCStatement stat : statements) {
+ if (JavacHandlerUtil.isConstructorCall(stat)) continue;
+ String varNameOfNullCheck = returnVarNameIfNullCheck(stat);
+ if (varNameOfNullCheck == null) break;
+ if (varNameOfNullCheck.equals(expectedName)) return;
+ }
+
+ List<JCStatement> tail = statements;
+ List<JCStatement> head = List.nil();
+ for (JCStatement stat : statements) {
+ if (JavacHandlerUtil.isConstructorCall(stat) || JavacHandlerUtil.isGenerated(stat)) {
+ tail = tail.tail;
+ head = head.prepend(stat);
+ continue;
+ }
+ break;
+ }
+
+ List<JCStatement> newList = tail.prepend(nullCheck);
+ for (JCStatement stat : head) newList = newList.prepend(stat);
+ declaration.body.stats = newList;
+ }
+
+ /**
+ * Checks if the statement is of the form 'if (x == null) {throw WHATEVER;},
+ * where the block braces are optional. If it is of this form, returns "x".
+ * If it is not of this form, returns null.
+ */
+ private String returnVarNameIfNullCheck(JCStatement stat) {
+ if (!(stat instanceof JCIf)) return null;
+
+ /* Check that the if's statement is a throw statement, possibly in a block. */ {
+ JCStatement then = ((JCIf) stat).thenpart;
+ if (then instanceof JCBlock) {
+ List<JCStatement> stats = ((JCBlock) then).stats;
+ if (stats.length() == 0) return null;
+ then = stats.get(0);
+ }
+ if (!(then instanceof JCThrow)) return null;
+ }
+
+ /* Check that the if's conditional is like 'x == null'. Return from this method (don't generate
+ a nullcheck) if 'x' is equal to our own variable's name: There's already a nullcheck here. */ {
+ JCExpression cond = ((JCIf) stat).cond;
+ while (cond instanceof JCParens) cond = ((JCParens) cond).expr;
+ if (!(cond instanceof JCBinary)) return null;
+ JCBinary bin = (JCBinary) cond;
+ if (compareCTC(getTag(bin), CTC_EQUAL)) return null;
+ if (!(bin.lhs instanceof JCIdent)) return null;
+ if (!(bin.rhs instanceof JCLiteral)) return null;
+ if (compareCTC(getTypeTag((JCLiteral) bin.rhs), CTC_BOT)) return null;
+ return ((JCIdent) bin.lhs).name.toString();
+ }
+ }
+}
diff --git a/src/delombok/lombok/delombok/LombokOptionsFactory.java b/src/delombok/lombok/delombok/LombokOptionsFactory.java
index 8d3b2528..e581593f 100644
--- a/src/delombok/lombok/delombok/LombokOptionsFactory.java
+++ b/src/delombok/lombok/delombok/LombokOptionsFactory.java
@@ -21,17 +21,16 @@
*/
package lombok.delombok;
+import lombok.javac.Javac;
import lombok.javac.Javac6BasedLombokOptions;
import lombok.javac.Javac8BasedLombokOptions;
import lombok.javac.LombokOptions;
-import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.util.Context;
public class LombokOptionsFactory {
-
enum LombokOptionCompilerVersion {
- JDK6_7 {
+ JDK7_AND_LOWER {
@Override LombokOptions createAndRegisterOptions(Context context) {
return Javac6BasedLombokOptions.replaceWithDelombokOptions(context);
}
@@ -46,17 +45,13 @@ public class LombokOptionsFactory {
abstract LombokOptions createAndRegisterOptions(Context context);
}
-
public static LombokOptions getDelombokOptions(Context context) {
LombokOptions options;
- if (JavaCompiler.version().startsWith("1.6") || JavaCompiler.version().startsWith("1.7")) {
- options = LombokOptionCompilerVersion.JDK6_7.createAndRegisterOptions(context);
- } else if (JavaCompiler.version().startsWith("1.8")) {
- options = LombokOptionCompilerVersion.JDK8.createAndRegisterOptions(context);
+ if (Javac.getJavaCompilerVersion() < 8) {
+ options = LombokOptionCompilerVersion.JDK7_AND_LOWER.createAndRegisterOptions(context);
} else {
- throw new IllegalStateException("No support for compiler version " + JavaCompiler.version() + " for delombok");
+ options = LombokOptionCompilerVersion.JDK8.createAndRegisterOptions(context);
}
return options;
-
}
}
diff --git a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
index 9817f050..075cc64f 100644
--- a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
+++ b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
@@ -22,6 +22,11 @@
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
+
+/*
+ * Code derived from com.sun.tools.javac.tree.Pretty, from the langtools project.
+ * A version can be found at, for example, http://hg.openjdk.java.net/jdk7/build/langtools
+ */
package lombok.delombok;
import static com.sun.tools.javac.code.Flags.ANNOTATION;
@@ -220,12 +225,21 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
return tree.getEndPosition(cu.endPositions);
}
- private void consumeComments(int till) throws IOException {
+ private void consumeComments(int until) throws IOException {
+ consumeComments(until, null);
+ }
+ private void consumeComments(int until, JCTree tree) throws IOException {
boolean prevNewLine = onNewLine;
boolean found = false;
CommentInfo head = comments.head;
- while (comments.nonEmpty() && head.pos < till) {
- printComment(head);
+ while (comments.nonEmpty() && head.pos < until) {
+ if (tree != null && docComments != null && docComments.containsKey(tree) && head.isJavadoc() && noFurtherJavadocForthcoming(until)) {
+ // This is (presumably) the exact same javadoc that has already been associated with the node that we're just about to
+ // print. These javadoc can be modified by lombok handlers, and as such we should NOT print them from the consumed comments db,
+ // and instead print the actual javadoc associated with the upcoming node (which the visit method for that node will take care of).
+ } else {
+ printComment(head);
+ }
comments = comments.tail;
head = comments.head;
}
@@ -233,6 +247,17 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
println();
}
}
+
+ private boolean noFurtherJavadocForthcoming(int until) {
+ List<CommentInfo> c = comments;
+ if (c.nonEmpty()) c = c.tail;
+ while (c.nonEmpty()) {
+ if (c.head.pos >= until) return true;
+ if (c.head.isJavadoc()) return false;
+ c = c.tail;
+ }
+ return true;
+ }
private void consumeTrailingComments(int from) throws IOException {
boolean prevNewLine = onNewLine;
@@ -404,7 +429,7 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
this.prec = prec;
if (tree == null) print("/*missing*/");
else {
- consumeComments(tree.pos);
+ consumeComments(tree.pos, tree);
tree.accept(this);
int endPos = endPos(tree);
consumeTrailingComments(endPos);
@@ -506,7 +531,14 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
print("/**"); println();
int pos = 0;
int endpos = lineEndPos(dc, pos);
+ boolean atStart = true;
while (pos < dc.length()) {
+ String line = dc.substring(pos, endpos);
+ if (line.trim().isEmpty() && atStart) {
+ atStart = false;
+ continue;
+ }
+ atStart = false;
align();
print(" *");
if (pos < dc.length() && dc.charAt(pos) > ' ') print(" ");
@@ -616,7 +648,7 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
docComments = tree.docComments;
printDocComment(tree);
if (tree.pid != null) {
- consumeComments(tree.pos);
+ consumeComments(tree.pos, tree);
print("package ");
printExpr(tree.pid);
print(";");
@@ -690,7 +722,7 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
public void visitClassDef(JCClassDecl tree) {
try {
- consumeComments(tree.pos);
+ consumeComments(tree.pos, tree);
println(); align();
printDocComment(tree);
printAnnotations(tree.mods.annotations);
@@ -982,6 +1014,29 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
public void visitTry(JCTry tree) {
try {
print("try ");
+ List<?> resources = null;
+ try {
+ Field f = JCTry.class.getField("resources");
+ resources = (List<?>) f.get(tree);
+ } catch (Exception ignore) {
+ // In JDK6 and down this field does not exist; resources will retain its initializer value which is what we want.
+ }
+
+ if (resources != null && resources.nonEmpty()) {
+ boolean first = true;
+ print("(");
+ for (Object var0 : resources) {
+ JCTree var = (JCTree) var0;
+ if (!first) {
+ println();
+ indent();
+ }
+ printStat(var);
+ first = false;
+ }
+ print(") ");
+ }
+
printStat(tree.body);
for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
printStat(l.head);
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
index f9b53e68..8c6011bc 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
@@ -267,7 +267,7 @@ public class EclipsePatcher extends Agent {
.build());
}
-
+
private static void patchPostCompileHookEclipse(ScriptManager sm) {
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder", "writeClassFileContents"))
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java
index 008e722a..a3d77055 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java
@@ -236,10 +236,8 @@ public class PatchExtensionMethod {
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 (arg instanceof MessageSend) {
+ ((MessageSend) arg).valueCast = arg.resolvedType;
}
if (!fixedBinding.parameters[i].isBaseType() && arg.resolvedType.isBaseType()) {
int id = arg.resolvedType.id;
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java b/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
index cb91fd25..0002e26e 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2012 The Project Lombok Authors.
+ * Copyright (C) 2010-2013 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
@@ -329,7 +329,7 @@ public class PatchFixes {
return newSimpleNames;
}
- public static byte[] runPostCompiler(byte[] bytes, String fileName) {
+ public static byte[] runPostCompiler(byte[] bytes, String fileName) {
byte[] transformed = PostCompiler.applyTransformations(bytes, fileName, DiagnosticsReceiver.CONSOLE);
return transformed == null ? bytes : transformed;
}
diff --git a/src/installer/lombok/installer/IdeLocation.java b/src/installer/lombok/installer/IdeLocation.java
index a15f2ac9..4d28fb90 100644
--- a/src/installer/lombok/installer/IdeLocation.java
+++ b/src/installer/lombok/installer/IdeLocation.java
@@ -25,6 +25,7 @@ import java.io.File;
import java.io.IOException;
import java.net.URL;
+import lombok.installer.eclipse.EclipseFinder;
import lombok.patcher.inject.LiveInjector;
/**
@@ -66,11 +67,12 @@ public abstract class IdeLocation {
}
private static final String LEGAL_PATH_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/";
+ private static final String LEGAL_PATH_CHARS_WINDOWS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/:\\ ";
public static String escapePath(String path) {
StringBuilder out = new StringBuilder();
-
+ String legalChars = IdeFinder.getOS() == EclipseFinder.OS.UNIX ? LEGAL_PATH_CHARS : LEGAL_PATH_CHARS_WINDOWS;
for (char c : path.toCharArray()) {
- if (LEGAL_PATH_CHARS.indexOf(c) == -1) out.append('\\');
+ if (legalChars.indexOf(c) == -1) out.append('\\');
out.append(c);
}
return out.toString();
diff --git a/src/installer/lombok/installer/eclipse/EclipseFinder.java b/src/installer/lombok/installer/eclipse/EclipseFinder.java
index 9fbcabbc..8a1a689a 100644
--- a/src/installer/lombok/installer/eclipse/EclipseFinder.java
+++ b/src/installer/lombok/installer/eclipse/EclipseFinder.java
@@ -64,14 +64,10 @@ public class EclipseFinder extends IdeFinder {
/**
* Returns a list of paths of Eclipse installations.
- * Eclipse installations are found by checking for the existence of 'eclipse.exe' in the following locations:
- * <ul>
- * <li>X:\*Program Files*\*Eclipse*</li>
- * <li>X:\*Eclipse*</li>
- * </ul>
*
- * Where 'X' is tried for all local disk drives, unless there's a problem calling fsutil, in which case only
- * C: is tried.
+ * The search process works by scanning for each 'source dir' for either an eclipse installation or a folder containing the text returned
+ * by getDirName(). If such a folder is found, this process is applied recursively. On windows, this process is run on each drive letter
+ * which represents a physical hard disk. If the native windows API call to determine these drive letters fails, only 'C:' is checked.
*/
private List<String> getSourceDirsOnWindowsWithDriveLetters() {
List<String> driveLetters = asList("C");
@@ -83,12 +79,22 @@ public class EclipseFinder extends IdeFinder {
List<String> sourceDirs = new ArrayList<String>();
for (String letter : driveLetters) {
for (String possibleSource : getSourceDirsOnWindows()) {
- sourceDirs.add(letter + ":" + possibleSource);
+ if (!isDriveSpecificOnWindows(possibleSource)) {
+ sourceDirs.add(letter + ":" + possibleSource);
+ }
}
}
+ for (String possibleSource : getSourceDirsOnWindows()) {
+ if (isDriveSpecificOnWindows(possibleSource)) sourceDirs.add(possibleSource);
+ }
+
return sourceDirs;
}
+ public boolean isDriveSpecificOnWindows(String path) {
+ return path.length() > 1 && path.charAt(1) == ':';
+ }
+
protected List<String> getSourceDirsOnMac() {
return Arrays.asList("/Applications", System.getProperty("user.home", "."));
}
diff --git a/src/installer/lombok/installer/eclipse/JbdsFinder.java b/src/installer/lombok/installer/eclipse/JbdsFinder.java
new file mode 100644
index 00000000..2dfaacba
--- /dev/null
+++ b/src/installer/lombok/installer/eclipse/JbdsFinder.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2013 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.installer.eclipse;
+
+import java.util.Arrays;
+import java.util.List;
+
+import lombok.installer.CorruptedIdeLocationException;
+import lombok.installer.IdeFinder;
+import lombok.installer.IdeLocation;
+
+import org.mangosdk.spi.ProviderFor;
+
+/**
+ * JBDS (JBoss Developer Studio) is an eclipse variant.
+ * Other than different executable names, it's the same as eclipse, as far as lombok support goes.
+ */
+@ProviderFor(IdeFinder.class)
+public class JbdsFinder extends EclipseFinder {
+ @Override protected IdeLocation createLocation(String guess) throws CorruptedIdeLocationException {
+ return new JbdsLocationProvider().create0(guess);
+ }
+
+ @Override protected String getDirName() {
+ return "studio";
+ }
+
+ @Override protected String getMacExecutableName() {
+ return "jbdevstudio.app";
+ }
+
+ @Override protected String getUnixExecutableName() {
+ return "jbdevstudio";
+ }
+
+ @Override protected String getWindowsExecutableName() {
+ return "jbdevstudio.exe";
+ }
+
+ @Override protected List<String> getSourceDirsOnWindows() {
+ return Arrays.asList("\\", "\\Program Files", "\\Program Files (x86)", System.getProperty("user.home", "."));
+ }
+
+ @Override protected List<String> getSourceDirsOnMac() {
+ return Arrays.asList("/Applications", System.getProperty("user.home", "."));
+ }
+
+ @Override protected List<String> getSourceDirsOnUnix() {
+ return Arrays.asList(System.getProperty("user.home", "."));
+ }
+}
diff --git a/src/installer/lombok/installer/eclipse/JbdsLocation.java b/src/installer/lombok/installer/eclipse/JbdsLocation.java
new file mode 100644
index 00000000..81fb5261
--- /dev/null
+++ b/src/installer/lombok/installer/eclipse/JbdsLocation.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 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.installer.eclipse;
+
+import java.io.File;
+import java.net.URL;
+
+import lombok.installer.CorruptedIdeLocationException;
+
+public class JbdsLocation extends EclipseLocation {
+ public JbdsLocation(String nameOfLocation, File pathToEclipseIni) throws CorruptedIdeLocationException {
+ super(nameOfLocation, pathToEclipseIni);
+ }
+
+ @Override public URL getIdeIcon() {
+ return JbdsLocation.class.getResource("jbds.png");
+ }
+
+ @Override protected String getIniFileName() {
+ return "jbdevstudio.ini";
+ }
+
+ @Override protected String getTypeName() {
+ return "JBoss Developer Studio";
+ }
+}
diff --git a/src/installer/lombok/installer/eclipse/JbdsLocationProvider.java b/src/installer/lombok/installer/eclipse/JbdsLocationProvider.java
new file mode 100644
index 00000000..e6df0e43
--- /dev/null
+++ b/src/installer/lombok/installer/eclipse/JbdsLocationProvider.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 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.installer.eclipse;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import lombok.installer.CorruptedIdeLocationException;
+import lombok.installer.IdeLocation;
+import lombok.installer.IdeLocationProvider;
+import lombok.installer.IdeFinder.OS;
+
+import org.mangosdk.spi.ProviderFor;
+
+@ProviderFor(IdeLocationProvider.class)
+public class JbdsLocationProvider extends EclipseLocationProvider {
+ @Override protected List<String> getEclipseExecutableNames() {
+ return Arrays.asList("jbdevstudio.app", "jbdevstudio.exe", "jbdevstudioc.exe", "jbdevstudio");
+ }
+
+ @Override protected String getIniName() {
+ return "jbdevstudio.ini";
+ }
+
+ @Override protected IdeLocation makeLocation(String name, File ini) throws CorruptedIdeLocationException {
+ return new JbdsLocation(name, ini);
+ }
+
+ @Override protected String getMacAppName() {
+ return "jbdevstudio.app";
+ }
+
+ @Override protected String getUnixAppName() {
+ return "jbdevstudio";
+ }
+
+ @Override public Pattern getLocationSelectors(OS os) {
+ switch (os) {
+ case MAC_OS_X:
+ return Pattern.compile("^(jbdevstudio|jbdevstudio\\.ini|jbdevstudio\\.app)$", Pattern.CASE_INSENSITIVE);
+ case WINDOWS:
+ return Pattern.compile("^(jbdevstudioc?\\.exe|jbdevstudio\\.ini)$", Pattern.CASE_INSENSITIVE);
+ default:
+ case UNIX:
+ return Pattern.compile("^(jbdevstudio|jbdevstudio\\.ini)$", Pattern.CASE_INSENSITIVE);
+ }
+ }
+}
diff --git a/src/installer/lombok/installer/eclipse/STSFinder.java b/src/installer/lombok/installer/eclipse/STSFinder.java
index 74b8ed34..82bc9b80 100644
--- a/src/installer/lombok/installer/eclipse/STSFinder.java
+++ b/src/installer/lombok/installer/eclipse/STSFinder.java
@@ -57,7 +57,7 @@ public class STSFinder extends EclipseFinder {
}
@Override protected List<String> getSourceDirsOnWindows() {
- return Arrays.asList("\\", "\\springsource", "\\Program Files", "\\Program Files\\springsource", System.getProperty("user.home", "."), System.getProperty("user.home", ".") + "\\springsource");
+ return Arrays.asList("\\", "\\springsource", "\\Program Files", "\\Program Files (x86)", "\\Program Files\\springsource", "\\Program Files (x86)\\springsource", System.getProperty("user.home", "."), System.getProperty("user.home", ".") + "\\springsource");
}
@Override protected List<String> getSourceDirsOnMac() {
diff --git a/src/installer/lombok/installer/eclipse/jbds.png b/src/installer/lombok/installer/eclipse/jbds.png
new file mode 100644
index 00000000..ca7738e6
--- /dev/null
+++ b/src/installer/lombok/installer/eclipse/jbds.png
Binary files differ
diff --git a/src/stubs/com/sun/tools/javac/util/Context.java b/src/javac-only-stubs/com/sun/tools/javac/util/Context.java
index 06b8ff4d..06b8ff4d 100644
--- a/src/stubs/com/sun/tools/javac/util/Context.java
+++ b/src/javac-only-stubs/com/sun/tools/javac/util/Context.java
diff --git a/src/stubs/com/sun/tools/javac/parser/DocCommentScanner.java b/src/stubs/com/sun/tools/javac/parser/DocCommentScanner.java
new file mode 100644
index 00000000..d461d54b
--- /dev/null
+++ b/src/stubs/com/sun/tools/javac/parser/DocCommentScanner.java
@@ -0,0 +1,25 @@
+/*
+ * These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these.
+ */
+package com.sun.tools.javac.parser;
+
+import java.nio.CharBuffer;
+
+public class DocCommentScanner extends Scanner {
+ protected DocCommentScanner(Factory fac, CharBuffer buffer) {
+ super(fac, buffer);
+ }
+
+ protected DocCommentScanner(Factory fac, char[] input, int inputLength) {
+ super(fac, input, inputLength);
+ }
+
+ protected DocCommentScanner(ScannerFactory fac, CharBuffer buffer) {
+ super(fac, buffer);
+ }
+
+ protected DocCommentScanner(ScannerFactory fac, char[] input, int inputLength) {
+ super(fac, input, inputLength);
+ }
+
+}
diff --git a/src/stubs/com/sun/tools/javac/parser/Scanner.java b/src/stubs/com/sun/tools/javac/parser/Scanner.java
index af94bacc..266208e5 100644
--- a/src/stubs/com/sun/tools/javac/parser/Scanner.java
+++ b/src/stubs/com/sun/tools/javac/parser/Scanner.java
@@ -59,4 +59,11 @@ public class Scanner implements Lexer {
public char[] getRawCharacters(int beginIndex, int endIndex) {
return null;
}
+
+ public void nextToken() {
+ }
+
+ public char[] getRawCharacters() {
+ return new char[0];
+ }
}
diff --git a/src/stubs/com/sun/tools/javadoc/DocCommentScanner.java b/src/stubs/com/sun/tools/javadoc/DocCommentScanner.java
new file mode 100644
index 00000000..e2ea9614
--- /dev/null
+++ b/src/stubs/com/sun/tools/javadoc/DocCommentScanner.java
@@ -0,0 +1,25 @@
+/*
+ * These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these.
+ */
+package com.sun.tools.javadoc;
+
+import java.nio.CharBuffer;
+
+import com.sun.tools.javac.parser.Scanner;
+import com.sun.tools.javac.util.Context;
+
+public class DocCommentScanner extends Scanner {
+ protected DocCommentScanner(com.sun.tools.javadoc.DocCommentScanner.Factory fac, CharBuffer buffer) {
+ super(fac, buffer);
+ }
+
+ protected DocCommentScanner(com.sun.tools.javadoc.DocCommentScanner.Factory fac, char[] input, int inputLength) {
+ super(fac, input, inputLength);
+ }
+
+ public static class Factory extends Scanner.Factory {
+ protected Factory(Context context) {
+ super(context);
+ }
+ }
+}
diff --git a/src/utils/lombok/core/JavaIdentifiers.java b/src/utils/lombok/core/JavaIdentifiers.java
new file mode 100644
index 00000000..cbe90eed
--- /dev/null
+++ b/src/utils/lombok/core/JavaIdentifiers.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 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.core;
+
+/**
+ * Utility functions for validating potential java verifiers.
+ */
+public class JavaIdentifiers {
+ private JavaIdentifiers() {}
+
+ private static final LombokImmutableList<String> KEYWORDS = LombokImmutableList.of(
+ "public", "private", "protected",
+ "default", "switch", "case",
+ "for", "do", "goto", "const", "strictfp", "while", "if", "else",
+ "byte", "short", "int", "long", "float", "double", "void", "boolean", "char",
+ "null", "false", "true",
+ "continue", "break", "return", "instanceof",
+ "synchronized", "volatile", "transient", "final", "static",
+ "interface", "class", "extends", "implements", "throws",
+ "throw", "catch", "try", "finally", "abstract", "assert",
+ "enum", "import", "package", "native", "new", "super", "this");
+
+ public static boolean isValidJavaIdentifier(String identifier) {
+ if (identifier == null) return false;
+ if (identifier.isEmpty()) return false;
+
+ if (!Character.isJavaIdentifierStart(identifier.charAt(0))) return false;
+ for (int i = 1; i < identifier.length(); i++) {
+ if (!Character.isJavaIdentifierPart(identifier.charAt(i))) return false;
+ }
+
+ return !isKeyword(identifier);
+ }
+
+ public static boolean isKeyword(String keyword) {
+ return KEYWORDS.contains(keyword);
+ }
+}
diff --git a/src/utils/lombok/core/LombokImmutableList.java b/src/utils/lombok/core/LombokImmutableList.java
new file mode 100644
index 00000000..e0e1136c
--- /dev/null
+++ b/src/utils/lombok/core/LombokImmutableList.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2013 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.core;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+public final class LombokImmutableList<T> implements Iterable<T> {
+ private Object[] content;
+ private static final LombokImmutableList<?> EMPTY = new LombokImmutableList<Object>(new Object[0]);
+
+ @SuppressWarnings("unchecked")
+ public static <T> LombokImmutableList<T> of() {
+ return (LombokImmutableList<T>) EMPTY;
+ }
+
+ public static <T> LombokImmutableList<T> of(T a) {
+ return new LombokImmutableList<T>(new Object[] {a});
+ }
+
+ public static <T> LombokImmutableList<T> of(T a, T b) {
+ return new LombokImmutableList<T>(new Object[] {a, b});
+ }
+
+ public static <T> LombokImmutableList<T> of(T a, T b, T c) {
+ return new LombokImmutableList<T>(new Object[] {a, b, c});
+ }
+
+ public static <T> LombokImmutableList<T> of(T a, T b, T c, T d) {
+ return new LombokImmutableList<T>(new Object[] {a, b, c, d});
+ }
+
+ public static <T> LombokImmutableList<T> of(T a, T b, T c, T d, T e) {
+ return new LombokImmutableList<T>(new Object[] {a, b, c, d, e});
+ }
+
+ public static <T> LombokImmutableList<T> of(T a, T b, T c, T d, T e, T f, T... g) {
+ Object[] rest = g == null ? new Object[] {null} : g;
+ Object[] val = new Object[rest.length + 6];
+ System.arraycopy(rest, 0, val, 6, rest.length);
+ val[0] = a;
+ val[1] = b;
+ val[2] = c;
+ val[3] = d;
+ val[4] = e;
+ val[5] = f;
+ return new LombokImmutableList<T>(val);
+ }
+
+ public static <T> LombokImmutableList<T> copyOf(Collection<? extends T> list) {
+ return new LombokImmutableList<T>(list.toArray());
+ }
+
+ public static <T> LombokImmutableList<T> copyOf(Iterable<? extends T> iterable) {
+ List<T> list = new ArrayList<T>();
+ for (T o : iterable) list.add(o);
+ return copyOf(list);
+ }
+
+ private LombokImmutableList(Object[] content) {
+ this.content = content;
+ }
+
+ public LombokImmutableList<T> replaceElementAt(int idx, T newValue) {
+ Object[] newContent = content.clone();
+ newContent[idx] = newValue;
+ return new LombokImmutableList<T>(newContent);
+ }
+
+ public LombokImmutableList<T> append(T newValue) {
+ int len = content.length;
+ Object[] newContent = new Object[len + 1];
+ System.arraycopy(content, 0, newContent, 0, len);
+ newContent[len] = newValue;
+ return new LombokImmutableList<T>(newContent);
+ }
+
+ public LombokImmutableList<T> prepend(T newValue) {
+ int len = content.length;
+ Object[] newContent = new Object[len + 1];
+ System.arraycopy(content, 0, newContent, 1, len);
+ newContent[0] = newValue;
+ return new LombokImmutableList<T>(newContent);
+ }
+
+ public int indexOf(T val) {
+ int len = content.length;
+ if (val == null) {
+ for (int i = 0; i < len; i++) if (content[i] == null) return i;
+ return -1;
+ }
+
+ for (int i = 0; i < len; i++) if (val.equals(content[i])) return i;
+ return -1;
+ }
+
+ public LombokImmutableList<T> removeElement(T val) {
+ int idx = indexOf(val);
+ return idx == -1 ? this : removeElementAt(idx);
+ }
+
+ public LombokImmutableList<T> removeElementAt(int idx) {
+ int len = content.length;
+ Object[] newContent = new Object[len - 1];
+ if (idx > 0) System.arraycopy(content, 0, newContent, 0, idx);
+ if (idx < len - 1) System.arraycopy(content, idx + 1, newContent, idx, len - idx - 1);
+ return new LombokImmutableList<T>(newContent);
+ }
+
+ public boolean isEmpty() {
+ return content.length == 0;
+ }
+
+ public int size() {
+ return content.length;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T get(int idx) {
+ return (T) content[idx];
+ }
+
+ public boolean contains(T in) {
+ if (in == null) {
+ for (Object e : content) if (e == null) return true;
+ return false;
+ }
+
+ for (Object e : content) if (in.equals(e)) return true;
+ return false;
+ }
+
+ public Iterator<T> iterator() {
+ return new Iterator<T>() {
+ private int idx = 0;
+ @Override public boolean hasNext() {
+ return idx < content.length;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public T next() {
+ if (idx < content.length) return (T) content[idx++];
+ throw new NoSuchElementException();
+ }
+
+ @Override public void remove() {
+ throw new UnsupportedOperationException("List is immutable");
+ }
+ };
+ }
+
+ @Override public String toString() {
+ return Arrays.toString(content);
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (!(obj instanceof LombokImmutableList)) return false;
+ if (obj == this) return true;
+ return Arrays.equals(content, ((LombokImmutableList<?>) obj).content);
+ }
+
+ @Override public int hashCode() {
+ return Arrays.hashCode(content);
+ }
+}
diff --git a/src/utils/lombok/eclipse/Eclipse.java b/src/utils/lombok/eclipse/Eclipse.java
index 301925d1..c2a863d5 100644
--- a/src/utils/lombok/eclipse/Eclipse.java
+++ b/src/utils/lombok/eclipse/Eclipse.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -21,6 +21,7 @@
*/
package lombok.eclipse;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -36,8 +37,11 @@ import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Literal;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.TryStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
public class Eclipse {
@@ -58,7 +62,9 @@ public class Eclipse {
* but we need to deal with it. This turns [[java][lang][String]] into "java.lang.String".
*/
public static String toQualifiedName(char[][] typeName) {
- StringBuilder sb = new StringBuilder();
+ int len = typeName.length - 1;
+ for (char[] c : typeName) len += c.length;
+ StringBuilder sb = new StringBuilder(len);
boolean first = true;
for (char[] c : typeName) {
sb.append(first ? "" : ".").append(c);
@@ -175,4 +181,58 @@ public class Eclipse {
return null;
}
+
+ private static long latestEcjCompilerVersionConstantCached = 0;
+
+ public static long getLatestEcjCompilerVersionConstant() {
+ if (latestEcjCompilerVersionConstantCached != 0) return latestEcjCompilerVersionConstantCached;
+
+ int highestVersionSoFar = 0;
+ for (Field f : ClassFileConstants.class.getDeclaredFields()) {
+ try {
+ if (f.getName().startsWith("JDK1_")) {
+ int thisVersion = Integer.parseInt(f.getName().substring("JDK1_".length()));
+ if (thisVersion > highestVersionSoFar) {
+ highestVersionSoFar = thisVersion;
+ latestEcjCompilerVersionConstantCached = (Long) f.get(null);
+ }
+ }
+ } catch (Exception ignore) {}
+ }
+
+ if (highestVersionSoFar > 6 && !ecjSupportsJava7Features()) {
+ latestEcjCompilerVersionConstantCached = ClassFileConstants.JDK1_6;
+ }
+ return latestEcjCompilerVersionConstantCached;
+ }
+
+ private static int ecjCompilerVersionCached = -1;
+ public static int getEcjCompilerVersion() {
+ if (ecjCompilerVersionCached >= 0) return ecjCompilerVersionCached;
+
+ for (Field f : CompilerOptions.class.getDeclaredFields()) {
+ try {
+ if (f.getName().startsWith("VERSION_1_")) {
+ ecjCompilerVersionCached = Math.max(ecjCompilerVersionCached, Integer.parseInt(f.getName().substring("VERSION_1_".length())));
+ }
+ } catch (Exception ignore) {}
+ }
+
+ if (ecjCompilerVersionCached < 5) ecjCompilerVersionCached = 5;
+ if (!ecjSupportsJava7Features()) ecjCompilerVersionCached = Math.min(6, ecjCompilerVersionCached);
+ return ecjCompilerVersionCached;
+ }
+
+ /**
+ * Certain ECJ versions that only go up to -source 6 report that they support -source 7 and even fail to error when -source 7 is applied.
+ * We detect this and correctly say that no more than -source 6 is supported. (when this is the case, this method returns false).
+ */
+ private static boolean ecjSupportsJava7Features() {
+ try {
+ TryStatement.class.getDeclaredField("resources");
+ return true;
+ } catch (NoSuchFieldException e) {
+ return false;
+ }
+ }
}
diff --git a/src/utils/lombok/javac/CommentCatcher.java b/src/utils/lombok/javac/CommentCatcher.java
index e3754627..8d1e71c0 100644
--- a/src/utils/lombok/javac/CommentCatcher.java
+++ b/src/utils/lombok/javac/CommentCatcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Project Lombok Authors.
+ * Copyright (C) 2011-2013 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
@@ -21,9 +21,12 @@
*/
package lombok.javac;
+import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.WeakHashMap;
+import lombok.Lombok;
+
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.util.Context;
@@ -62,36 +65,33 @@ public class CommentCatcher {
private static void registerCommentsCollectingScannerFactory(Context context) {
try {
- if (JavaCompiler.version().startsWith("1.6")) {
- Class.forName("lombok.javac.java6.CommentCollectingScannerFactory").getMethod("preRegister", Context.class).invoke(null, context);
- } else if (JavaCompiler.version().startsWith("1.7") || JavaCompiler.version().startsWith("1.8")) {
- Class.forName("lombok.javac.java7.CommentCollectingScannerFactory").getMethod("preRegister", Context.class).invoke(null, context);
+ Class<?> scannerFactory;
+ if (Javac.getJavaCompilerVersion() <= 6) {
+ scannerFactory = Class.forName("lombok.javac.java6.CommentCollectingScannerFactory");
} else {
- throw new IllegalStateException("No comments parser for compiler version " + JavaCompiler.version());
+ scannerFactory = Class.forName("lombok.javac.java7.CommentCollectingScannerFactory");
}
+ scannerFactory.getMethod("preRegister", Context.class).invoke(null, context);
+ } catch (InvocationTargetException e) {
+ throw Lombok.sneakyThrow(e.getCause());
} catch (Exception e) {
- if (e instanceof RuntimeException) throw (RuntimeException)e;
- throw new RuntimeException(e);
+ throw Lombok.sneakyThrow(e);
}
}
private static void setInCompiler(JavaCompiler compiler, Context context, Map<JCCompilationUnit, List<CommentInfo>> commentsMap) {
-
try {
- if (JavaCompiler.version().startsWith("1.6")) {
- Class<?> parserFactory = Class.forName("lombok.javac.java6.CommentCollectingParserFactory");
- parserFactory.getMethod("setInCompiler",JavaCompiler.class, Context.class, Map.class).invoke(null, compiler, context, commentsMap);
- } else if (JavaCompiler.version().startsWith("1.7") || JavaCompiler.version().startsWith("1.8")) {
- Class<?> parserFactory = Class.forName("lombok.javac.java7.CommentCollectingParserFactory");
- parserFactory.getMethod("setInCompiler",JavaCompiler.class, Context.class, Map.class).invoke(null, compiler, context, commentsMap);
+ Class<?> parserFactory;
+ if (Javac.getJavaCompilerVersion() <= 6) {
+ parserFactory = Class.forName("lombok.javac.java6.CommentCollectingParserFactory");
} else {
- throw new IllegalStateException("No comments parser for compiler version " + JavaCompiler.version());
+ parserFactory = Class.forName("lombok.javac.java7.CommentCollectingParserFactory");
}
-
+ parserFactory.getMethod("setInCompiler", JavaCompiler.class, Context.class, Map.class).invoke(null, compiler, context, commentsMap);
+ } catch (InvocationTargetException e) {
+ throw Lombok.sneakyThrow(e.getCause());
} catch (Exception e) {
- if (e instanceof RuntimeException) throw (RuntimeException)e;
- throw new RuntimeException(e);
+ throw Lombok.sneakyThrow(e);
}
}
-
}
diff --git a/src/utils/lombok/javac/CommentInfo.java b/src/utils/lombok/javac/CommentInfo.java
index 7375d51a..afdd8469 100644
--- a/src/utils/lombok/javac/CommentInfo.java
+++ b/src/utils/lombok/javac/CommentInfo.java
@@ -66,7 +66,7 @@ public final class CommentInfo {
}
public boolean isJavadoc() {
- return content.startsWith("/**");
+ return content.startsWith("/**") && content.length() > 4;
}
@Override
diff --git a/src/utils/lombok/javac/JCNoTypeFactory.java b/src/utils/lombok/javac/JCNoTypeFactory.java
deleted file mode 100644
index e7be7665..00000000
--- a/src/utils/lombok/javac/JCNoTypeFactory.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package lombok.javac;
-
-import com.sun.tools.javac.code.Symbol.TypeSymbol;
-import com.sun.tools.javac.code.Type;
-
-
-public class JCNoTypeFactory {
- public static final Type getJCNotType(Object typeTag, TypeSymbol tsym) {
-
- }
-}
diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java
index 9f2936a8..dfa51e00 100644
--- a/src/utils/lombok/javac/Javac.java
+++ b/src/utils/lombok/javac/Javac.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 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
@@ -21,19 +21,38 @@
*/
package lombok.javac;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.util.Objects;
+import java.lang.reflect.Modifier;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeVisitor;
+
+import lombok.Lombok;
+
+import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.main.JavaCompiler;
+import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCBinary;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCLiteral;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCUnary;
import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Name;
/**
* Container for static utility methods relevant to lombok's operation on javac.
@@ -46,6 +65,30 @@ public class Javac {
/** Matches any of the 8 primitive names, such as {@code boolean}. */
private static final Pattern PRIMITIVE_TYPE_NAME_PATTERN = Pattern.compile("^(boolean|byte|short|int|long|float|double|char)$");
+ private static final Pattern VERSION_PARSER = Pattern.compile("^(\\d{1,6})\\.(\\d{1,6}).*$");
+
+ private static final AtomicInteger compilerVersion = new AtomicInteger(-1);
+
+ /**
+ * Returns the version of this java compiler, i.e. the JDK that it shipped in. For example, for javac v1.7, this returns {@code 7}.
+ */
+ public static int getJavaCompilerVersion() {
+ int cv = compilerVersion.get();
+ if (cv != -1) return cv;
+ Matcher m = VERSION_PARSER.matcher(JavaCompiler.version());
+ if (m.matches()) {
+ int major = Integer.parseInt(m.group(1));
+ int minor = Integer.parseInt(m.group(2));
+ if (major == 1) {
+ compilerVersion.set(minor);
+ return minor;
+ }
+ }
+
+ compilerVersion.set(6);
+ return 6;
+ }
+
/**
* Checks if the given expression (that really ought to refer to a type
* expression) represents a primitive type.
@@ -102,56 +145,53 @@ public class Javac {
public static final Object CTC_EQUAL = getTreeTag("EQ");
public static boolean compareCTC(Object ctc1, Object ctc2) {
- return Objects.equals(ctc1, ctc2);
+ return ctc1 == null ? ctc2 == null : ctc1.equals(ctc2);
}
+ private static final ConcurrentMap<String, Object> TYPE_TAG_CACHE = new ConcurrentHashMap<String, Object>();
+ private static final ConcurrentMap<String, Object> TREE_TAG_CACHE = new ConcurrentHashMap<String, Object>();
+
+
/**
- * Retrieves the current type tag. The actual type object differs depending on the Compiler version
- *
- * For JDK 8 this is an enum value of type <code>com.sun.tools.javac.code.TypeTag</code>
- * for JDK 7 and lower, this is the value of the constant within <code>com.sun.tools.javac.code.TypeTags</code>
+ * Retrieves the provided TypeTag value, in a compiler version independent manner.
*
+ * The actual type object differs depending on the Compiler version:
+ * <ul>
+ * <li>For JDK 8 this is an enum value of type <code>com.sun.tools.javac.code.TypeTag</code>
+ * <li>for JDK 7 and lower, this is the value of the constant within <code>com.sun.tools.javac.code.TypeTags</code>
+ * </ul>
* Solves the problem of compile time constant inlining, resulting in lombok
* having the wrong value (javac compiler changes private api constants from
* time to time).
*
- * @param identifier
- * @return the ordinal value of the typetag constant
+ * @param identifier Identifier to turn into a TypeTag.
+ * @return the value of the typetag constant (either enum instance or an Integer object).
*/
public static Object getTypeTag(String identifier) {
- try {
- if (JavaCompiler.version().startsWith("1.8")) {
- return Class.forName("com.sun.tools.javac.code.TypeTag").getField(identifier).get(null);
- } else {
- return Class.forName("com.sun.tools.javac.code.TypeTags").getField(identifier).get(null);
- }
- } catch (NoSuchFieldException e) {
- throw new RuntimeException(e);
- } catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- } catch (Exception e) {
- if (e instanceof RuntimeException) throw (RuntimeException) e;
- throw new RuntimeException(e);
- }
+ return getFieldCached(TYPE_TAG_CACHE, getJavaCompilerVersion() < 8 ? "com.sun.tools.javac.code.TypeTag" : "com.sun.tools.javac.code.TypeTags", identifier);
}
public static Object getTreeTag(String identifier) {
- try {
- if (JavaCompiler.version().startsWith("1.8")) {
- return Class.forName("com.sun.tools.javac.tree.JCTree$Tag").getField(identifier).get(null);
- } else {
- return Class.forName("com.sun.tools.javac.tree.JCTree").getField(identifier).get(null);
- }
+ return getFieldCached(TREE_TAG_CACHE, getJavaCompilerVersion() < 8 ? "com.sun.tools.javac.tree.JCTree" : "com.sun.tools.javac.tree.JCTree$Tag", identifier);
+ }
+
+ private static Object getFieldCached(ConcurrentMap<String, Object> cache, String className, String fieldName) {
+ Object value = cache.get(fieldName);
+ if (value != null) return value;
+ try {
+ value = Class.forName(className).getField(fieldName).get(null);
} catch (NoSuchFieldException e) {
- throw new RuntimeException(e);
+ throw Lombok.sneakyThrow(e);
} catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- } catch (Exception e) {
- if (e instanceof RuntimeException) throw (RuntimeException) e;
- throw new RuntimeException(e);
+ throw Lombok.sneakyThrow(e);
+ } catch (ClassNotFoundException e) {
+ throw Lombok.sneakyThrow(e);
}
+
+ cache.putIfAbsent(fieldName, value);
+ return value;
}
-
+
public static Object getTreeTypeTag(JCPrimitiveTypeTree tree) {
return tree.typetag;
}
@@ -159,82 +199,239 @@ public class Javac {
public static Object getTreeTypeTag(JCLiteral tree) {
return tree.typetag;
}
+
+ private static final Method createIdent, createLiteral, createUnary, createBinary;
+ static {
+ if (getJavaCompilerVersion() < 8) {
+ createIdent = getMethod(TreeMaker.class, "TypeIdent", int.class);
+ } else {
+ createIdent = getMethod(TreeMaker.class, "TypeIdent", "com.sun.tools.javac.code.TypeTag");
+ }
+ createIdent.setAccessible(true);
+
+ if (getJavaCompilerVersion() < 8) {
+ createLiteral = getMethod(TreeMaker.class, "Literal", int.class, Object.class);
+ } else {
+ createLiteral = getMethod(TreeMaker.class, "Literal", "com.sun.tools.javac.code.TypeTag", "java.lang.Object");
+ }
+ createLiteral.setAccessible(true);
+
+ if (getJavaCompilerVersion() < 8) {
+ createUnary = getMethod(TreeMaker.class, "Unary", int.class, JCExpression.class);
+ } else {
+ createUnary = getMethod(TreeMaker.class, "Unary", "com.sun.tools.javac.code.TypeTag", JCExpression.class.getName());
+ }
+ createUnary.setAccessible(true);
+
+ if (getJavaCompilerVersion() < 8) {
+ createBinary = getMethod(TreeMaker.class, "Binary", Integer.TYPE, JCExpression.class, JCExpression.class);
+ } else {
+ createBinary = getMethod(TreeMaker.class, "Binary", "com.sun.tools.javac.code.TypeTag", JCExpression.class.getName(), JCExpression.class.getName());
+ }
+ createBinary.setAccessible(true);
+ }
+
+ private static Method getMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
+ try {
+ return clazz.getMethod(name, paramTypes);
+ } catch (NoSuchMethodException e) {
+ throw Lombok.sneakyThrow(e);
+ }
+ }
+
+ private static Method getMethod(Class<?> clazz, String name, String... paramTypes) {
+ try {
+ Class<?>[] c = new Class[paramTypes.length];
+ for (int i = 0; i < paramTypes.length; i++) c[i] = Class.forName(paramTypes[i]);
+ return clazz.getMethod(name, c);
+ } catch (NoSuchMethodException e) {
+ throw Lombok.sneakyThrow(e);
+ } catch (ClassNotFoundException e) {
+ throw Lombok.sneakyThrow(e);
+ }
+ }
+
public static JCExpression makeTypeIdent(TreeMaker maker, Object ctc) {
try {
- Method createIdent;
- if (JavaCompiler.version().startsWith("1.8")) {
- createIdent = TreeMaker.class.getMethod("TypeIdent", Class.forName("com.sun.tools.javac.code.TypeTag"));
- } else {
- createIdent = TreeMaker.class.getMethod("TypeIdent", Integer.TYPE);
- }
return (JCExpression) createIdent.invoke(maker, ctc);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
} catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- } catch (Exception e) {
- if (e instanceof RuntimeException) throw (RuntimeException) e;
- throw new RuntimeException(e);
+ throw Lombok.sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ throw Lombok.sneakyThrow(e.getCause());
}
}
public static JCLiteral makeLiteral(TreeMaker maker, Object ctc, Object argument) {
try {
- Method createLiteral;
- if (JavaCompiler.version().startsWith("1.8")) {
- createLiteral = TreeMaker.class.getMethod("Literal", Class.forName("com.sun.tools.javac.code.TypeTag"), Object.class);
- } else {
- createLiteral = TreeMaker.class.getMethod("Literal", Integer.TYPE, Object.class);
- }
return (JCLiteral) createLiteral.invoke(maker, ctc, argument);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
} catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- } catch (Exception e) {
- if (e instanceof RuntimeException) throw (RuntimeException) e;
- throw new RuntimeException(e);
+ throw Lombok.sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ throw Lombok.sneakyThrow(e.getCause());
}
}
public static JCUnary makeUnary(TreeMaker maker, Object ctc, JCExpression argument) {
try {
- Method createUnary;
- if (JavaCompiler.version().startsWith("1.8")) {
- createUnary = TreeMaker.class.getMethod("Unary", Class.forName("com.sun.tools.javac.code.TypeTag"), JCExpression.class);
- } else {
- createUnary = TreeMaker.class.getMethod("Unary", Integer.TYPE, JCExpression.class);
- }
return (JCUnary) createUnary.invoke(maker, ctc, argument);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
} catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- } catch (Exception e) {
- if (e instanceof RuntimeException) throw (RuntimeException) e;
- throw new RuntimeException(e);
+ throw Lombok.sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ throw Lombok.sneakyThrow(e.getCause());
}
}
- public static JCBinary makeBinary(TreeMaker maker, Object ctc, JCExpression rhsArgument, JCExpression lhsArgument) {
+ public static JCBinary makeBinary(TreeMaker maker, Object ctc, JCExpression lhsArgument, JCExpression rhsArgument) {
try {
- Method createUnary;
- if (JavaCompiler.version().startsWith("1.8")) {
- createUnary = TreeMaker.class.getMethod("Binary", Class.forName("com.sun.tools.javac.code.TypeTag"), JCExpression.class, JCExpression.class);
- } else {
- createUnary = TreeMaker.class.getMethod("Binary", Integer.TYPE, JCExpression.class, JCExpression.class);
- }
- return (JCBinary) createUnary.invoke(maker, ctc, rhsArgument, lhsArgument);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
+ return (JCBinary) createBinary.invoke(maker, ctc, lhsArgument, rhsArgument);
} catch (IllegalAccessException e) {
- throw new RuntimeException(e);
+ throw Lombok.sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ throw Lombok.sneakyThrow(e.getCause());
+ }
+ }
+
+ private static final Class<?> JC_VOID_TYPE, JC_NO_TYPE;
+
+ static {
+ Class<?> c = null;
+ try {
+ c = Class.forName("com.sun.tools.javac.code.Type$JCVoidType");
+ } catch (Exception ignore) {}
+ JC_VOID_TYPE = c;
+ c = null;
+ try {
+ c = Class.forName("com.sun.tools.javac.code.Type$JCNoType");
+ } catch (Exception ignore) {}
+ JC_NO_TYPE = c;
+ }
+
+ public static Type createVoidType(TreeMaker maker, Object tag) {
+ if (Javac.getJavaCompilerVersion() < 8) {
+ return new JCNoType(((Integer) tag).intValue());
+ } else {
+ try {
+ if (compareCTC(tag, CTC_VOID)) {
+ return (Type) JC_VOID_TYPE.newInstance();
+ } else {
+ return (Type) JC_NO_TYPE.newInstance();
+ }
+ } catch (IllegalAccessException e) {
+ throw Lombok.sneakyThrow(e);
+ } catch (InstantiationException e) {
+ throw Lombok.sneakyThrow(e);
+ }
+ }
+ }
+
+ private static class JCNoType extends Type implements NoType {
+ public JCNoType(int tag) {
+ super(tag, null);
+ }
+
+ @Override
+ public TypeKind getKind() {
+ if (Javac.compareCTC(tag, CTC_VOID)) return TypeKind.VOID;
+ if (Javac.compareCTC(tag, CTC_NONE)) return TypeKind.NONE;
+ throw new AssertionError("Unexpected tag: " + tag);
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitNoType(this, p);
+ }
+ }
+
+ private static final Field JCTREE_TAG, JCLITERAL_TYPETAG, JCPRIMITIVETYPETREE_TYPETAG;
+ private static final Method JCTREE_GETTAG;
+ static {
+ Field f = null;
+ try {
+ f = JCTree.class.getDeclaredField("tag");
+ } catch (NoSuchFieldException e) {}
+ JCTREE_TAG = f;
+
+ f = null;
+ try {
+ f = JCLiteral.class.getDeclaredField("typetag");
+ } catch (NoSuchFieldException e) {}
+ JCLITERAL_TYPETAG = f;
+
+ f = null;
+ try {
+ f = JCPrimitiveTypeTree.class.getDeclaredField("typetag");
+ } catch (NoSuchFieldException e) {}
+ JCPRIMITIVETYPETREE_TYPETAG = f;
+
+ Method m = null;
+ try {
+ m = JCTree.class.getDeclaredMethod("getTag");
+ } catch (NoSuchMethodException e) {}
+ JCTREE_GETTAG = m;
+ }
+
+ public static Object getTag(JCTree node) {
+ if (JCTREE_GETTAG != null) {
+ try {
+ return JCTREE_GETTAG.invoke(node);
+ } catch (Exception e) {}
+ }
+ try {
+ return JCTREE_TAG.get(node);
} catch (Exception e) {
- if (e instanceof RuntimeException) throw (RuntimeException) e;
- throw new RuntimeException(e);
+ throw new IllegalStateException("Can't get node tag");
}
}
-
+ public static Object getTypeTag(JCLiteral node) {
+ try {
+ return JCLITERAL_TYPETAG.get(node);
+ } catch (Exception e) {
+ throw new IllegalStateException("Can't get JCLiteral typetag");
+ }
+ }
+
+ public static Object getTypeTag(JCPrimitiveTypeTree node) {
+ try {
+ return JCPRIMITIVETYPETREE_TYPETAG.get(node);
+ } catch (Exception e) {
+ throw new IllegalStateException("Can't get JCPrimitiveTypeTree typetag");
+ }
+ }
+
+ private static Method classDef;
+
+ public static JCClassDecl ClassDef(TreeMaker maker, JCModifiers mods, Name name, List<JCTypeParameter> typarams, JCExpression extending, List<JCExpression> implementing, List<JCTree> defs) {
+ if (classDef == null) try {
+ classDef = TreeMaker.class.getDeclaredMethod("ClassDef", JCModifiers.class, Name.class, List.class, JCExpression.class, List.class, List.class);
+ } catch (NoSuchMethodException ignore) {}
+ if (classDef == null) try {
+ classDef = TreeMaker.class.getDeclaredMethod("ClassDef", JCModifiers.class, Name.class, List.class, JCTree.class, List.class, List.class);
+ } catch (NoSuchMethodException ignore) {}
+
+ if (classDef == null) throw new IllegalStateException("Lombok bug #20130617-1310: ClassDef doesn't look like anything we thought it would look like.");
+ if (!Modifier.isPublic(classDef.getModifiers()) && !classDef.isAccessible()) {
+ classDef.setAccessible(true);
+ }
+
+ try {
+ return (JCClassDecl) classDef.invoke(maker, mods, name, typarams, extending, implementing, defs);
+ } catch (InvocationTargetException e) {
+ throw sneakyThrow(e.getCause());
+ } catch (IllegalAccessException e) {
+ throw sneakyThrow(e.getCause());
+ }
+ }
+
+ private static RuntimeException sneakyThrow(Throwable t) {
+ if (t == null) throw new NullPointerException("t");
+ Javac.<RuntimeException>sneakyThrow0(t);
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T {
+ throw (T)t;
+ }
}
diff --git a/src/utils/lombok/javac/java6/CommentCollectingParserFactory.java b/src/utils/lombok/javac/java6/CommentCollectingParserFactory.java
index b2a248c8..7e34b723 100644
--- a/src/utils/lombok/javac/java6/CommentCollectingParserFactory.java
+++ b/src/utils/lombok/javac/java6/CommentCollectingParserFactory.java
@@ -25,7 +25,7 @@ public class CommentCollectingParserFactory extends Parser.Factory {
}
@Override public Parser newParser(Lexer S, boolean keepDocComments, boolean genEndPos) {
- Object x = new CommentCollectingParser(this, S, keepDocComments, commentsMap);
+ Object x = new CommentCollectingParser(this, S, true, commentsMap);
return (Parser) x;
// CCP is based on a stub which extends nothing, but at runtime the stub is replaced with either
//javac6's EndPosParser which extends Parser, or javac7's EndPosParser which implements Parser.
diff --git a/src/utils/lombok/javac/java6/CommentCollectingScanner.java b/src/utils/lombok/javac/java6/CommentCollectingScanner.java
index 66e1514d..b584ec16 100644
--- a/src/utils/lombok/javac/java6/CommentCollectingScanner.java
+++ b/src/utils/lombok/javac/java6/CommentCollectingScanner.java
@@ -27,12 +27,10 @@ import lombok.javac.CommentInfo;
import lombok.javac.CommentInfo.EndConnection;
import lombok.javac.CommentInfo.StartConnection;
-import com.sun.tools.javac.parser.Scanner;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
-
-public class CommentCollectingScanner extends Scanner {
+public class CommentCollectingScanner extends DocCommentScanner {
private final ListBuffer<CommentInfo> comments = ListBuffer.lb();
private int endComment = 0;
@@ -56,6 +54,7 @@ public class CommentCollectingScanner extends Scanner {
CommentInfo comment = new CommentInfo(prevEndPos, pos, endPos, content, start, end);
comments.append(comment);
+ super.processComment(style);
}
private EndConnection determineEndConnection(int pos) {
diff --git a/src/utils/lombok/javac/java6/CommentCollectingScannerFactory.java b/src/utils/lombok/javac/java6/CommentCollectingScannerFactory.java
index 30acbd5a..f3d6bd72 100644
--- a/src/utils/lombok/javac/java6/CommentCollectingScannerFactory.java
+++ b/src/utils/lombok/javac/java6/CommentCollectingScannerFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Project Lombok Authors.
+ * Copyright (C) 2011-2013 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
@@ -26,19 +26,44 @@ import java.nio.CharBuffer;
import com.sun.tools.javac.parser.Scanner;
import com.sun.tools.javac.util.Context;
-public class CommentCollectingScannerFactory extends Scanner.Factory {
+public class CommentCollectingScannerFactory extends DocCommentScanner.Factory {
+ @SuppressWarnings("all")
public static void preRegister(final Context context) {
if (context.get(scannerFactoryKey) == null) {
- context.put(scannerFactoryKey, new Context.Factory<Scanner.Factory>() {
- public CommentCollectingScanner.Factory make() {
+ // Careful! There is voodoo magic here!
+ //
+ // Context.Factory is parameterized. make() is for javac6 and below; make(Context) is for javac7 and up.
+ // this anonymous inner class definition is intentionally 'raw' - the return type of both 'make' methods is 'T',
+ // which means the compiler will only generate the correct "real" override method (with returntype Object, which is
+ // the lower bound for T, as a synthetic accessor for the make with returntype ScannerFactory) for that make method which
+ // is actually on the classpath (either make() for javac6-, or make(Context) for javac7+).
+ //
+ // We normally solve this issue via src/stubs, with BOTH make methods listed, but for some reason the presence of a stubbed out
+ // Context (or even a complete copy, it doesn't matter) results in a really strange eclipse bug, where any mention of any kind
+ // of com.sun.tools.javac.tree.TreeMaker in a source file disables ALL usage of 'go to declaration' and auto-complete in the entire
+ // source file.
+ //
+ // Thus, in short:
+ // * Do NOT parameterize the anonymous inner class literal.
+ // * Leave the return types as 'j.l.Object'.
+ // * Leave both make methods intact; deleting one has no effect on javac6- / javac7+, but breaks the other. Hard to test for.
+ // * Do not stub com.sun.tools.javac.util.Context or any of its inner types, like Factory.
+ @SuppressWarnings("all")
+ class MyFactory implements Context.Factory {
+ // This overrides the javac6- version of make.
+ public Object make() {
return new CommentCollectingScannerFactory(context);
}
- public CommentCollectingScanner.Factory make(Context c) {
+ // This overrides the javac7+ version of make.
+ public Object make(Context c) {
return new CommentCollectingScannerFactory(c);
}
- });
+ }
+
+ @SuppressWarnings("unchecked") Context.Factory<Scanner.Factory> factory = new MyFactory();
+ context.put(scannerFactoryKey, factory);
}
}
diff --git a/src/utils/lombok/javac/java6/DocCommentScanner.java b/src/utils/lombok/javac/java6/DocCommentScanner.java
new file mode 100644
index 00000000..ff3eadd4
--- /dev/null
+++ b/src/utils/lombok/javac/java6/DocCommentScanner.java
@@ -0,0 +1,461 @@
+package lombok.javac.java6;
+
+/*
+ * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import static com.sun.tools.javac.util.LayoutCharacters.*;
+
+import java.nio.CharBuffer;
+
+import com.sun.tools.javac.parser.Scanner;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Position;
+
+/** An extension to the base lexical analyzer that captures
+ * and processes the contents of doc comments. It does so by
+ * translating Unicode escape sequences and by stripping the
+ * leading whitespace and starts from each line of the comment.
+ *
+ * <p><b>This is NOT part of any API supported by Sun Microsystems. If
+ * you write code that depends on this, you do so at your own risk.
+ * This code and its internal interfaces are subject to change or
+ * deletion without notice.</b>
+ */
+public class DocCommentScanner extends Scanner {
+
+ /** A factory for creating scanners. */
+ public static class Factory extends Scanner.Factory {
+
+ @SuppressWarnings({"unchecked", "all"})
+ public static void preRegister(final Context context) {
+ context.put(scannerFactoryKey, new Context.Factory() {
+ public Object make() {
+ return new Factory(context);
+ }
+
+ public Object make(Context c) {
+ return new Factory(c);
+ }
+ });
+ }
+
+ /** Create a new scanner factory. */
+ protected Factory(Context context) {
+ super(context);
+ }
+
+ @Override
+ public Scanner newScanner(CharSequence input) {
+ if (input instanceof CharBuffer) {
+ return new DocCommentScanner(this, (CharBuffer)input);
+ } else {
+ char[] array = input.toString().toCharArray();
+ return newScanner(array, array.length);
+ }
+ }
+
+ @Override
+ public Scanner newScanner(char[] input, int inputLength) {
+ return new DocCommentScanner(this, input, inputLength);
+ }
+ }
+
+
+ /** Create a scanner from the input buffer. buffer must implement
+ * array() and compact(), and remaining() must be less than limit().
+ */
+ protected DocCommentScanner(Factory fac, CharBuffer buffer) {
+ super(fac, buffer);
+ }
+
+ /** Create a scanner from the input array. The array must have at
+ * least a single character of extra space.
+ */
+ protected DocCommentScanner(Factory fac, char[] input, int inputLength) {
+ super(fac, input, inputLength);
+ }
+
+ /** Starting position of the comment in original source
+ */
+ private int pos;
+
+ /** The comment input buffer, index of next chacter to be read,
+ * index of one past last character in buffer.
+ */
+ private char[] buf;
+ private int bp;
+ private int buflen;
+
+ /** The current character.
+ */
+ private char ch;
+
+ /** The column number position of the current character.
+ */
+ private int col;
+
+ /** The buffer index of the last converted Unicode character
+ */
+ private int unicodeConversionBp = 0;
+
+ /**
+ * Buffer for doc comment.
+ */
+ private char[] docCommentBuffer = new char[1024];
+
+ /**
+ * Number of characters in doc comment buffer.
+ */
+ private int docCommentCount;
+
+ /**
+ * Translated and stripped contents of doc comment
+ */
+ private String docComment = null;
+
+
+ /** Unconditionally expand the comment buffer.
+ */
+ private void expandCommentBuffer() {
+ char[] newBuffer = new char[docCommentBuffer.length * 2];
+ System.arraycopy(docCommentBuffer, 0, newBuffer,
+ 0, docCommentBuffer.length);
+ docCommentBuffer = newBuffer;
+ }
+
+ /** Convert an ASCII digit from its base (8, 10, or 16)
+ * to its value.
+ */
+ private int digit(int base) {
+ char c = ch;
+ int result = Character.digit(c, base);
+ if (result >= 0 && c > 0x7f) {
+ ch = "0123456789abcdef".charAt(result);
+ }
+ return result;
+ }
+
+ /** Convert Unicode escape; bp points to initial '\' character
+ * (Spec 3.3).
+ */
+ private void convertUnicode() {
+ if (ch == '\\' && unicodeConversionBp != bp) {
+ bp++; ch = buf[bp]; col++;
+ if (ch == 'u') {
+ do {
+ bp++; ch = buf[bp]; col++;
+ } while (ch == 'u');
+ int limit = bp + 3;
+ if (limit < buflen) {
+ int d = digit(16);
+ int code = d;
+ while (bp < limit && d >= 0) {
+ bp++; ch = buf[bp]; col++;
+ d = digit(16);
+ code = (code << 4) + d;
+ }
+ if (d >= 0) {
+ ch = (char)code;
+ unicodeConversionBp = bp;
+ return;
+ }
+ }
+ // "illegal.Unicode.esc", reported by base scanner
+ } else {
+ bp--;
+ ch = '\\';
+ col--;
+ }
+ }
+ }
+
+
+ /** Read next character.
+ */
+ private void scanChar() {
+ bp++;
+ ch = buf[bp];
+ switch (ch) {
+ case '\r': // return
+ col = 0;
+ break;
+ case '\n': // newline
+ if (bp == 0 || buf[bp-1] != '\r') {
+ col = 0;
+ }
+ break;
+ case '\t': // tab
+ col = (col / TabInc * TabInc) + TabInc;
+ break;
+ case '\\': // possible Unicode
+ col++;
+ convertUnicode();
+ break;
+ default:
+ col++;
+ break;
+ }
+ }
+
+ /**
+ * Read next character in doc comment, skipping over double '\' characters.
+ * If a double '\' is skipped, put in the buffer and update buffer count.
+ */
+ private void scanDocCommentChar() {
+ scanChar();
+ if (ch == '\\') {
+ if (buf[bp+1] == '\\' && unicodeConversionBp != bp) {
+ if (docCommentCount == docCommentBuffer.length)
+ expandCommentBuffer();
+ docCommentBuffer[docCommentCount++] = ch;
+ bp++; col++;
+ } else {
+ convertUnicode();
+ }
+ }
+ }
+
+ /* Reset doc comment before reading each new token
+ */
+ public void nextToken() {
+ docComment = null;
+ super.nextToken();
+ }
+
+ /**
+ * Returns the documentation string of the current token.
+ */
+ public String docComment() {
+ return docComment;
+ }
+
+ /**
+ * Process a doc comment and make the string content available.
+ * Strips leading whitespace and stars.
+ */
+ @SuppressWarnings("fallthrough")
+ protected void processComment(CommentStyle style) {
+ if (style != CommentStyle.JAVADOC) {
+ return;
+ }
+
+ pos = pos();
+ buf = getRawCharacters(pos, endPos());
+ buflen = buf.length;
+ bp = 0;
+ col = 0;
+
+ docCommentCount = 0;
+
+ boolean firstLine = true;
+
+ // Skip over first slash
+ scanDocCommentChar();
+ // Skip over first star
+ scanDocCommentChar();
+
+ // consume any number of stars
+ while (bp < buflen && ch == '*') {
+ scanDocCommentChar();
+ }
+ // is the comment in the form /**/, /***/, /****/, etc. ?
+ if (bp < buflen && ch == '/') {
+ docComment = "";
+ return;
+ }
+
+ // skip a newline on the first line of the comment.
+ if (bp < buflen) {
+ if (ch == LF) {
+ scanDocCommentChar();
+ firstLine = false;
+ } else if (ch == CR) {
+ scanDocCommentChar();
+ if (ch == LF) {
+ scanDocCommentChar();
+ firstLine = false;
+ }
+ }
+ }
+
+ outerLoop:
+
+ // The outerLoop processes the doc comment, looping once
+ // for each line. For each line, it first strips off
+ // whitespace, then it consumes any stars, then it
+ // puts the rest of the line into our buffer.
+ while (bp < buflen) {
+
+ // The wsLoop consumes whitespace from the beginning
+ // of each line.
+ wsLoop:
+
+ while (bp < buflen) {
+ switch(ch) {
+ case ' ':
+ scanDocCommentChar();
+ break;
+ case '\t':
+ col = ((col - 1) / TabInc * TabInc) + TabInc;
+ scanDocCommentChar();
+ break;
+ case FF:
+ col = 0;
+ scanDocCommentChar();
+ break;
+// Treat newline at beginning of line (blank line, no star)
+// as comment text. Old Javadoc compatibility requires this.
+/*---------------------------------*
+ case CR: // (Spec 3.4)
+ scanDocCommentChar();
+ if (ch == LF) {
+ col = 0;
+ scanDocCommentChar();
+ }
+ break;
+ case LF: // (Spec 3.4)
+ scanDocCommentChar();
+ break;
+*---------------------------------*/
+ default:
+ // we've seen something that isn't whitespace;
+ // jump out.
+ break wsLoop;
+ }
+ }
+
+ // Are there stars here? If so, consume them all
+ // and check for the end of comment.
+ if (ch == '*') {
+ // skip all of the stars
+ do {
+ scanDocCommentChar();
+ } while (ch == '*');
+
+ // check for the closing slash.
+ if (ch == '/') {
+ // We're done with the doc comment
+ // scanChar() and breakout.
+ break outerLoop;
+ }
+ } else if (! firstLine) {
+ //The current line does not begin with a '*' so we will indent it.
+ for (int i = 1; i < col; i++) {
+ if (docCommentCount == docCommentBuffer.length)
+ expandCommentBuffer();
+ docCommentBuffer[docCommentCount++] = ' ';
+ }
+ }
+
+ // The textLoop processes the rest of the characters
+ // on the line, adding them to our buffer.
+ textLoop:
+ while (bp < buflen) {
+ switch (ch) {
+ case '*':
+ // Is this just a star? Or is this the
+ // end of a comment?
+ scanDocCommentChar();
+ if (ch == '/') {
+ // This is the end of the comment,
+ // set ch and return our buffer.
+ break outerLoop;
+ }
+ // This is just an ordinary star. Add it to
+ // the buffer.
+ if (docCommentCount == docCommentBuffer.length)
+ expandCommentBuffer();
+ docCommentBuffer[docCommentCount++] = '*';
+ break;
+ case ' ':
+ case '\t':
+ if (docCommentCount == docCommentBuffer.length)
+ expandCommentBuffer();
+ docCommentBuffer[docCommentCount++] = ch;
+ scanDocCommentChar();
+ break;
+ case FF:
+ scanDocCommentChar();
+ break textLoop; // treat as end of line
+ case CR: // (Spec 3.4)
+ scanDocCommentChar();
+ if (ch != LF) {
+ // Canonicalize CR-only line terminator to LF
+ if (docCommentCount == docCommentBuffer.length)
+ expandCommentBuffer();
+ docCommentBuffer[docCommentCount++] = (char)LF;
+ break textLoop;
+ }
+ /* fall through to LF case */
+ case LF: // (Spec 3.4)
+ // We've seen a newline. Add it to our
+ // buffer and break out of this loop,
+ // starting fresh on a new line.
+ if (docCommentCount == docCommentBuffer.length)
+ expandCommentBuffer();
+ docCommentBuffer[docCommentCount++] = ch;
+ scanDocCommentChar();
+ break textLoop;
+ default:
+ // Add the character to our buffer.
+ if (docCommentCount == docCommentBuffer.length)
+ expandCommentBuffer();
+ docCommentBuffer[docCommentCount++] = ch;
+ scanDocCommentChar();
+ }
+ } // end textLoop
+ firstLine = false;
+ } // end outerLoop
+
+ if (docCommentCount > 0) {
+ int i = docCommentCount - 1;
+ trailLoop:
+ while (i > -1) {
+ switch (docCommentBuffer[i]) {
+ case '*':
+ i--;
+ break;
+ default:
+ break trailLoop;
+ }
+ }
+ docCommentCount = i + 1;
+
+ // Store the text of the doc comment
+ docComment = new String(docCommentBuffer, 0 , docCommentCount);
+ } else {
+ docComment = "";
+ }
+ }
+
+ /** Build a map for translating between line numbers and
+ * positions in the input.
+ *
+ * @return a LineMap */
+ public Position.LineMap getLineMap() {
+ char[] buf = getRawCharacters();
+ return Position.makeLineMap(buf, buf.length, true);
+ }
+}
diff --git a/src/utils/lombok/javac/java7/CommentCollectingParserFactory.java b/src/utils/lombok/javac/java7/CommentCollectingParserFactory.java
index e361a5bd..e9575c14 100644
--- a/src/utils/lombok/javac/java7/CommentCollectingParserFactory.java
+++ b/src/utils/lombok/javac/java7/CommentCollectingParserFactory.java
@@ -30,8 +30,8 @@ public class CommentCollectingParserFactory extends ParserFactory {
public Parser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap) {
ScannerFactory scannerFactory = ScannerFactory.instance(context);
- Lexer lexer = scannerFactory.newScanner(input, keepDocComments);
- Object x = new CommentCollectingParser(this, lexer, keepDocComments, keepLineMap, commentsMap);
+ Lexer lexer = scannerFactory.newScanner(input, true);
+ Object x = new CommentCollectingParser(this, lexer, true, keepLineMap, commentsMap);
return (Parser) x;
// CCP is based on a stub which extends nothing, but at runtime the stub is replaced with either
//javac6's EndPosParser which extends Parser, or javac7's EndPosParser which implements Parser.
diff --git a/src/utils/lombok/javac/java7/CommentCollectingScanner.java b/src/utils/lombok/javac/java7/CommentCollectingScanner.java
index e2d040f2..6ebd3ac1 100644
--- a/src/utils/lombok/javac/java7/CommentCollectingScanner.java
+++ b/src/utils/lombok/javac/java7/CommentCollectingScanner.java
@@ -26,12 +26,12 @@ import java.nio.CharBuffer;
import lombok.javac.CommentInfo;
import lombok.javac.CommentInfo.EndConnection;
import lombok.javac.CommentInfo.StartConnection;
+
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.parser.DocCommentScanner;
-import com.sun.tools.javac.parser.Scanner;
-
-public class CommentCollectingScanner extends Scanner {
+public class CommentCollectingScanner extends DocCommentScanner {
private final ListBuffer<CommentInfo> comments = ListBuffer.lb();
private int endComment = 0;
@@ -55,6 +55,7 @@ public class CommentCollectingScanner extends Scanner {
CommentInfo comment = new CommentInfo(prevEndPos, pos, endPos, content, start, end);
comments.append(comment);
+ super.processComment(style);
}
private EndConnection determineEndConnection(int pos) {
diff --git a/src/utils/lombok/javac/java7/CommentCollectingScannerFactory.java b/src/utils/lombok/javac/java7/CommentCollectingScannerFactory.java
index 9a29528e..626d3d63 100644
--- a/src/utils/lombok/javac/java7/CommentCollectingScannerFactory.java
+++ b/src/utils/lombok/javac/java7/CommentCollectingScannerFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Project Lombok Authors.
+ * Copyright (C) 2011-2013 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
@@ -29,17 +29,41 @@ import com.sun.tools.javac.util.Context;
public class CommentCollectingScannerFactory extends ScannerFactory {
+ @SuppressWarnings("all")
public static void preRegister(final Context context) {
if (context.get(scannerFactoryKey) == null) {
- context.put(scannerFactoryKey, new Context.Factory<ScannerFactory>() {
- public ScannerFactory make() {
+ // Careful! There is voodoo magic here!
+ //
+ // Context.Factory is parameterized. make() is for javac6 and below; make(Context) is for javac7 and up.
+ // this anonymous inner class definition is intentionally 'raw' - the return type of both 'make' methods is 'T',
+ // which means the compiler will only generate the correct "real" override method (with returntype Object, which is
+ // the lower bound for T, as a synthetic accessor for the make with returntype ScannerFactory) for that make method which
+ // is actually on the classpath (either make() for javac6-, or make(Context) for javac7+).
+ //
+ // We normally solve this issue via src/stubs, with BOTH make methods listed, but for some reason the presence of a stubbed out
+ // Context (or even a complete copy, it doesn't matter) results in a really strange eclipse bug, where any mention of any kind
+ // of com.sun.tools.javac.tree.TreeMaker in a source file disables ALL usage of 'go to declaration' and auto-complete in the entire
+ // source file.
+ //
+ // Thus, in short:
+ // * Do NOT parameterize the anonymous inner class literal.
+ // * Leave the return types as 'j.l.Object'.
+ // * Leave both make methods intact; deleting one has no effect on javac6- / javac7+, but breaks the other. Hard to test for.
+ // * Do not stub com.sun.tools.javac.util.Context or any of its inner types, like Factory.
+ @SuppressWarnings("all")
+ class MyFactory implements Context.Factory {
+ // This overrides the javac6- version of make.
+ public Object make() {
return new CommentCollectingScannerFactory(context);
}
- @Override public ScannerFactory make(Context c) {
+ // This overrides the javac7+ version.
+ public Object make(Context c) {
return new CommentCollectingScannerFactory(c);
}
- });
+ }
+ @SuppressWarnings("unchecked") Context.Factory<ScannerFactory> factory = new MyFactory();
+ context.put(scannerFactoryKey, factory);
}
}