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}.
+ *
+ * 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.
+ *
+ * The effect of {@code @Builder} is that an inner class is generated named TBuilder,
+ * with a private constructor. Instances of TBuilder are made with the static
+ * method named {@code builder()} which is also generated for you in the class itself (not in the builder class).
+ *
+ * The TBuilder 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 build() 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.
+ *
+ * Complete documentation is found at the project lombok features page for @Builder.
+ *
+ *
+ * Before:
+ *
+ *
+ * @Builder
+ * class Example {
+ * private int foo;
+ * private final String bar;
+ * }
+ *
+ *
+ * After:
+ *
+ *
+ * class Example<T> {
+ * private T foo;
+ * private final String bar;
+ *
+ * private Example(T foo, String bar) {
+ * this.foo = foo;
+ * this.bar = bar;
+ * }
+ *
+ * public static <T> ExampleBuilder<T> builder() {
+ * return new ExampleBuilder<T>();
+ * }
+ *
+ * public static class ExampleBuilder<T> {
+ * 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;
+ * }
+ *
+ * @java.lang.Override public String toString() {
+ * return "ExampleBuilder(foo = " + foo + ", bar = " + bar + ")";
+ * }
+ *
+ * public Example build() {
+ * return new Example(foo, bar);
+ * }
+ * }
+ * }
+ *
+ */
+@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}.
+ *
+ * Default: true
+ */
+ 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.
+ *
+ * Default: true
+ */
+ 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 @Log
* @see lombok.extern.log4j.Log4j @Log4j
+ * @see lombok.extern.log4j.Log4j2 @Log4j2
* @see lombok.extern.slf4j.Slf4j @Slf4j
* @see lombok.extern.slf4j.XSlf4j @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 @CommonsLog
* @see lombok.extern.log4j.Log4j @Log4j
+ * @see lombok.extern.log4j.Log4j2 @Log4j2
* @see lombok.extern.slf4j.Slf4j @Slf4j
* @see lombok.extern.slf4j.XSlf4j @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 @Log4j2
* @see lombok.extern.apachecommons.CommonsLog @CommonsLog
* @see lombok.extern.java.Log @Log
* @see lombok.extern.slf4j.Slf4j @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.
+ *
+ * Complete documentation is found at the project lombok features page for lombok log annotations.
+ *
+ * Example:
+ *
+ * @Log4j2
+ * public class LogExample {
+ * }
+ *
+ *
+ * will generate:
+ *
+ *
+ * public class LogExample {
+ * private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.Logger.getLogger(LogExample.class);
+ * }
+ *
+ *
+ * This annotation is valid for classes and enumerations.
+ *
+ * @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 @Log4j
+ * @see lombok.extern.apachecommons.CommonsLog @CommonsLog
+ * @see lombok.extern.java.Log @Log
+ * @see lombok.extern.slf4j.Slf4j @Slf4j
+ * @see lombok.extern.slf4j.XSlf4j @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 @CommonsLog
* @see lombok.extern.java.Log @Log
* @see lombok.extern.log4j.Log4j @Log4j
+ * @see lombok.extern.log4j.Log4j2 @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 @CommonsLog
* @see lombok.extern.java.Log @Log
* @see lombok.extern.log4j.Log4j @Log4j
+ * @see lombok.extern.log4j.Log4j2 @Log4j2
* @see lombok.extern.slf4j.Slf4j @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 {
@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 {
* @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 {
return (cu.pid instanceof JCFieldAccess || cu.pid instanceof JCIdent) ? cu.pid.toString() : null;
}
- private static Collection imports(JCCompilationUnit cu) {
- List imports = new ArrayList();
- 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 {
}
void traverseChildren(JavacASTVisitor visitor, JavacNode node) {
- for (JavacNode child : new ArrayList(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 {
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 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) rv;
+ return Collections.emptyList();
+ }
+
+ private JavacNode buildTry(JCTry tryNode) {
+ if (setAndGetAsHandled(tryNode)) return null;
+ List childNodes = new ArrayList();
+ 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 childNodes = new ArrayList();
@@ -264,6 +307,7 @@ public class JavacAST extends AST {
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 {
}
private JavacNode drill(JCTree statement) {
- List childNodes = new ArrayList();
- for (FieldAccess fa : fieldsOf(statement.getClass())) childNodes.addAll(buildWithField(JavacNode.class, statement, fa));
- return putInMap(new JavacNode(this, statement, childNodes, Kind.STATEMENT));
+ try {
+ List childNodes = new ArrayList();
+ 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}
+ * {@code public class HandleGetter extends JavacAnnotationHandler}
*
* 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 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 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 applyNameToStarImports(String startsWith, String name) {
+ ArrayList out = new ArrayList();
+
+ 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 ifaces = ((ClassType)type).interfaces_field;
- Type supertype = ((ClassType)type).supertype_field;
+ List 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 {
+ @Override public void handle(AnnotationValues 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 typesOfParameters = new ArrayList();
+ java.util.List namesOfParameters = new ArrayList();
+ JCExpression returnType;
+ List typeParams = List.nil();
+ List 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 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.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("")) {
+ 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 fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
+ java.util.List newMethods = new ArrayList();
+ 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.nil(), builderType, List.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 fieldNames, JavacNode type, List thrownExceptions) {
+ TreeMaker maker = type.getTreeMaker();
+
+ JCExpression call;
+ JCStatement statement;
+
+ ListBuffer args = ListBuffer.lb();
+ for (Name n : fieldNames) {
+ args.append(maker.Ident(n));
+ }
+
+ if (staticName == null) {
+ call = maker.NewClass(null, List.nil(), returnType, args.toList(), null);
+ statement = maker.Return(call);
+ } else {
+ ListBuffer 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.of(statement));
+
+ return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(name), returnType, List.nil(), List.nil(), thrownExceptions, body, null);
+ }
+
+ private JCMethodDecl generateBuilderMethod(String builderMethodName, String builderClassName, JavacNode type, List typeParams) {
+ TreeMaker maker = type.getTreeMaker();
+
+ ListBuffer typeArgs = ListBuffer.lb();
+ for (JCTypeParameter typeParam : typeParams) {
+ typeArgs.append(maker.Ident(typeParam.name));
+ }
+
+ JCExpression call = maker.NewClass(null, List.nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.nil(), null);
+ JCStatement statement = maker.Return(call);
+
+ JCBlock body = maker.Block(0, List.of(statement));
+ return maker.MethodDef(maker.Modifiers(Flags.STATIC | Flags.PUBLIC), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(maker, typeParams), List.nil(), List.nil(), body, null);
+ }
+
+ private java.util.List addFieldsToBuilder(JavacNode builderType, java.util.List namesOfParameters, java.util.List typesOfParameters, JCTree source) {
+ int len = namesOfParameters.size();
+ java.util.List existing = new ArrayList();
+ for (JavacNode child : builderType.down()) {
+ if (child.getKind() == Kind.FIELD) existing.add(child);
+ }
+
+ java.util.Listout = new ArrayList();
+
+ 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.