mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-19 22:15:19 +00:00
@Singleton for j.u.Sets works in javac.
This commit is contained in:
1 parent
f180ab3c17
commit
4618097d98
9 files changed
+763
-92
No files matched your search
@@ -19,7 +19,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package lombok.javac.apt;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2014 The Project Lombok Authors.
|
||||
* Copyright (C) 2013-2015 The Project Lombok Authors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,8 +21,8 @@
|
||||
*/
|
||||
package lombok.javac.handlers;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.mangosdk.spi.ProviderFor;
|
||||
|
||||
@@ -34,6 +34,8 @@ 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.JCIf;
|
||||
import com.sun.tools.javac.tree.JCTree.JCLiteral;
|
||||
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.JCModifiers;
|
||||
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
|
||||
@@ -48,14 +50,18 @@ import com.sun.tools.javac.util.Name;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.ConfigurationKeys;
|
||||
import lombok.Singular;
|
||||
import lombok.core.AST.Kind;
|
||||
import lombok.core.AnnotationValues;
|
||||
import lombok.core.HandlerPriority;
|
||||
import lombok.experimental.NonFinal;
|
||||
import lombok.javac.Javac;
|
||||
import lombok.javac.JavacAnnotationHandler;
|
||||
import lombok.javac.JavacNode;
|
||||
import lombok.javac.JavacTreeMaker;
|
||||
import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
|
||||
import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
|
||||
import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
|
||||
import static lombok.core.handlers.HandlerUtil.*;
|
||||
import static lombok.javac.handlers.JavacHandlerUtil.*;
|
||||
import static lombok.javac.Javac.*;
|
||||
@@ -66,9 +72,18 @@ import static lombok.javac.JavacTreeMaker.TypeTag.*;
|
||||
public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
private static final boolean toBoolean(Object expr, boolean defaultValue) {
|
||||
if (expr == null) return defaultValue;
|
||||
if (expr instanceof JCLiteral) return ((Integer) ((JCLiteral) expr).value) != 0;
|
||||
return ((Boolean) expr).booleanValue();
|
||||
}
|
||||
|
||||
private static class BuilderFieldData {
|
||||
JCExpression type;
|
||||
Name name;
|
||||
SingularData singularData;
|
||||
|
||||
JavacNode mainCreatedField;
|
||||
}
|
||||
|
||||
@Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) {
|
||||
handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder");
|
||||
|
||||
@@ -92,20 +107,21 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
if (!checkName("builderClassName", builderClassName, annotationNode)) return;
|
||||
}
|
||||
|
||||
deleteAnnotationIfNeccessary(annotationNode, Builder.class);
|
||||
deleteImportFromCompilationUnit(annotationNode, "lombok.experimental.Builder");
|
||||
@SuppressWarnings("deprecation")
|
||||
Class<? extends Annotation> oldExperimentalBuilder = lombok.experimental.Builder.class;
|
||||
deleteAnnotationIfNeccessary(annotationNode, Builder.class, oldExperimentalBuilder);
|
||||
|
||||
JavacNode parent = annotationNode.up();
|
||||
|
||||
java.util.List<JCExpression> typesOfParameters = new ArrayList<JCExpression>();
|
||||
java.util.List<Name> namesOfParameters = new ArrayList<Name>();
|
||||
java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
|
||||
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;
|
||||
JavacNode fillParametersFrom = parent.get() instanceof JCMethodDecl ? parent : null;
|
||||
boolean addCleaning = false;
|
||||
|
||||
if (parent.get() instanceof JCClassDecl) {
|
||||
tdParent = parent;
|
||||
@@ -119,12 +135,14 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
// 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(removePrefixFromField(fieldNode));
|
||||
typesOfParameters.add(fd.vartype);
|
||||
BuilderFieldData bfd = new BuilderFieldData();
|
||||
bfd.name = removePrefixFromField(fieldNode);
|
||||
bfd.type = fd.vartype;
|
||||
bfd.singularData = getSingularData(fieldNode);
|
||||
builderFields.add(bfd);
|
||||
allFields.append(fieldNode);
|
||||
}
|
||||
|
||||
|
||||
new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode);
|
||||
|
||||
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
|
||||
@@ -133,7 +151,8 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
nameOfStaticBuilderMethod = null;
|
||||
if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
|
||||
} else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("<init>")) {
|
||||
if (!fillParametersFrom.typarams.isEmpty()) {
|
||||
JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
|
||||
if (!jmd.typarams.isEmpty()) {
|
||||
annotationNode.addError("@Builder is not supported on constructors with constructor type parameters.");
|
||||
return;
|
||||
}
|
||||
@@ -141,20 +160,21 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
JCClassDecl td = (JCClassDecl) tdParent.get();
|
||||
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
|
||||
typeParams = td.typarams;
|
||||
thrownExceptions = fillParametersFrom.thrown;
|
||||
thrownExceptions = jmd.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) {
|
||||
JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
|
||||
if ((jmd.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;
|
||||
returnType = jmd.restype;
|
||||
typeParams = jmd.typarams;
|
||||
thrownExceptions = jmd.thrown;
|
||||
nameOfStaticBuilderMethod = jmd.name;
|
||||
if (builderClassName.isEmpty()) {
|
||||
if (returnType instanceof JCTypeApply) {
|
||||
returnType = ((JCTypeApply) returnType).clazz;
|
||||
@@ -189,9 +209,14 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
}
|
||||
|
||||
if (fillParametersFrom != null) {
|
||||
for (JCVariableDecl param : fillParametersFrom.params) {
|
||||
namesOfParameters.add(param.name);
|
||||
typesOfParameters.add(param.vartype);
|
||||
for (JavacNode param : fillParametersFrom.down()) {
|
||||
if (param.getKind() != Kind.ARGUMENT) continue;
|
||||
BuilderFieldData bfd = new BuilderFieldData();
|
||||
JCVariableDecl raw = (JCVariableDecl) param.get();
|
||||
bfd.name = raw.name;
|
||||
bfd.type = raw.vartype;
|
||||
bfd.singularData = getSingularData(param);
|
||||
builderFields.add(bfd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,11 +226,21 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
} 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, annotationNode, fluent, chain);
|
||||
if (newMethod != null) newMethods.add(newMethod);
|
||||
|
||||
for (BuilderFieldData bfd : builderFields) {
|
||||
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
|
||||
if (bfd.singularData.getSingularizer().requiresCleaning()) {
|
||||
addCleaning = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generateBuilderFields(builderType, builderFields, ast);
|
||||
if (addCleaning) {
|
||||
JavacTreeMaker maker = builderType.getTreeMaker();
|
||||
JCVariableDecl uncleanField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName("$lombokUnclean"), maker.TypeIdent(CTC_BOOLEAN), null);
|
||||
injectField(builderType, uncleanField);
|
||||
}
|
||||
|
||||
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
|
||||
@@ -213,38 +248,94 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
if (cd != null) injectMethod(builderType, cd);
|
||||
}
|
||||
|
||||
for (JCMethodDecl newMethod : newMethods) injectMethod(builderType, newMethod);
|
||||
for (BuilderFieldData builderFieldData : builderFields) {
|
||||
makeSetterMethodForBuilder(builderType, builderFieldData, annotationNode, fluent, chain);
|
||||
}
|
||||
|
||||
if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
|
||||
JCMethodDecl md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, namesOfParameters, builderType, thrownExceptions);
|
||||
JCMethodDecl md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning);
|
||||
if (md != null) injectMethod(builderType, md);
|
||||
}
|
||||
|
||||
if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
|
||||
java.util.List<JavacNode> fieldNodes = new ArrayList<JavacNode>();
|
||||
for (BuilderFieldData bfd : builderFields) {
|
||||
JavacNode mcf = bfd.mainCreatedField;
|
||||
if (mcf != null) fieldNodes.add(mcf);
|
||||
}
|
||||
JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast);
|
||||
if (md != null) injectMethod(builderType, md);
|
||||
}
|
||||
|
||||
if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
|
||||
|
||||
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
|
||||
JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams);
|
||||
recursiveSetGeneratedBy(md, ast, annotationNode.getContext());
|
||||
if (md != null) injectMethod(tdParent, md);
|
||||
}
|
||||
|
||||
recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext());
|
||||
}
|
||||
|
||||
public JCMethodDecl generateBuildMethod(String name, Name staticName, JCExpression returnType, java.util.List<Name> fieldNames, JavacNode type, List<JCExpression> thrownExceptions) {
|
||||
private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) {
|
||||
JavacTreeMaker maker = type.getTreeMaker();
|
||||
ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
|
||||
|
||||
for (BuilderFieldData bfd : builderFields) {
|
||||
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
|
||||
bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, type, source, statements);
|
||||
}
|
||||
}
|
||||
|
||||
statements.append(maker.Exec(maker.Assign(maker.Ident(type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, false))));
|
||||
JCBlock body = maker.Block(0, statements.toList());
|
||||
return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName("$lombokClean"), maker.Type(Javac.createVoidType(maker, CTC_VOID)), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
|
||||
/*
|
||||
* 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(Javac.createVoidType(treeMaker, CTC_VOID));
|
||||
shouldReturnThis = false;
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
private JCMethodDecl generateBuildMethod(String name, Name staticName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning) {
|
||||
JavacTreeMaker maker = type.getTreeMaker();
|
||||
|
||||
JCExpression call;
|
||||
JCStatement statement;
|
||||
ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
|
||||
|
||||
if (addCleaning) {
|
||||
JCExpression notClean = maker.Unary(CTC_NOT, maker.Ident(type.toName("$lombokUnclean")));
|
||||
JCStatement invokeClean = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(type.toName("$lombokClean")), List.<JCExpression>nil()));
|
||||
JCIf ifUnclean = maker.If(notClean, invokeClean, null);
|
||||
statements.append(ifUnclean);
|
||||
}
|
||||
|
||||
for (BuilderFieldData bfd : builderFields) {
|
||||
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
|
||||
bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name);
|
||||
}
|
||||
}
|
||||
|
||||
ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
|
||||
for (Name n : fieldNames) {
|
||||
args.append(maker.Ident(n));
|
||||
for (BuilderFieldData bfd : builderFields) {
|
||||
args.append(maker.Ident(bfd.name));
|
||||
}
|
||||
|
||||
if (addCleaning) {
|
||||
statements.append(maker.Exec(maker.Assign(maker.Ident(type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, true))));
|
||||
}
|
||||
|
||||
if (staticName == null) {
|
||||
call = maker.NewClass(null, List.<JCExpression>nil(), returnType, args.toList(), null);
|
||||
statement = maker.Return(call);
|
||||
statements.append(maker.Return(call));
|
||||
} else {
|
||||
ListBuffer<JCExpression> typeParams = new ListBuffer<JCExpression>();
|
||||
for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) {
|
||||
@@ -254,13 +345,13 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
JCExpression fn = maker.Select(maker.Ident(((JCClassDecl) type.up().get()).name), staticName);
|
||||
call = maker.Apply(typeParams.toList(), fn, args.toList());
|
||||
if (returnType instanceof JCPrimitiveTypeTree && CTC_VOID.equals(typeTag(returnType))) {
|
||||
statement = maker.Exec(call);
|
||||
statements.append(maker.Exec(call));
|
||||
} else {
|
||||
statement = maker.Return(call);
|
||||
statements.append(maker.Return(call));
|
||||
}
|
||||
}
|
||||
|
||||
JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
|
||||
JCBlock body = maker.Block(0, statements.toList());
|
||||
|
||||
return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(name), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
|
||||
}
|
||||
@@ -280,50 +371,57 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
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);
|
||||
}
|
||||
|
||||
public java.util.List<JavacNode> addFieldsToBuilder(JavacNode builderType, java.util.List<Name> namesOfParameters, java.util.List<JCExpression> typesOfParameters, JCTree source) {
|
||||
int len = namesOfParameters.size();
|
||||
public void generateBuilderFields(JavacNode builderType, java.util.List<BuilderFieldData> builderFields, JCTree source) {
|
||||
int len = builderFields.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;
|
||||
BuilderFieldData bfd = builderFields.get(i);
|
||||
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
|
||||
bfd.mainCreatedField = bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType, source);
|
||||
} else {
|
||||
for (JavacNode exists : existing) {
|
||||
Name n = ((JCVariableDecl) exists.get()).name;
|
||||
if (n.equals(bfd.name)) {
|
||||
bfd.mainCreatedField = exists;
|
||||
continue top;
|
||||
}
|
||||
}
|
||||
JavacTreeMaker maker = builderType.getTreeMaker();
|
||||
JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
|
||||
JCVariableDecl newField = maker.VarDef(mods, bfd.name, cloneType(maker, bfd.type, source, builderType.getContext()), null);
|
||||
bfd.mainCreatedField = injectField(builderType, newField);
|
||||
}
|
||||
JavacTreeMaker maker = builderType.getTreeMaker();
|
||||
JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
|
||||
JCVariableDecl newField = maker.VarDef(mods, name, cloneType(maker, typesOfParameters.get(i), source, builderType.getContext()), null);
|
||||
out.add(injectField(builderType, newField));
|
||||
}
|
||||
|
||||
Collections.reverse(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
public void makeSetterMethodForBuilder(JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, boolean fluent, boolean chain) {
|
||||
if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) {
|
||||
makeSimpleSetterMethodForBuilder(builderType, fieldNode.mainCreatedField, source, fluent, chain);
|
||||
} else {
|
||||
fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, builderType, source.get(), fluent, chain);
|
||||
}
|
||||
}
|
||||
|
||||
public JCMethodDecl makeSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JavacNode source, boolean fluent, boolean chain) {
|
||||
private void makeSimpleSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JavacNode 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;
|
||||
if (existingName.equals(fieldName)) return;
|
||||
}
|
||||
|
||||
boolean isBoolean = isBoolean(fieldNode);
|
||||
String setterName = fluent ? fieldNode.getName() : toSetterName(builderType.getAst(), null, fieldNode.getName(), isBoolean);
|
||||
String setterName = fluent ? fieldNode.getName() : toSetterName(fieldNode.getAst(), null, fieldNode.getName(), isBoolean);
|
||||
|
||||
JavacTreeMaker maker = builderType.getTreeMaker();
|
||||
return HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
|
||||
JavacTreeMaker maker = fieldNode.getTreeMaker();
|
||||
JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
|
||||
injectMethod(builderType, newMethod);
|
||||
}
|
||||
|
||||
public JavacNode findInnerClass(JavacNode parent, String name) {
|
||||
@@ -341,4 +439,54 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(maker, typeParams), null, List.<JCExpression>nil(), List.<JCTree>nil());
|
||||
return injectType(tdParent, builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the explicitly requested singular annotation on this node (field
|
||||
* or parameter), or null if there's no {@code @Singular} annotation on it.
|
||||
*
|
||||
* @param node The node (field or method param) to inspect for its name and potential {@code @Singular} annotation.
|
||||
*/
|
||||
private SingularData getSingularData(JavacNode node) {
|
||||
for (JavacNode child : node.down()) {
|
||||
if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Singular.class, child)) {
|
||||
Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name;
|
||||
AnnotationValues<Singular> ann = createAnnotation(Singular.class, child);
|
||||
deleteAnnotationIfNeccessary(child, Singular.class);
|
||||
String explicitSingular = ann.getInstance().value();
|
||||
if (explicitSingular.isEmpty()) {
|
||||
explicitSingular = autoSingularize(node.getName());
|
||||
if (explicitSingular == null) {
|
||||
node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))");
|
||||
explicitSingular = pluralName.toString();
|
||||
}
|
||||
}
|
||||
Name singularName = node.toName(explicitSingular);
|
||||
|
||||
JCExpression type = null;
|
||||
if (node.get() instanceof JCVariableDecl) {
|
||||
type = ((JCVariableDecl) node.get()).vartype;
|
||||
}
|
||||
|
||||
String name = null;
|
||||
List<JCExpression> typeArgs = List.nil();
|
||||
if (type instanceof JCTypeApply) {
|
||||
typeArgs = ((JCTypeApply) type).arguments;
|
||||
type = ((JCTypeApply) type).clazz;
|
||||
}
|
||||
|
||||
name = type.toString();
|
||||
|
||||
String targetFqn = JavacSingularsRecipes.get().toQualified(name);
|
||||
JavacSingularizer singularizer = JavacSingularsRecipes.get().getSingularizer(targetFqn);
|
||||
if (singularizer == null) {
|
||||
node.addError("Lombok does not know how to create the singular-form builder methods for type '" + name + "'; they won't be generated.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -243,51 +243,45 @@ public class JavacHandlerUtil {
|
||||
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
|
||||
JCAnnotation anno = (JCAnnotation) node.get();
|
||||
List<JCExpression> arguments = anno.getArguments();
|
||||
for (Method m : type.getDeclaredMethods()) {
|
||||
if (!Modifier.isPublic(m.getModifiers())) continue;
|
||||
String name = m.getName();
|
||||
|
||||
for (JCExpression arg : arguments) {
|
||||
String mName;
|
||||
JCExpression rhs;
|
||||
java.util.List<String> raws = new ArrayList<String>();
|
||||
java.util.List<Object> guesses = new ArrayList<Object>();
|
||||
java.util.List<Object> expressions = new ArrayList<Object>();
|
||||
final java.util.List<DiagnosticPosition> positions = new ArrayList<DiagnosticPosition>();
|
||||
boolean isExplicit = false;
|
||||
|
||||
for (JCExpression arg : arguments) {
|
||||
String mName;
|
||||
JCExpression rhs;
|
||||
|
||||
if (arg instanceof JCAssign) {
|
||||
JCAssign assign = (JCAssign) arg;
|
||||
mName = assign.lhs.toString();
|
||||
rhs = assign.rhs;
|
||||
} else {
|
||||
rhs = arg;
|
||||
mName = "value";
|
||||
}
|
||||
|
||||
if (!mName.equals(name)) continue;
|
||||
isExplicit = true;
|
||||
if (rhs instanceof JCNewArray) {
|
||||
List<JCExpression> elems = ((JCNewArray)rhs).elems;
|
||||
for (JCExpression inner : elems) {
|
||||
raws.add(inner.toString());
|
||||
expressions.add(inner);
|
||||
guesses.add(calculateGuess(inner));
|
||||
positions.add(inner.pos());
|
||||
}
|
||||
} else {
|
||||
raws.add(rhs.toString());
|
||||
expressions.add(rhs);
|
||||
guesses.add(calculateGuess(rhs));
|
||||
positions.add(rhs.pos());
|
||||
}
|
||||
if (arg instanceof JCAssign) {
|
||||
JCAssign assign = (JCAssign) arg;
|
||||
mName = assign.lhs.toString();
|
||||
rhs = assign.rhs;
|
||||
} else {
|
||||
rhs = arg;
|
||||
mName = "value";
|
||||
}
|
||||
|
||||
values.put(name, new AnnotationValue(node, raws, expressions, guesses, isExplicit) {
|
||||
if (rhs instanceof JCNewArray) {
|
||||
List<JCExpression> elems = ((JCNewArray)rhs).elems;
|
||||
for (JCExpression inner : elems) {
|
||||
raws.add(inner.toString());
|
||||
expressions.add(inner);
|
||||
guesses.add(calculateGuess(inner));
|
||||
positions.add(inner.pos());
|
||||
}
|
||||
} else {
|
||||
raws.add(rhs.toString());
|
||||
expressions.add(rhs);
|
||||
guesses.add(calculateGuess(rhs));
|
||||
positions.add(rhs.pos());
|
||||
}
|
||||
|
||||
values.put(mName, new AnnotationValue(node, raws, expressions, guesses, true) {
|
||||
@Override public void setError(String message, int valueIdx) {
|
||||
if (valueIdx < 0) node.addError(message);
|
||||
else node.addError(message, positions.get(valueIdx));
|
||||
}
|
||||
|
||||
@Override public void setWarning(String message, int valueIdx) {
|
||||
if (valueIdx < 0) node.addWarning(message);
|
||||
else node.addWarning(message, positions.get(valueIdx));
|
||||
@@ -295,6 +289,21 @@ public class JavacHandlerUtil {
|
||||
});
|
||||
}
|
||||
|
||||
for (Method m : type.getDeclaredMethods()) {
|
||||
if (!Modifier.isPublic(m.getModifiers())) continue;
|
||||
String name = m.getName();
|
||||
if (!values.containsKey(name)) {
|
||||
values.put(name, new AnnotationValue(node, new ArrayList<String>(), new ArrayList<Object>(), new ArrayList<Object>(), false) {
|
||||
@Override public void setError(String message, int valueIdx) {
|
||||
node.addError(message);
|
||||
}
|
||||
@Override public void setWarning(String message, int valueIdx) {
|
||||
node.addWarning(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new AnnotationValues<A>(type, values, node);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Project Lombok Authors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package lombok.javac.handlers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.core.LombokImmutableList;
|
||||
import lombok.core.SpiLoadUtil;
|
||||
import lombok.core.TypeLibrary;
|
||||
import lombok.javac.JavacNode;
|
||||
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.JCTree.JCExpression;
|
||||
import com.sun.tools.javac.tree.JCTree.JCStatement;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import com.sun.tools.javac.util.ListBuffer;
|
||||
import com.sun.tools.javac.util.Name;
|
||||
|
||||
public class JavacSingularsRecipes {
|
||||
private static final JavacSingularsRecipes INSTANCE = new JavacSingularsRecipes();
|
||||
private final Map<String, JavacSingularizer> singularizers = new HashMap<String, JavacSingularizer>();
|
||||
private final TypeLibrary singularizableTypes = new TypeLibrary();
|
||||
|
||||
private JavacSingularsRecipes() {
|
||||
try {
|
||||
loadAll(singularizableTypes, singularizers);
|
||||
singularizableTypes.lock();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Lombok's @Singularizable feature is broken due to misconfigured SPI files: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadAll(TypeLibrary library, Map<String, JavacSingularizer> map) throws IOException {
|
||||
for (JavacSingularizer handler : SpiLoadUtil.findServices(JavacSingularizer.class, JavacSingularizer.class.getClassLoader())) {
|
||||
for (String type : handler.getSupportedTypes()) {
|
||||
JavacSingularizer existingSingularizer = map.get(type);
|
||||
if (existingSingularizer != null) {
|
||||
JavacSingularizer toKeep = existingSingularizer.getClass().getName().compareTo(handler.getClass().getName()) > 0 ? handler : existingSingularizer;
|
||||
System.err.println("Multiple singularizers found for type " + type + "; the alphabetically first class is used: " + toKeep.getClass().getName());
|
||||
map.put(type, toKeep);
|
||||
} else {
|
||||
map.put(type, handler);
|
||||
library.addType(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static JavacSingularsRecipes get() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public String toQualified(String typeReference) {
|
||||
return singularizableTypes.toQualified(typeReference);
|
||||
}
|
||||
|
||||
public JavacSingularizer getSingularizer(String fqn) {
|
||||
return singularizers.get(fqn);
|
||||
}
|
||||
|
||||
public static final class SingularData {
|
||||
private final JavacNode annotation;
|
||||
private final Name singularName;
|
||||
private final Name pluralName;
|
||||
private final List<JCExpression> typeArgs;
|
||||
private final String targetFqn;
|
||||
private final JavacSingularizer singularizer;
|
||||
|
||||
public SingularData(JavacNode annotation, Name singularName, Name pluralName, List<JCExpression> typeArgs, String targetFqn, JavacSingularizer singularizer) {
|
||||
this.annotation = annotation;
|
||||
this.singularName = singularName;
|
||||
this.pluralName = pluralName;
|
||||
this.typeArgs = typeArgs;
|
||||
this.targetFqn = targetFqn;
|
||||
this.singularizer = singularizer;
|
||||
}
|
||||
|
||||
public JavacNode getAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
public Name getSingularName() {
|
||||
return singularName;
|
||||
}
|
||||
|
||||
public Name getPluralName() {
|
||||
return pluralName;
|
||||
}
|
||||
|
||||
public List<JCExpression> getTypeArgs() {
|
||||
return typeArgs;
|
||||
}
|
||||
|
||||
public String getTargetFqn() {
|
||||
return targetFqn;
|
||||
}
|
||||
|
||||
public JavacSingularizer getSingularizer() {
|
||||
return singularizer;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class JavacSingularizer {
|
||||
public abstract LombokImmutableList<String> getSupportedTypes();
|
||||
|
||||
public abstract JavacNode generateFields(SingularData data, JavacNode builderType, JCTree source);
|
||||
public abstract void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain);
|
||||
public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName);
|
||||
|
||||
public boolean requiresCleaning() {
|
||||
try {
|
||||
return !getClass().getMethod("appendCleaningCode", ListBuffer.class).getDeclaringClass().equals(JavacSingularizer.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void appendCleaningCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Project Lombok Authors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package lombok.javac.handlers.singulars;
|
||||
|
||||
import static lombok.javac.handlers.JavacHandlerUtil.*;
|
||||
|
||||
import org.mangosdk.spi.ProviderFor;
|
||||
|
||||
import lombok.core.LombokImmutableList;
|
||||
import lombok.core.handlers.HandlerUtil;
|
||||
import static lombok.javac.Javac.*;
|
||||
import lombok.javac.JavacNode;
|
||||
import lombok.javac.JavacTreeMaker;
|
||||
import lombok.javac.handlers.JavacHandlerUtil;
|
||||
import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
|
||||
import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
|
||||
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import com.sun.tools.javac.code.Flags;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.JCTree.JCBlock;
|
||||
import com.sun.tools.javac.tree.JCTree.JCExpression;
|
||||
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
|
||||
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.JCModifiers;
|
||||
import com.sun.tools.javac.tree.JCTree.JCStatement;
|
||||
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
|
||||
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.JCWildcard;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import com.sun.tools.javac.util.ListBuffer;
|
||||
import com.sun.tools.javac.util.Name;
|
||||
|
||||
@ProviderFor(JavacSingularizer.class)
|
||||
public class JavacJavaUtilSetSingularizer extends JavacJavaUtilSingularizer {
|
||||
@Override public LombokImmutableList<String> getSupportedTypes() {
|
||||
return LombokImmutableList.of("java.util.Set", "java.util.SortedSet", "java.util.NavigableSet");
|
||||
}
|
||||
|
||||
@Override public JavacNode generateFields(SingularData data, JavacNode builderType, JCTree source) {
|
||||
JavacTreeMaker maker = builderType.getTreeMaker();
|
||||
JCExpression type = JavacHandlerUtil.chainDots(builderType, "java", "util", "ArrayList");
|
||||
type = addTypeArgs(1, false, builderType, type, data.getTypeArgs(), source);
|
||||
|
||||
JCVariableDecl buildField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), data.getPluralName(), type, null);
|
||||
return injectField(builderType, buildField);
|
||||
}
|
||||
|
||||
@Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) {
|
||||
JavacTreeMaker maker = builderType.getTreeMaker();
|
||||
JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
|
||||
JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
|
||||
|
||||
generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
|
||||
generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
|
||||
}
|
||||
|
||||
private void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
|
||||
List<JCTypeParameter> typeParams = List.nil();
|
||||
List<JCExpression> thrown = List.nil();
|
||||
JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
|
||||
ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
|
||||
JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), "add");
|
||||
JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAdd, List.<JCExpression>of(maker.Ident(data.getSingularName())));
|
||||
statements.append(maker.Exec(invokeAdd));
|
||||
if (returnStatement != null) statements.append(returnStatement);
|
||||
JCBlock body = maker.Block(0, statements.toList());
|
||||
Name name = data.getSingularName();
|
||||
long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
|
||||
if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName("add", name.toString()));
|
||||
JCExpression paramType; {
|
||||
if (data.getTypeArgs() == null || data.getTypeArgs().isEmpty()) {
|
||||
paramType = chainDots(builderType, "java", "lang", "Object");
|
||||
} else {
|
||||
JCExpression originalType = data.getTypeArgs().head;
|
||||
if (originalType.getKind() == Kind.UNBOUNDED_WILDCARD || originalType.getKind() == Kind.SUPER_WILDCARD) {
|
||||
paramType = chainDots(builderType, "java", "lang", "Object");
|
||||
} else if (originalType.getKind() == Kind.EXTENDS_WILDCARD) {
|
||||
try {
|
||||
paramType = cloneType(maker, (JCExpression) ((JCWildcard) originalType).inner, source, builderType.getContext());
|
||||
} catch (Exception e) {
|
||||
paramType = chainDots(builderType, "java", "lang", "Object");
|
||||
}
|
||||
} else {
|
||||
paramType = cloneType(maker, originalType, source, builderType.getContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getSingularName(), paramType, null);
|
||||
JCMethodDecl method = maker.MethodDef(mods, name, returnType, typeParams, List.of(param), thrown, body, null);
|
||||
injectMethod(builderType, method);
|
||||
}
|
||||
|
||||
private void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
|
||||
List<JCTypeParameter> typeParams = List.nil();
|
||||
List<JCExpression> thrown = List.nil();
|
||||
JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
|
||||
ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
|
||||
JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), "addAll");
|
||||
JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAdd, List.<JCExpression>of(maker.Ident(data.getPluralName())));
|
||||
statements.append(maker.Exec(invokeAdd));
|
||||
if (returnStatement != null) statements.append(returnStatement);
|
||||
JCBlock body = maker.Block(0, statements.toList());
|
||||
Name name = data.getPluralName();
|
||||
long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
|
||||
if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName("addAll", name.toString()));
|
||||
JCExpression paramType = chainDots(builderType, "java", "util", "Collection");
|
||||
paramType = addTypeArgs(1, true, builderType, paramType, data.getTypeArgs(), source);
|
||||
JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getPluralName(), paramType, null);
|
||||
JCMethodDecl method = maker.MethodDef(mods, name, returnType, typeParams, List.of(param), thrown, body, null);
|
||||
injectMethod(builderType, method);
|
||||
}
|
||||
|
||||
private JCExpression getSize(JavacTreeMaker maker, JavacNode builderType, Name name) {
|
||||
JCExpression fn = maker.Select(maker.Select(maker.Ident(builderType.toName("this")), name), builderType.toName("size"));
|
||||
return maker.Apply(List.<JCExpression>nil(), fn, List.<JCExpression>nil());
|
||||
}
|
||||
|
||||
@Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) {
|
||||
JavacTreeMaker maker = builderType.getTreeMaker();
|
||||
JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn());
|
||||
localShadowerType = addTypeArgs(1, false, builderType, localShadowerType, data.getTypeArgs(), source);
|
||||
JCExpression constructTargetType; {
|
||||
if (data.getTargetFqn().equals("java.util.Set")) {
|
||||
JCExpression internalType = chainDots(builderType, "java", "util", "LinkedHashSet");
|
||||
internalType = addTypeArgs(1, false, builderType, internalType, data.getTypeArgs(), source);
|
||||
JCExpression loadFactor = maker.Literal(CTC_FLOAT, 0.75f);
|
||||
JCExpression lessThanCutoff = maker.Binary(CTC_LESS_THAN, getSize(maker, builderType, data.getPluralName()), maker.Literal(CTC_INT, 0x40000000));
|
||||
JCExpression maxInt = chainDots(builderType, "java", "lang", "Integer", "MAX_VALUE");
|
||||
JCExpression belowThree = maker.Binary(CTC_LESS_THAN, getSize(maker, builderType, data.getPluralName()), maker.Literal(CTC_INT, 3));
|
||||
JCExpression sizePlusOne = maker.Binary(CTC_PLUS, getSize(maker, builderType, data.getPluralName()), maker.Literal(CTC_INT, 1));
|
||||
JCExpression sizeDivThree = maker.Binary(CTC_DIV, getSize(maker, builderType, data.getPluralName()), maker.Literal(CTC_INT, 3));
|
||||
JCExpression sizePlusSizeDivThree = maker.Binary(CTC_PLUS, getSize(maker, builderType, data.getPluralName()), sizeDivThree);
|
||||
JCExpression rest = maker.Conditional(belowThree, sizePlusOne, sizePlusSizeDivThree);
|
||||
JCExpression initialCapacity = maker.Conditional(lessThanCutoff, rest, maxInt);
|
||||
constructTargetType = maker.NewClass(null, List.<JCExpression>nil(), internalType, List.<JCExpression>of(initialCapacity, loadFactor), null);
|
||||
} else {
|
||||
JCExpression internalType = chainDots(builderType, "java", "util", "TreeSet");
|
||||
internalType = addTypeArgs(1, false, builderType, internalType, data.getTypeArgs(), source);
|
||||
constructTargetType = maker.NewClass(null, List.<JCExpression>nil(), internalType, List.<JCExpression>nil(), null);
|
||||
}
|
||||
}
|
||||
JCVariableDecl varDef = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, constructTargetType);
|
||||
statements.append(varDef);
|
||||
JCFieldAccess varDotAddAll = maker.Select(maker.Ident(data.getPluralName()), builderType.toName("addAll"));
|
||||
JCExpression thisDotFieldName = maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName());
|
||||
statements.append(maker.Exec(maker.Apply(List.<JCExpression>nil(), varDotAddAll, List.of(thisDotFieldName))));
|
||||
String singletonMaker = "unmodifiable" + data.getTargetFqn().substring(data.getTargetFqn().lastIndexOf(".") + 1);
|
||||
JCExpression javaUtilCollectionsInvoke = maker.Apply(List.<JCExpression>nil(), chainDots(builderType, "java", "util", "Collections", singletonMaker), List.<JCExpression>of(maker.Ident(data.getPluralName())));
|
||||
statements.append(maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), javaUtilCollectionsInvoke)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Project Lombok Authors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package lombok.javac.handlers.singulars;
|
||||
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import com.sun.tools.javac.code.BoundKind;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.JCTree.JCExpression;
|
||||
import com.sun.tools.javac.tree.JCTree.JCWildcard;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import com.sun.tools.javac.util.ListBuffer;
|
||||
|
||||
import lombok.javac.JavacNode;
|
||||
import lombok.javac.JavacTreeMaker;
|
||||
import static lombok.javac.handlers.JavacHandlerUtil.*;
|
||||
import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
|
||||
|
||||
public abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
|
||||
/**
|
||||
* Adds the requested number of type arguments to the provided type, copying each argument in {@code typeArgs}. If typeArgs is too long, the extra elements are ignored.
|
||||
* If {@code typeArgs} is null or too short, {@code java.lang.Object} will be substituted for each missing type argument.
|
||||
*
|
||||
* @param count The number of type arguments requested.
|
||||
* @param addExtends If {@code true}, all bounds are either '? extends X' or just '?'. If false, the reverse is applied, and '? extends Foo' is converted to Foo, '?' to Object, etc.
|
||||
* @param node Some node in the same AST. Just used to obtain makers and contexts and such.
|
||||
* @param type The type to add generics to.
|
||||
* @param typeArgs the list of type args to clone.
|
||||
* @param source The source annotation that is the root cause of this code generation.
|
||||
*/
|
||||
protected JCExpression addTypeArgs(int count, boolean addExtends, JavacNode node, JCExpression type, List<JCExpression> typeArgs, JCTree source) {
|
||||
JavacTreeMaker maker = node.getTreeMaker();
|
||||
Context context = node.getContext();
|
||||
|
||||
if (count < 0) throw new IllegalArgumentException("count is negative");
|
||||
if (count == 0) return type;
|
||||
ListBuffer<JCExpression> arguments = new ListBuffer<JCExpression>();
|
||||
|
||||
if (typeArgs != null) for (JCExpression orig : typeArgs) {
|
||||
if (!addExtends) {
|
||||
if (orig.getKind() == Kind.UNBOUNDED_WILDCARD || orig.getKind() == Kind.SUPER_WILDCARD) {
|
||||
arguments.append(chainDots(node, "java", "lang", "Object"));
|
||||
} else if (orig.getKind() == Kind.EXTENDS_WILDCARD) {
|
||||
JCExpression inner;
|
||||
try {
|
||||
inner = (JCExpression) ((JCWildcard) orig).inner;
|
||||
} catch (Exception e) {
|
||||
inner = chainDots(node, "java", "lang", "Object");
|
||||
}
|
||||
arguments.append(cloneType(maker, inner, source, context));
|
||||
} else {
|
||||
arguments.append(cloneType(maker, orig, source, context));
|
||||
}
|
||||
} else {
|
||||
if (orig.getKind() == Kind.UNBOUNDED_WILDCARD || orig.getKind() == Kind.SUPER_WILDCARD) {
|
||||
arguments.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null));
|
||||
} else if (orig.getKind() == Kind.EXTENDS_WILDCARD) {
|
||||
arguments.append(cloneType(maker, orig, source, context));
|
||||
} else {
|
||||
arguments.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.EXTENDS), cloneType(maker, orig, source, context)));
|
||||
}
|
||||
}
|
||||
if (--count == 0) break;
|
||||
}
|
||||
|
||||
while (count-- > 0) {
|
||||
if (addExtends) {
|
||||
arguments.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null));
|
||||
} else {
|
||||
arguments.append(chainDots(node, "java", "lang", "Object"));
|
||||
}
|
||||
}
|
||||
return maker.TypeApply(type, arguments.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -165,6 +165,10 @@ public class Javac {
|
||||
public static final TypeTag CTC_CLASS = typeTag("CLASS");
|
||||
|
||||
public static final TreeTag CTC_NOT_EQUAL = treeTag("NE");
|
||||
public static final TreeTag CTC_LESS_THAN = treeTag("LT");
|
||||
public static final TreeTag CTC_GREATER_THAN = treeTag("GT");
|
||||
public static final TreeTag CTC_LESS_OR_EQUAL= treeTag("LE");
|
||||
public static final TreeTag CTC_GREATER_OR_EQUAL = treeTag("GE");
|
||||
public static final TreeTag CTC_POS = treeTag("POS");
|
||||
public static final TreeTag CTC_NEG = treeTag("NEG");
|
||||
public static final TreeTag CTC_NOT = treeTag("NOT");
|
||||
@@ -172,6 +176,7 @@ public class Javac {
|
||||
public static final TreeTag CTC_BITXOR = treeTag("BITXOR");
|
||||
public static final TreeTag CTC_UNSIGNED_SHIFT_RIGHT = treeTag("USR");
|
||||
public static final TreeTag CTC_MUL = treeTag("MUL");
|
||||
public static final TreeTag CTC_DIV = treeTag("DIV");
|
||||
public static final TreeTag CTC_PLUS = treeTag("PLUS");
|
||||
public static final TreeTag CTC_EQUAL = treeTag("EQ");
|
||||
public static final TreeTag CTC_PREINC = treeTag("PREINC");
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
class BuilderSingletons<T> {
|
||||
private Set<T> dangerMice;
|
||||
private SortedSet<? extends Number> octopodes;
|
||||
private Set rawSet;
|
||||
private Set<String> stringSet;
|
||||
@java.lang.SuppressWarnings("all")
|
||||
BuilderSingletons(final Set<T> dangerMice, final SortedSet<? extends Number> octopodes, final Set rawSet, final Set<String> stringSet) {
|
||||
this.dangerMice = dangerMice;
|
||||
this.octopodes = octopodes;
|
||||
this.rawSet = rawSet;
|
||||
this.stringSet = stringSet;
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public static class BuilderSingletonsBuilder<T> {
|
||||
private java.util.ArrayList<T> dangerMice;
|
||||
private java.util.ArrayList<Number> octopodes;
|
||||
private java.util.ArrayList<java.lang.Object> rawSet;
|
||||
private java.util.ArrayList<String> stringSet;
|
||||
@java.lang.SuppressWarnings("all")
|
||||
BuilderSingletonsBuilder() {
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public BuilderSingletonsBuilder<T> dangerMouse(final T dangerMouse) {
|
||||
this.dangerMice.add(dangerMouse);
|
||||
return this;
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public BuilderSingletonsBuilder<T> dangerMice(final java.util.Collection<? extends T> dangerMice) {
|
||||
this.dangerMice.addAll(dangerMice);
|
||||
return this;
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public BuilderSingletonsBuilder<T> octopus(final Number octopus) {
|
||||
this.octopodes.add(octopus);
|
||||
return this;
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public BuilderSingletonsBuilder<T> octopodes(final java.util.Collection<? extends Number> octopodes) {
|
||||
this.octopodes.addAll(octopodes);
|
||||
return this;
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public BuilderSingletonsBuilder<T> rawSet(final java.lang.Object rawSet) {
|
||||
this.rawSet.add(rawSet);
|
||||
return this;
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public BuilderSingletonsBuilder<T> rawSet(final java.util.Collection<?> rawSet) {
|
||||
this.rawSet.addAll(rawSet);
|
||||
return this;
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public BuilderSingletonsBuilder<T> stringSet(final String stringSet) {
|
||||
this.stringSet.add(stringSet);
|
||||
return this;
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public BuilderSingletonsBuilder<T> stringSet(final java.util.Collection<? extends String> stringSet) {
|
||||
this.stringSet.addAll(stringSet);
|
||||
return this;
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public BuilderSingletons<T> build() {
|
||||
java.util.Set<T> dangerMice = new java.util.LinkedHashSet<T>(this.dangerMice.size() < 1073741824 ? this.dangerMice.size() < 3 ? this.dangerMice.size() + 1 : this.dangerMice.size() + this.dangerMice.size() / 3 : java.lang.Integer.MAX_VALUE, 0.75F);
|
||||
dangerMice.addAll(this.dangerMice);
|
||||
dangerMice = java.util.Collections.unmodifiableSet(dangerMice);
|
||||
java.util.SortedSet<Number> octopodes = new java.util.TreeSet<Number>();
|
||||
octopodes.addAll(this.octopodes);
|
||||
octopodes = java.util.Collections.unmodifiableSortedSet(octopodes);
|
||||
java.util.Set<java.lang.Object> rawSet = new java.util.LinkedHashSet<java.lang.Object>(this.rawSet.size() < 1073741824 ? this.rawSet.size() < 3 ? this.rawSet.size() + 1 : this.rawSet.size() + this.rawSet.size() / 3 : java.lang.Integer.MAX_VALUE, 0.75F);
|
||||
rawSet.addAll(this.rawSet);
|
||||
rawSet = java.util.Collections.unmodifiableSet(rawSet);
|
||||
java.util.Set<String> stringSet = new java.util.LinkedHashSet<String>(this.stringSet.size() < 1073741824 ? this.stringSet.size() < 3 ? this.stringSet.size() + 1 : this.stringSet.size() + this.stringSet.size() / 3 : java.lang.Integer.MAX_VALUE, 0.75F);
|
||||
stringSet.addAll(this.stringSet);
|
||||
stringSet = java.util.Collections.unmodifiableSet(stringSet);
|
||||
return new BuilderSingletons<T>(dangerMice, octopodes, rawSet, stringSet);
|
||||
}
|
||||
@java.lang.Override
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public java.lang.String toString() {
|
||||
return "BuilderSingletons.BuilderSingletonsBuilder(dangerMice=" + this.dangerMice + ", octopodes=" + this.octopodes + ", rawSet=" + this.rawSet + ", stringSet=" + this.stringSet + ")";
|
||||
}
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public static <T> BuilderSingletonsBuilder<T> builder() {
|
||||
return new BuilderSingletonsBuilder<T>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
|
||||
import lombok.Singular;
|
||||
|
||||
@lombok.Builder
|
||||
class BuilderSingletons<T> {
|
||||
@Singular private Set<T> dangerMice;
|
||||
@Singular private SortedSet<? extends Number> octopodes;
|
||||
@Singular("rawSet") private Set rawSet;
|
||||
@Singular("stringSet") private Set<String> stringSet;
|
||||
}
|
||||
Reference in new issue
Block a user