mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-14 04:16:19 +00:00
Eclipse Builder implementation finished. Tests need fleshing out though.
This commit is contained in:
1 parent
2d76b1d22d
commit
648c3eeee6
9 files changed
+391
-59
No files matched your search
@@ -359,6 +359,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)}.
|
||||
@@ -1208,15 +1222,15 @@ public class EclipseHandlerUtil {
|
||||
* Inserts a field into an existing type. The type must represent a {@code TypeDeclaration}.
|
||||
* The field carries the @{@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) {
|
||||
@@ -1243,7 +1257,7 @@ public class EclipseHandlerUtil {
|
||||
}
|
||||
}
|
||||
|
||||
type.add(field, Kind.FIELD);
|
||||
return type.add(field, Kind.FIELD);
|
||||
}
|
||||
|
||||
private static boolean isEnumConstant(final FieldDeclaration field) {
|
||||
@@ -1253,7 +1267,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();
|
||||
|
||||
@@ -1286,7 +1300,7 @@ public class EclipseHandlerUtil {
|
||||
parent.methods = newArray;
|
||||
}
|
||||
|
||||
type.add(method, Kind.METHOD);
|
||||
return type.add(method, Kind.METHOD);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1295,7 +1309,7 @@ public class EclipseHandlerUtil {
|
||||
* @param typeNode parent type to inject new type into
|
||||
* @param type New type (class, interface, etc) to inject.
|
||||
*/
|
||||
public static void injectType(final EclipseNode typeNode, final TypeDeclaration type) {
|
||||
public static EclipseNode injectType(final EclipseNode typeNode, final TypeDeclaration type) {
|
||||
type.annotations = createSuppressWarningsAll(type, type.annotations);
|
||||
TypeDeclaration parent = (TypeDeclaration) typeNode.get();
|
||||
|
||||
@@ -1307,7 +1321,8 @@ public class EclipseHandlerUtil {
|
||||
newArray[parent.memberTypes.length] = type;
|
||||
parent.memberTypes = newArray;
|
||||
}
|
||||
typeNode.add(type, Kind.TYPE);
|
||||
|
||||
return typeNode.add(type, Kind.TYPE);
|
||||
}
|
||||
|
||||
private static final char[] ALL = "all".toCharArray();
|
||||
|
||||
@@ -21,51 +21,344 @@
|
||||
*/
|
||||
package lombok.eclipse.handlers;
|
||||
|
||||
import static lombok.eclipse.Eclipse.*;
|
||||
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.ImmutableList;
|
||||
import lombok.core.JavaIdentifiers;
|
||||
import lombok.eclipse.Eclipse;
|
||||
import lombok.eclipse.EclipseAnnotationHandler;
|
||||
import lombok.eclipse.EclipseNode;
|
||||
import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists;
|
||||
import lombok.experimental.Builder;
|
||||
|
||||
@ProviderFor(EclipseAnnotationHandler.class)
|
||||
public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
@Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) {
|
||||
String builderMethodName = annotation.getInstance().builderMethodName();
|
||||
if (builderMethodName == null) builderMethodName = "builder";
|
||||
if (builderMethodName.length() == 0) {
|
||||
annotationNode.addError("builderMethodName cannot be the empty string.");
|
||||
return;
|
||||
}
|
||||
long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
|
||||
|
||||
if (!JavaIdentifiers.isValidJavaIdentifier(builderMethodName)) {
|
||||
annotationNode.addError("builderMethodName must be a valid java method name.");
|
||||
return;
|
||||
}
|
||||
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 = "";
|
||||
|
||||
checkName("builderMethodName", builderMethodName, annotationNode);
|
||||
checkName("buildMethodName", buildMethodName, annotationNode);
|
||||
if (!builderClassName.isEmpty()) checkName("builderClassName", builderClassName, annotationNode);
|
||||
|
||||
EclipseNode parent = annotationNode.up();
|
||||
|
||||
if (parent.get() instanceof ConstructorDeclaration) {
|
||||
|
||||
}
|
||||
List<TypeReference> typesOfParameters = new ArrayList<TypeReference>();
|
||||
List<char[]> namesOfParameters = new ArrayList<char[]>();
|
||||
TypeReference returnType;
|
||||
TypeParameter[] typeParams;
|
||||
TypeReference[] thrownExceptions;
|
||||
char[] nameOfStaticBuilderMethod;
|
||||
EclipseNode tdParent;
|
||||
|
||||
if (parent.get() instanceof MethodDeclaration) {
|
||||
|
||||
}
|
||||
AbstractMethodDeclaration fillParametersFrom = null;
|
||||
|
||||
if (parent.get() instanceof TypeDeclaration) {
|
||||
// TODO: How do we ensure this one will 'win' over the implicit constructors generated by @Data and @Value.
|
||||
new HandleConstructor().generateAllArgsConstructor(parent, AccessLevel.PRIVATE, null, true, Collections.<Annotation>emptyList(), ast);
|
||||
tdParent = parent;
|
||||
TypeDeclaration td = (TypeDeclaration) tdParent.get();
|
||||
new HandleConstructor().generateAllArgsConstructor(parent, AccessLevel.PRIVATE, null, SkipIfConstructorExists.I_AM_BUILDER, Collections.<Annotation>emptyList(), ast);
|
||||
|
||||
for (EclipseNode fieldNode : HandleConstructor.findAllFields(parent)) {
|
||||
FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
|
||||
namesOfParameters.add(fd.name);
|
||||
typesOfParameters.add(fd.type);
|
||||
}
|
||||
|
||||
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);
|
||||
List<EclipseNode> fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
|
||||
List<AbstractMethodDeclaration> newMethods = new ArrayList<AbstractMethodDeclaration>();
|
||||
for (EclipseNode fieldNode : fieldNodes) {
|
||||
MethodDeclaration newMethod = makeSetterMethodForBuilder(builderType, fieldNode, ast);
|
||||
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(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
|
||||
MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams, ast);
|
||||
if (md != null) injectMethod(tdParent, md);
|
||||
}
|
||||
|
||||
|
||||
// create builder method in parent.
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
return HandleSetter.createSetter(td, fieldNode, fieldNode.getName(), true, 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);
|
||||
}
|
||||
|
||||
private static void checkName(String nameSpec, String identifier, EclipseNode annotationNode) {
|
||||
if (identifier.isEmpty()) {
|
||||
annotationNode.addError(nameSpec + " cannot be the empty string.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!JavaIdentifiers.isValidJavaIdentifier(identifier)) {
|
||||
annotationNode.addError(nameSpec + " must be a valid java identifier.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -30,6 +30,7 @@ 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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -109,4 +109,13 @@ import java.lang.annotation.Target;
|
||||
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 "";
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import java.util.List;
|
||||
import lombok.experimental.Builder;
|
||||
|
||||
class BuilderComplex {
|
||||
@Builder
|
||||
private static <T extends Number> void testVoidWithGenerics(T number, int arg2, String arg3, BuilderComplex selfRef) {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import java.util.List;
|
||||
|
||||
@lombok.experimental.Builder
|
||||
class BuilderSimple<T> {
|
||||
private final int noshow = 0;
|
||||
private final int yes;
|
||||
private List<T> also;
|
||||
private int $butNotMe;
|
||||
}
|
||||
Reference in new issue
Block a user