aboutsummaryrefslogtreecommitdiff
path: root/src/core/lombok
diff options
context:
space:
mode:
authorttzn <ttzn.dev@gmail.com>2020-12-26 13:13:24 +0100
committerttzn <ttzn.dev@gmail.com>2021-03-24 22:27:25 +0100
commit63ea3db6b7ab41ff945cad1ec8af7f78b5679f66 (patch)
treea16197915108110f2c12f0ece0d32cef8fdab9f7 /src/core/lombok
parent30a2921e6321d20f0f925694dfd6a32d9e1f1552 (diff)
downloadlombok-63ea3db6b7ab41ff945cad1ec8af7f78b5679f66.tar.gz
lombok-63ea3db6b7ab41ff945cad1ec8af7f78b5679f66.tar.bz2
lombok-63ea3db6b7ab41ff945cad1ec8af7f78b5679f66.zip
@StandardException feature working draft
* annotation with javac and Eclipse handlers * single test file
Diffstat (limited to 'src/core/lombok')
-rw-r--r--src/core/lombok/ConfigurationKeys.java6
-rw-r--r--src/core/lombok/StandardException.java34
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/HandleStandardException.java250
-rw-r--r--src/core/lombok/javac/handlers/HandleStandardException.java216
4 files changed, 506 insertions, 0 deletions
diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java
index ba66649b..b8cd442a 100644
--- a/src/core/lombok/ConfigurationKeys.java
+++ b/src/core/lombok/ConfigurationKeys.java
@@ -695,4 +695,10 @@ public class ConfigurationKeys {
*/
public static final ConfigurationKey<CheckerFrameworkVersion> CHECKER_FRAMEWORK = new ConfigurationKey<CheckerFrameworkVersion>("checkerframework", "If set with the version of checkerframework.org (in major.minor, or just 'true' for the latest supported version), create relevant checkerframework.org annotations for code lombok generates (default: false).") {};
+ /**
+ * lombok configuration: {@code lombok.standardException.flagUsage} = {@code WARNING} | {@code ERROR}.
+ *
+ * If set, <em>any</em> usage of {@code @StandardException} results in a warning / error.
+ */
+ public static final ConfigurationKey<FlagUsageType> STANDARD_EXCEPTION_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.standardException.flagUsage", "Emit a warning or error if @StandardException is used.") {};
}
diff --git a/src/core/lombok/StandardException.java b/src/core/lombok/StandardException.java
new file mode 100644
index 00000000..0456a649
--- /dev/null
+++ b/src/core/lombok/StandardException.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.SOURCE)
+public @interface StandardException {
+}
diff --git a/src/core/lombok/eclipse/handlers/HandleStandardException.java b/src/core/lombok/eclipse/handlers/HandleStandardException.java
new file mode 100755
index 00000000..9996f4fa
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/HandleStandardException.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2010-2020 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers;
+
+import lombok.AccessLevel;
+import lombok.ConfigurationKeys;
+import lombok.StandardException;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.eclipse.Eclipse;
+import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseHandlerUtil.*;
+import org.eclipse.jdt.internal.compiler.ast.*;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.mangosdk.spi.ProviderFor;
+
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+import static lombok.core.handlers.HandlerUtil.handleFlagUsage;
+import static lombok.eclipse.Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+import static lombok.eclipse.Eclipse.pos;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+@ProviderFor(EclipseAnnotationHandler.class)
+public class HandleStandardException extends EclipseAnnotationHandler<StandardException> {
+ private static final String NAME = StandardException.class.getSimpleName();
+
+ @Override
+ public void handle(AnnotationValues<StandardException> annotation, Annotation ast, EclipseNode annotationNode) {
+ handleFlagUsage(annotationNode, ConfigurationKeys.STANDARD_EXCEPTION_FLAG_USAGE, "@StandardException");
+
+ EclipseNode typeNode = annotationNode.up();
+ if (!checkLegality(typeNode, annotationNode)) return;
+
+ SuperParameter message = new SuperParameter("message", new SingleTypeReference("String".toCharArray(), pos(typeNode.get())));
+ SuperParameter cause = new SuperParameter("cause", new SingleTypeReference("Throwable".toCharArray(), pos(typeNode.get())));
+
+ boolean skip = true;
+ generateConstructor(
+ typeNode, AccessLevel.PUBLIC, Collections.<SuperParameter>emptyList(), skip, annotationNode);
+ generateConstructor(
+ typeNode, AccessLevel.PUBLIC, Collections.singletonList(message), skip, annotationNode);
+ generateConstructor(
+ typeNode, AccessLevel.PUBLIC, Collections.singletonList(cause), skip, annotationNode);
+ generateConstructor(
+ typeNode, AccessLevel.PUBLIC, Arrays.asList(message, cause), skip, annotationNode);
+ }
+
+ private static boolean checkLegality(EclipseNode typeNode, EclipseNode errorNode) {
+ TypeDeclaration typeDecl = null;
+ if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get();
+ int modifiers = typeDecl == null ? 0 : typeDecl.modifiers;
+ boolean notAClass = (modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0;
+
+ if (typeDecl == null || notAClass) {
+ errorNode.addError(HandleStandardException.NAME + " is only supported on a class or an enum.");
+ return false;
+ }
+
+ return true;
+ }
+
+ public void generateConstructor(
+ EclipseNode typeNode, AccessLevel level, List<SuperParameter> parameters, boolean skipIfConstructorExists,
+ EclipseNode sourceNode) {
+
+ generate(typeNode, level, parameters, skipIfConstructorExists, sourceNode);
+ }
+
+ public void generate(
+ EclipseNode typeNode, AccessLevel level, List<SuperParameter> parameters, boolean skipIfConstructorExists,
+ EclipseNode sourceNode) {
+ if (!(skipIfConstructorExists
+ && constructorExists(typeNode, parameters) != MemberExistsResult.NOT_EXISTS)) {
+ ConstructorDeclaration constr = createConstructor(level, typeNode, parameters, sourceNode);
+ injectMethod(typeNode, constr);
+ }
+ }
+
+ /**
+ * Checks if a constructor with the provided parameters exists under the type node.
+ */
+ public static MemberExistsResult constructorExists(EclipseNode node, List<SuperParameter> parameters) {
+ node = upToTypeNode(node);
+ SuperParameter[] parameterArray = parameters.toArray(new SuperParameter[0]);
+
+ if (node != null && node.get() instanceof TypeDeclaration) {
+ TypeDeclaration typeDecl = (TypeDeclaration)node.get();
+ if (typeDecl.methods != null) for (AbstractMethodDeclaration def : typeDecl.methods) {
+ if (def instanceof ConstructorDeclaration) {
+ if (!paramsMatch(node, def.arguments, parameterArray)) continue;
+ return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK;
+ }
+ }
+ }
+
+ return MemberExistsResult.NOT_EXISTS;
+ }
+
+ private static boolean paramsMatch(EclipseNode node, Argument[] arguments, SuperParameter[] parameters) {
+ if (arguments == null) {
+ return parameters.length == 0;
+ } else if (arguments.length != parameters.length) {
+ return false;
+ } else {
+ for (int i = 0; i < parameters.length; i++) {
+ String fieldTypeName = Eclipse.toQualifiedName(parameters[i].type.getTypeName());
+ String argTypeName = Eclipse.toQualifiedName(arguments[i].type.getTypeName());
+
+ if (!typeNamesMatch(node, fieldTypeName, argTypeName))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean typeNamesMatch(EclipseNode node, String a, String b) {
+ boolean isFqn = node.getImportListAsTypeResolver().typeMatches(node, a, b);
+ boolean reverseIsFqn = node.getImportListAsTypeResolver().typeMatches(node, b, a);
+ return isFqn || reverseIsFqn;
+ }
+
+ private static final char[][] JAVA_BEANS_CONSTRUCTORPROPERTIES = new char[][] { "java".toCharArray(), "beans".toCharArray(), "ConstructorProperties".toCharArray() };
+ public static Annotation[] createConstructorProperties(ASTNode source, Collection<SuperParameter> fields) {
+ if (fields.isEmpty()) return null;
+
+ int pS = source.sourceStart, pE = source.sourceEnd;
+ long p = (long) pS << 32 | pE;
+ long[] poss = new long[3];
+ Arrays.fill(poss, p);
+ QualifiedTypeReference constructorPropertiesType = new QualifiedTypeReference(JAVA_BEANS_CONSTRUCTORPROPERTIES, poss);
+ setGeneratedBy(constructorPropertiesType, source);
+ SingleMemberAnnotation ann = new SingleMemberAnnotation(constructorPropertiesType, pS);
+ ann.declarationSourceEnd = pE;
+
+ ArrayInitializer fieldNames = new ArrayInitializer();
+ fieldNames.sourceStart = pS;
+ fieldNames.sourceEnd = pE;
+ fieldNames.expressions = new Expression[fields.size()];
+
+ int ctr = 0;
+ for (SuperParameter field : fields) {
+ char[] fieldName = field.name.toCharArray();
+ fieldNames.expressions[ctr] = new StringLiteral(fieldName, pS, pE, 0);
+ setGeneratedBy(fieldNames.expressions[ctr], source);
+ ctr++;
+ }
+
+ ann.memberValue = fieldNames;
+ setGeneratedBy(ann, source);
+ setGeneratedBy(ann.memberValue, source);
+ return new Annotation[] { ann };
+ }
+
+ @SuppressWarnings("deprecation") public static ConstructorDeclaration createConstructor(
+ AccessLevel level, EclipseNode type, Collection<SuperParameter> parameters, EclipseNode sourceNode) {
+
+ ASTNode source = sourceNode.get();
+ TypeDeclaration typeDeclaration = ((TypeDeclaration) type.get());
+
+ boolean isEnum = (((TypeDeclaration) type.get()).modifiers & ClassFileConstants.AccEnum) != 0;
+
+ if (isEnum) level = AccessLevel.PRIVATE;
+
+ boolean addConstructorProperties;
+ if (parameters.isEmpty()) {
+ addConstructorProperties = false;
+ } else {
+ Boolean v = type.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES);
+ addConstructorProperties = v != null ? v.booleanValue() :
+ Boolean.FALSE.equals(type.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES));
+ }
+
+ ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
+
+ constructor.modifiers = toEclipseModifier(level);
+ constructor.selector = typeDeclaration.name;
+ constructor.thrownExceptions = null;
+ constructor.typeParameters = null;
+ constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart;
+ constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd;
+ constructor.arguments = null;
+
+ List<Argument> params = new ArrayList<Argument>();
+ List<Expression> superArgs = new ArrayList<Expression>();
+
+ for (SuperParameter fieldNode : parameters) {
+ char[] fieldName = fieldNode.name.toCharArray();
+ long fieldPos = (((long) type.get().sourceStart) << 32) | type.get().sourceEnd;
+ Argument parameter = new Argument(fieldName, fieldPos, copyType(fieldNode.type, source), Modifier.FINAL);
+ params.add(parameter);
+ superArgs.add(new SingleNameReference(fieldName, 0));
+ }
+
+ // Super constructor call
+ constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super);
+ constructor.constructorCall.arguments = superArgs.toArray(new Expression[0]);
+ constructor.constructorCall.sourceStart = source.sourceStart;
+ constructor.constructorCall.sourceEnd = source.sourceEnd;
+
+ constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[0]);
+
+ Annotation[] constructorProperties = null;
+ if (addConstructorProperties && !isLocalType(type)) constructorProperties = createConstructorProperties(source, parameters);
+ constructor.annotations = copyAnnotations(source,
+ constructorProperties);
+
+ constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope);
+ return constructor;
+ }
+
+ public 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());
+ return true;
+ }
+
+ private static class SuperParameter {
+ private final String name;
+ private final TypeReference type;
+
+ private SuperParameter(String name, TypeReference type) {
+ this.name = name;
+ this.type = type;
+ }
+ }
+}
diff --git a/src/core/lombok/javac/handlers/HandleStandardException.java b/src/core/lombok/javac/handlers/HandleStandardException.java
new file mode 100644
index 00000000..6a382788
--- /dev/null
+++ b/src/core/lombok/javac/handlers/HandleStandardException.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2010-2019 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 com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.*;
+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.ConfigurationKeys;
+import lombok.StandardException;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.delombok.LombokOptionsFactory;
+import lombok.javac.Javac;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacHandlerUtil.*;
+import org.mangosdk.spi.ProviderFor;
+
+import static lombok.core.handlers.HandlerUtil.handleFlagUsage;
+import static lombok.javac.Javac.CTC_VOID;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+@ProviderFor(JavacAnnotationHandler.class)
+public class HandleStandardException extends JavacAnnotationHandler<StandardException> {
+ private static final String NAME = StandardException.class.getSimpleName();
+
+ @Override
+ public void handle(AnnotationValues<StandardException> annotation, JCAnnotation ast, JavacNode annotationNode) {
+ handleFlagUsage(annotationNode, ConfigurationKeys.STANDARD_EXCEPTION_FLAG_USAGE, "@StandardException");
+ deleteAnnotationIfNeccessary(annotationNode, StandardException.class);
+ JavacNode typeNode = annotationNode.up();
+ if (!checkLegality(typeNode, annotationNode)) return;
+
+ SuperParameter messageField = new SuperParameter("message", typeNode.getSymbolTable().stringType);
+ SuperParameter causeField = new SuperParameter("cause", typeNode.getSymbolTable().throwableType);
+
+ boolean skip = true;
+ generateConstructor(typeNode, AccessLevel.PUBLIC, List.<SuperParameter>nil(), skip, annotationNode);
+ generateConstructor(typeNode, AccessLevel.PUBLIC, List.of(messageField), skip, annotationNode);
+ generateConstructor(typeNode, AccessLevel.PUBLIC, List.of(causeField), skip, annotationNode);
+ generateConstructor(typeNode, AccessLevel.PUBLIC, List.of(messageField, causeField), skip, annotationNode);
+ }
+
+ private static boolean checkLegality(JavacNode typeNode, JavacNode errorNode) {
+ JCClassDecl typeDecl = null;
+ if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get();
+ long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags;
+ boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION)) != 0;
+
+ if (typeDecl == null || notAClass) {
+ errorNode.addError(NAME + " is only supported on a class or an enum.");
+ return false;
+ }
+
+ return true;
+ }
+
+ public void generateConstructor(JavacNode typeNode, AccessLevel level, List<SuperParameter> fields,
+ boolean skipIfConstructorExists, JavacNode source) {
+ generate(typeNode, level, fields, skipIfConstructorExists, source);
+ }
+
+ private void generate(JavacNode typeNode, AccessLevel level, List<SuperParameter> fields, boolean skipIfConstructorExists,
+ JavacNode source) {
+ ListBuffer<Type> argTypes = new ListBuffer<Type>();
+ for (SuperParameter field : fields) {
+ Type mirror = field.type;
+ if (mirror == null) {
+ argTypes = null;
+ break;
+ }
+ argTypes.append(mirror);
+ }
+ List<Type> argTypes_ = argTypes == null ? null : argTypes.toList();
+
+ if (!(skipIfConstructorExists && constructorExists(typeNode, fields) != MemberExistsResult.NOT_EXISTS)) {
+ JCMethodDecl constr = createConstructor(level, typeNode, fields, source);
+ injectMethod(typeNode, constr, argTypes_, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID));
+ }
+ }
+
+ public static MemberExistsResult constructorExists(JavacNode node, List<SuperParameter> parameters) {
+ node = upToTypeNode(node);
+
+ if (node != null && node.get() instanceof JCClassDecl) {
+ for (JCTree def : ((JCClassDecl) node.get()).defs) {
+ if (def instanceof JCMethodDecl) {
+ JCMethodDecl md = (JCMethodDecl) def;
+ if (md.name.contentEquals("<init>") && (md.mods.flags & Flags.GENERATEDCONSTR) == 0) {
+ if (!paramsMatch(md.params, parameters)) continue;
+ return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK;
+ }
+ }
+ }
+ }
+
+ return MemberExistsResult.NOT_EXISTS;
+ }
+
+ private static boolean paramsMatch(List<JCVariableDecl> params, List<SuperParameter> superParams) {
+ if (params == null) {
+ return superParams.size() == 0;
+ } else if (params.size() != superParams.size()) {
+ return false;
+ } else {
+ for (int i = 0; i < superParams.size(); i++) {
+ SuperParameter field = superParams.get(i);
+ JCVariableDecl param = params.get(i);
+ if (!param.getType().type.equals(field.type))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static void addConstructorProperties(JCModifiers mods, JavacNode node, List<SuperParameter> fields) {
+ if (fields.isEmpty()) return;
+ JavacTreeMaker maker = node.getTreeMaker();
+ JCExpression constructorPropertiesType = chainDots(node, "java", "beans", "ConstructorProperties");
+ ListBuffer<JCExpression> fieldNames = new ListBuffer<JCExpression>();
+ for (SuperParameter field : fields) {
+ Name fieldName = node.toName(field.name);
+ fieldNames.append(maker.Literal(fieldName.toString()));
+ }
+ JCExpression fieldNamesArray = maker.NewArray(null, List.<JCExpression>nil(), fieldNames.toList());
+ JCAnnotation annotation = maker.Annotation(constructorPropertiesType, List.of(fieldNamesArray));
+ mods.annotations = mods.annotations.append(annotation);
+ }
+
+ @SuppressWarnings("deprecation") public static JCMethodDecl createConstructor(AccessLevel level, JavacNode typeNode,
+ List<SuperParameter> fieldsToParam, JavacNode source) {
+ JavacTreeMaker maker = typeNode.getTreeMaker();
+
+ boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0;
+ if (isEnum) level = AccessLevel.PRIVATE;
+
+ boolean addConstructorProperties;
+
+ if (fieldsToParam.isEmpty()) {
+ addConstructorProperties = false;
+ } else {
+ Boolean v = typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES);
+ addConstructorProperties = v != null ? v.booleanValue() :
+ Boolean.FALSE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES));
+ }
+
+ ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
+ ListBuffer<JCExpression> superArgs = new ListBuffer<JCExpression>();
+
+ for (SuperParameter fieldNode : fieldsToParam) {
+ Name fieldName = source.toName(fieldNode.name);
+ long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
+ JCExpression pType = maker.getUnderlyingTreeMaker().Ident(fieldNode.type.tsym);
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), fieldName, pType, null);
+ params.append(param);
+ superArgs.append(maker.Ident(fieldName));
+ }
+
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(),
+ maker.Ident(typeNode.toName("super")),
+ superArgs.toList());
+ statements.add(maker.Exec(callToSuper));
+
+ JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.<JCAnnotation>nil());
+ if (addConstructorProperties && !isLocalType(typeNode) && LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) {
+ addConstructorProperties(mods, typeNode, fieldsToParam);
+ }
+ return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"),
+ null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(),
+ maker.Block(0L, statements.toList()), null), source.get(), typeNode.getContext());
+ }
+
+ public static boolean isLocalType(JavacNode type) {
+ Kind kind = type.up().getKind();
+ if (kind == Kind.COMPILATION_UNIT) return false;
+ if (kind == Kind.TYPE) return isLocalType(type.up());
+ return true;
+ }
+
+ private static class SuperParameter {
+ private final String name;
+ private final Type type;
+
+ private SuperParameter(String name, Type type) {
+ this.name = name;
+ this.type = type;
+ }
+ }
+}