mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-11 03:13:38 +00:00
[pr 2702] finishing the @StandardException feature.
* rewritten how it works a bit: Now compatible with parent exceptions that don't have the Throwable variants. * rewritten how it works a bit: You can now provide the full constructor only; the rest will forward to it. * fixing up style. * rewrite the docs.
This commit is contained in:
11 files changed
+330
-265
No files matched your search
@@ -2,7 +2,7 @@ Lombok Changelog
|
||||
----------------
|
||||
|
||||
### v1.18.21 "Edgy Guinea Pig"
|
||||
* There is no edge release at the moment; 1.18.20 is the most recent version.
|
||||
* Added the `@StandardException` feature. [Pull Request #2702](https://github.com/rzwitserloot/lombok/pull/2702).
|
||||
|
||||
### v1.18.20 (April 2nd, 2021)
|
||||
* PLATFORM: JDK16 support added. [Issue #2681](https://github.com/rzwitserloot/lombok/issues/2681).
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2020 The Project Lombok Authors.
|
||||
* Copyright (C) 2021 The Project Lombok Authors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -26,129 +26,138 @@ import lombok.ConfigurationKeys;
|
||||
import lombok.experimental.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 lombok.spi.Provides;
|
||||
import org.eclipse.jdt.internal.compiler.ast.*;
|
||||
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
|
||||
|
||||
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.*;
|
||||
|
||||
@Provides
|
||||
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;
|
||||
EclipseNode typeNode = annotationNode.up();
|
||||
if (!isClass(typeNode)) {
|
||||
annotationNode.addError("@StandardException is only supported on a class.");
|
||||
return;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void generateConstructor(
|
||||
EclipseNode typeNode, AccessLevel level, List<SuperParameter> parameters, boolean skipIfConstructorExists,
|
||||
EclipseNode sourceNode) {
|
||||
AccessLevel access = annotation.getInstance().access();
|
||||
|
||||
generate(typeNode, level, parameters, skipIfConstructorExists, sourceNode);
|
||||
generateNoArgsConstructor(typeNode, access, annotationNode);
|
||||
generateMsgOnlyConstructor(typeNode, access, annotationNode);
|
||||
generateCauseOnlyConstructor(typeNode, access, annotationNode);
|
||||
generateFullConstructor(typeNode, access, annotationNode);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
private void generateNoArgsConstructor(EclipseNode typeNode, AccessLevel level, EclipseNode source) {
|
||||
if (hasConstructor(typeNode) != MemberExistsResult.NOT_EXISTS) return;
|
||||
int pS = source.get().sourceStart, pE = source.get().sourceEnd;
|
||||
|
||||
ExplicitConstructorCall explicitCall = new ExplicitConstructorCall(ExplicitConstructorCall.This);
|
||||
explicitCall.arguments = new Expression[] {new NullLiteral(pS, pE), new NullLiteral(pS, pE)};
|
||||
ConstructorDeclaration constructor = createConstructor(level, typeNode, false, false, source, explicitCall, null);
|
||||
injectMethod(typeNode, constructor);
|
||||
}
|
||||
|
||||
|
||||
private void generateMsgOnlyConstructor(EclipseNode typeNode, AccessLevel level, EclipseNode source) {
|
||||
if (hasConstructor(typeNode, String.class) != MemberExistsResult.NOT_EXISTS) return;
|
||||
int pS = source.get().sourceStart, pE = source.get().sourceEnd;
|
||||
long p = (long) pS << 32 | pE;
|
||||
|
||||
ExplicitConstructorCall explicitCall = new ExplicitConstructorCall(ExplicitConstructorCall.This);
|
||||
explicitCall.arguments = new Expression[] {new SingleNameReference(MESSAGE, p), new NullLiteral(pS, pE)};
|
||||
ConstructorDeclaration constructor = createConstructor(level, typeNode, true, false, source, explicitCall, null);
|
||||
injectMethod(typeNode, constructor);
|
||||
}
|
||||
|
||||
private void generateCauseOnlyConstructor(EclipseNode typeNode, AccessLevel level, EclipseNode source) {
|
||||
if (hasConstructor(typeNode, Throwable.class) != MemberExistsResult.NOT_EXISTS) return;
|
||||
int pS = source.get().sourceStart, pE = source.get().sourceEnd;
|
||||
long p = (long) pS << 32 | pE;
|
||||
|
||||
ExplicitConstructorCall explicitCall = new ExplicitConstructorCall(ExplicitConstructorCall.This);
|
||||
Expression causeNotNull = new EqualExpression(new SingleNameReference(CAUSE, p), new NullLiteral(pS, pE), OperatorIds.NOT_EQUAL);
|
||||
MessageSend causeDotGetMessage = new MessageSend();
|
||||
causeDotGetMessage.sourceStart = pS; causeDotGetMessage.sourceEnd = pE;
|
||||
causeDotGetMessage.receiver = new SingleNameReference(CAUSE, p);
|
||||
causeDotGetMessage.selector = GET_MESSAGE;
|
||||
Expression messageExpr = new ConditionalExpression(causeNotNull, causeDotGetMessage, new NullLiteral(pS, pE));
|
||||
explicitCall.arguments = new Expression[] {messageExpr, new SingleNameReference(CAUSE, p)};
|
||||
ConstructorDeclaration constructor = createConstructor(level, typeNode, false, true, source, explicitCall, null);
|
||||
injectMethod(typeNode, constructor);
|
||||
}
|
||||
|
||||
private void generateFullConstructor(EclipseNode typeNode, AccessLevel level, EclipseNode source) {
|
||||
if (hasConstructor(typeNode, String.class, Throwable.class) != MemberExistsResult.NOT_EXISTS) return;
|
||||
int pS = source.get().sourceStart, pE = source.get().sourceEnd;
|
||||
long p = (long) pS << 32 | pE;
|
||||
|
||||
ExplicitConstructorCall explicitCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super);
|
||||
explicitCall.arguments = new Expression[] {new SingleNameReference(MESSAGE, p)};
|
||||
Expression causeNotNull = new EqualExpression(new SingleNameReference(CAUSE, p), new NullLiteral(pS, pE), OperatorIds.NOT_EQUAL);
|
||||
MessageSend causeDotInitCause = new MessageSend();
|
||||
causeDotInitCause.sourceStart = pS; causeDotInitCause.sourceEnd = pE;
|
||||
causeDotInitCause.receiver = new SuperReference(pS, pE);
|
||||
causeDotInitCause.selector = INIT_CAUSE;
|
||||
causeDotInitCause.arguments = new Expression[] {new SingleNameReference(CAUSE, p)};
|
||||
IfStatement ifs = new IfStatement(causeNotNull, causeDotInitCause, pS, pE);
|
||||
ConstructorDeclaration constructor = createConstructor(level, typeNode, true, true, source, explicitCall, ifs);
|
||||
injectMethod(typeNode, constructor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a constructor with the provided parameters exists under the type node.
|
||||
*/
|
||||
public static MemberExistsResult constructorExists(EclipseNode node, List<SuperParameter> parameters) {
|
||||
public static MemberExistsResult hasConstructor(EclipseNode node, Class<?>... paramTypes) {
|
||||
node = upToTypeNode(node);
|
||||
SuperParameter[] parameterArray = parameters.toArray(new SuperParameter[0]);
|
||||
|
||||
|
||||
if (node != null && node.get() instanceof TypeDeclaration) {
|
||||
TypeDeclaration typeDecl = (TypeDeclaration)node.get();
|
||||
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;
|
||||
if ((def.bits & ASTNode.IsDefaultConstructor) != 0) continue;
|
||||
if (!paramsMatch(node, def.arguments, paramTypes)) 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;
|
||||
}
|
||||
|
||||
private static boolean paramsMatch(EclipseNode node, Argument[] a, Class<?>[] b) {
|
||||
if (a == null) return b == null || b.length == 0;
|
||||
if (b == null) return a.length == 0;
|
||||
if (a.length != b.length) return false;
|
||||
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
if (!typeMatches(b[i], node, a[i].type)) 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;
|
||||
private static final char[] MESSAGE = "message".toCharArray(), CAUSE = "cause".toCharArray(), GET_MESSAGE = "getMessage".toCharArray(), INIT_CAUSE = "initCause".toCharArray();
|
||||
|
||||
public static Annotation[] createConstructorProperties(ASTNode source, boolean msgParam, boolean causeParam) {
|
||||
if (!msgParam && !causeParam) 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);
|
||||
@@ -157,12 +166,16 @@ public class HandleStandardException extends EclipseAnnotationHandler<StandardEx
|
||||
ArrayInitializer fieldNames = new ArrayInitializer();
|
||||
fieldNames.sourceStart = pS;
|
||||
fieldNames.sourceEnd = pE;
|
||||
fieldNames.expressions = new Expression[fields.size()];
|
||||
fieldNames.expressions = new Expression[(msgParam && causeParam) ? 2 : 1];
|
||||
|
||||
int ctr = 0;
|
||||
for (SuperParameter field : fields) {
|
||||
char[] fieldName = field.name.toCharArray();
|
||||
fieldNames.expressions[ctr] = new StringLiteral(fieldName, pS, pE, 0);
|
||||
if (msgParam) {
|
||||
fieldNames.expressions[ctr] = new StringLiteral(MESSAGE, pS, pE, 0);
|
||||
setGeneratedBy(fieldNames.expressions[ctr], source);
|
||||
ctr++;
|
||||
}
|
||||
if (causeParam) {
|
||||
fieldNames.expressions[ctr] = new StringLiteral(CAUSE, pS, pE, 0);
|
||||
setGeneratedBy(fieldNames.expressions[ctr], source);
|
||||
ctr++;
|
||||
}
|
||||
@@ -172,59 +185,55 @@ public class HandleStandardException extends EclipseAnnotationHandler<StandardEx
|
||||
setGeneratedBy(ann.memberValue, source);
|
||||
return new Annotation[] { ann };
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") public static ConstructorDeclaration createConstructor(
|
||||
AccessLevel level, EclipseNode type, Collection<SuperParameter> parameters, EclipseNode sourceNode) {
|
||||
|
||||
@SuppressWarnings("deprecation") public static ConstructorDeclaration createConstructor(AccessLevel level, EclipseNode typeNode, boolean msgParam, boolean causeParam, EclipseNode sourceNode, ExplicitConstructorCall explicitCall, Statement extra) {
|
||||
ASTNode source = sourceNode.get();
|
||||
TypeDeclaration typeDeclaration = ((TypeDeclaration) type.get());
|
||||
|
||||
boolean isEnum = (((TypeDeclaration) type.get()).modifiers & ClassFileConstants.AccEnum) != 0;
|
||||
if (isEnum) level = AccessLevel.PRIVATE;
|
||||
|
||||
TypeDeclaration typeDeclaration = ((TypeDeclaration) typeNode.get());
|
||||
int pS = source.sourceStart, pE = source.sourceEnd;
|
||||
long p = (long) pS << 32 | pE;
|
||||
|
||||
boolean addConstructorProperties;
|
||||
if (parameters.isEmpty()) {
|
||||
if ((!msgParam && !causeParam) || isLocalType(typeNode)) {
|
||||
addConstructorProperties = false;
|
||||
} else {
|
||||
Boolean v = type.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES);
|
||||
Boolean v = typeNode.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));
|
||||
Boolean.FALSE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES));
|
||||
}
|
||||
|
||||
ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
|
||||
ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.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.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = pS;
|
||||
constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = pE;
|
||||
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);
|
||||
|
||||
if (msgParam) {
|
||||
TypeReference typeRef = new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[] {p, p, p});
|
||||
Argument parameter = new Argument(MESSAGE, p, typeRef, 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;
|
||||
|
||||
if (causeParam) {
|
||||
TypeReference typeRef = new QualifiedTypeReference(TypeConstants.JAVA_LANG_THROWABLE, new long[] {p, p, p});
|
||||
Argument parameter = new Argument(CAUSE, p, typeRef, Modifier.FINAL);
|
||||
params.add(parameter);
|
||||
}
|
||||
|
||||
explicitCall.sourceStart = pS;
|
||||
explicitCall.sourceEnd = pE;
|
||||
constructor.constructorCall = explicitCall;
|
||||
constructor.statements = extra != null ? new Statement[] {extra} : null;
|
||||
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);
|
||||
|
||||
if (addConstructorProperties) constructorProperties = createConstructorProperties(source, msgParam, causeParam);
|
||||
constructor.annotations = copyAnnotations(source, constructorProperties);
|
||||
constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope);
|
||||
return constructor;
|
||||
}
|
||||
@@ -235,14 +244,4 @@ public class HandleStandardException extends EclipseAnnotationHandler<StandardEx
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2017 The Project Lombok Authors.
|
||||
* Copyright (C) 2021 The Project Lombok Authors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -26,9 +26,25 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
|
||||
/**
|
||||
* Put on any class that extends some {@code java.lang.Throwable} type to add the 4 common exception constructors.
|
||||
*
|
||||
* Specifically, all 4 constructors derived from the combinatorial explosion of {@code String message} and {@code Throwable cause}.
|
||||
* You may write any or all of these 4 constructors by hand; lombok will only generate the missing ones.
|
||||
* <p>
|
||||
* All but the full {@code (String message, Throwable cause)} constructor are implemented as a {@code this(msg, cause)} call; it is therefore
|
||||
* possibly to write code to run on construction by writing just the {@code (String message, Throwable cause)} constructor.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface StandardException {
|
||||
/**
|
||||
* Sets the access level of the generated constuctors. By default, generated constructors are {@code public}.
|
||||
* Note: This does nothing if you write your own constructors (we won't change their access levels).
|
||||
*
|
||||
* @return The constructors will be generated with this access modifier.
|
||||
*/
|
||||
AccessLevel access() default lombok.AccessLevel.PUBLIC;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2019 The Project Lombok Authors.
|
||||
* Copyright (C) 2021 The Project Lombok Authors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -42,174 +42,171 @@ import lombok.javac.handlers.JavacHandlerUtil.*;
|
||||
import lombok.spi.Provides;
|
||||
|
||||
import static lombok.core.handlers.HandlerUtil.handleFlagUsage;
|
||||
import static lombok.javac.Javac.CTC_VOID;
|
||||
import static lombok.javac.Javac.*;
|
||||
import static lombok.javac.handlers.JavacHandlerUtil.*;
|
||||
|
||||
@Provides
|
||||
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);
|
||||
deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
|
||||
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;
|
||||
if (!isClass(typeNode)) {
|
||||
annotationNode.addError("@StandardException is only supported on a class.");
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
AccessLevel access = annotation.getInstance().access();
|
||||
if (access == null) access = AccessLevel.PUBLIC;
|
||||
if (access == AccessLevel.NONE) {
|
||||
annotationNode.addError("AccessLevel.NONE is not valid here");
|
||||
access = AccessLevel.PUBLIC;
|
||||
}
|
||||
|
||||
generateNoArgsConstructor(typeNode, access, annotationNode);
|
||||
generateMsgOnlyConstructor(typeNode, access, annotationNode);
|
||||
generateCauseOnlyConstructor(typeNode, access, annotationNode);
|
||||
generateFullConstructor(typeNode, access, annotationNode);
|
||||
}
|
||||
|
||||
public static MemberExistsResult constructorExists(JavacNode node, List<SuperParameter> parameters) {
|
||||
|
||||
private void generateNoArgsConstructor(JavacNode typeNode, AccessLevel level, JavacNode source) {
|
||||
if (hasConstructor(typeNode) != MemberExistsResult.NOT_EXISTS) return;
|
||||
JavacTreeMaker maker = typeNode.getTreeMaker();
|
||||
|
||||
List<JCExpression> args = List.<JCExpression>of(maker.Literal(CTC_BOT, null), maker.Literal(CTC_BOT, null));
|
||||
JCStatement thisCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(typeNode.toName("this")), args));
|
||||
JCMethodDecl constr = createConstructor(level, typeNode, false, false, source, List.of(thisCall));
|
||||
injectMethod(typeNode, constr, List.<Type>nil(), Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID));
|
||||
}
|
||||
|
||||
private void generateMsgOnlyConstructor(JavacNode typeNode, AccessLevel level, JavacNode source) {
|
||||
if (hasConstructor(typeNode, String.class) != MemberExistsResult.NOT_EXISTS) return;
|
||||
JavacTreeMaker maker = typeNode.getTreeMaker();
|
||||
|
||||
List<JCExpression> args = List.<JCExpression>of(maker.Ident(typeNode.toName("message")), maker.Literal(CTC_BOT, null));
|
||||
JCStatement thisCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(typeNode.toName("this")), args));
|
||||
JCMethodDecl constr = createConstructor(level, typeNode, true, false, source, List.of(thisCall));
|
||||
injectMethod(typeNode, constr, List.<Type>of(typeNode.getSymbolTable().stringType), Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID));
|
||||
}
|
||||
|
||||
private void generateCauseOnlyConstructor(JavacNode typeNode, AccessLevel level, JavacNode source) {
|
||||
if (hasConstructor(typeNode, Throwable.class) != MemberExistsResult.NOT_EXISTS) return;
|
||||
JavacTreeMaker maker = typeNode.getTreeMaker();
|
||||
Name causeName = typeNode.toName("cause");
|
||||
|
||||
JCExpression causeDotGetMessage = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(causeName), typeNode.toName("getMessage")), List.<JCExpression>nil());
|
||||
JCExpression msgExpression = maker.Conditional(maker.Binary(CTC_NOT_EQUAL, maker.Ident(causeName), maker.Literal(CTC_BOT, null)), causeDotGetMessage, maker.Literal(CTC_BOT, null));
|
||||
|
||||
List<JCExpression> args = List.<JCExpression>of(msgExpression, maker.Ident(causeName));
|
||||
JCStatement thisCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(typeNode.toName("this")), args));
|
||||
JCMethodDecl constr = createConstructor(level, typeNode, false, true, source, List.of(thisCall));
|
||||
injectMethod(typeNode, constr, List.<Type>of(typeNode.getSymbolTable().throwableType), Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID));
|
||||
}
|
||||
|
||||
private void generateFullConstructor(JavacNode typeNode, AccessLevel level, JavacNode source) {
|
||||
if (hasConstructor(typeNode, String.class, Throwable.class) != MemberExistsResult.NOT_EXISTS) return;
|
||||
JavacTreeMaker maker = typeNode.getTreeMaker();
|
||||
|
||||
Name causeName = typeNode.toName("cause");
|
||||
Name superName = typeNode.toName("super");
|
||||
|
||||
List<JCExpression> args = List.<JCExpression>of(maker.Ident(typeNode.toName("message")));
|
||||
JCStatement superCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(superName), args));
|
||||
JCExpression causeNotNull = maker.Binary(CTC_NOT_EQUAL, maker.Ident(causeName), maker.Literal(CTC_BOT, null));
|
||||
JCStatement initCauseCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(superName), typeNode.toName("initCause")), List.<JCExpression>of(maker.Ident(causeName))));
|
||||
JCStatement initCause = maker.If(causeNotNull, initCauseCall, null);
|
||||
JCMethodDecl constr = createConstructor(level, typeNode, true, true, source, List.of(superCall, initCause));
|
||||
injectMethod(typeNode, constr, List.<Type>of(typeNode.getSymbolTable().stringType, typeNode.getSymbolTable().throwableType), Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID));
|
||||
}
|
||||
|
||||
private static MemberExistsResult hasConstructor(JavacNode node, Class<?>... paramTypes) {
|
||||
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;
|
||||
if (!paramsMatch(node, md.params, paramTypes)) 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;
|
||||
}
|
||||
|
||||
private static boolean paramsMatch(JavacNode node, List<JCVariableDecl> a, Class<?>[] b) {
|
||||
if (a == null) return b == null || b.length == 0;
|
||||
if (b == null) return a.size() == 0;
|
||||
if (a.size() != b.length) return false;
|
||||
|
||||
for (int i = 0; i < a.size(); i++) {
|
||||
JCVariableDecl param = a.get(i);
|
||||
Class<?> c = b[i];
|
||||
if (!typeMatches(c, node, param.vartype)) return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void addConstructorProperties(JCModifiers mods, JavacNode node, List<SuperParameter> fields) {
|
||||
if (fields.isEmpty()) return;
|
||||
private static void addConstructorProperties(JCModifiers mods, JavacNode node, boolean msgParam, boolean causeParam) {
|
||||
if (!msgParam && !causeParam) 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()));
|
||||
}
|
||||
if (msgParam) fieldNames.append(maker.Literal("message"));
|
||||
if (causeParam) fieldNames.append(maker.Literal("cause"));
|
||||
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) {
|
||||
@SuppressWarnings("deprecation") private static JCMethodDecl createConstructor(AccessLevel level, JavacNode typeNode, boolean msgParam, boolean causeParam, JavacNode source, List<JCStatement> statements) {
|
||||
JavacTreeMaker maker = typeNode.getTreeMaker();
|
||||
|
||||
boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0;
|
||||
if (isEnum) level = AccessLevel.PRIVATE;
|
||||
|
||||
boolean addConstructorProperties;
|
||||
if (fieldsToParam.isEmpty()) {
|
||||
if ((!msgParam && !causeParam) || isLocalType(typeNode) || !LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) {
|
||||
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);
|
||||
ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
|
||||
|
||||
if (msgParam) {
|
||||
Name fieldName = typeNode.toName("message");
|
||||
long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
|
||||
JCExpression pType = maker.getUnderlyingTreeMaker().Ident(fieldNode.type.tsym);
|
||||
JCExpression pType = genJavaLangTypeRef(typeNode, "String");
|
||||
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));
|
||||
|
||||
|
||||
if (causeParam) {
|
||||
Name fieldName = typeNode.toName("cause");
|
||||
long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
|
||||
JCExpression pType = genJavaLangTypeRef(typeNode, "Throwable");
|
||||
JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), fieldName, pType, null);
|
||||
params.append(param);
|
||||
}
|
||||
|
||||
JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.<JCAnnotation>nil());
|
||||
if (addConstructorProperties && !isLocalType(typeNode) && LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) {
|
||||
addConstructorProperties(mods, typeNode, fieldsToParam);
|
||||
}
|
||||
if (addConstructorProperties) addConstructorProperties(mods, typeNode, msgParam, causeParam);
|
||||
return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"),
|
||||
null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(),
|
||||
maker.Block(0L, statements.toList()), null), source);
|
||||
maker.Block(0L, statements), null), source);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,36 @@
|
||||
class EmptyException extends Exception {
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public EmptyException() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public EmptyException(final String message) {
|
||||
public EmptyException(final java.lang.String message) {
|
||||
this(message, null);
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public EmptyException(final java.lang.Throwable cause) {
|
||||
this(cause != null ? cause.getMessage() : null, cause);
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public EmptyException(final java.lang.String message, final java.lang.Throwable cause) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public EmptyException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public EmptyException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
if (cause != null) super.initCause(cause);
|
||||
}
|
||||
}
|
||||
class NoArgsException extends Exception {
|
||||
public NoArgsException() {
|
||||
}
|
||||
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public NoArgsException(final String message) {
|
||||
protected NoArgsException(final java.lang.String message) {
|
||||
this(message, null);
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
protected NoArgsException(final java.lang.Throwable cause) {
|
||||
this(cause != null ? cause.getMessage() : null, cause);
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
protected NoArgsException(final java.lang.String message, final java.lang.Throwable cause) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public NoArgsException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public NoArgsException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
if (cause != null) super.initCause(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import lombok.AccessLevel;
|
||||
import lombok.experimental.StandardException;
|
||||
@StandardException class EmptyException extends Exception {
|
||||
public @java.lang.SuppressWarnings("all") EmptyException() {
|
||||
this(null, null);
|
||||
}
|
||||
public @java.lang.SuppressWarnings("all") EmptyException(final java.lang.String message) {
|
||||
this(message, null);
|
||||
}
|
||||
public @java.lang.SuppressWarnings("all") EmptyException(final java.lang.Throwable cause) {
|
||||
this(((cause != null) ? cause.getMessage() : null), cause);
|
||||
}
|
||||
public @java.lang.SuppressWarnings("all") EmptyException(final java.lang.String message, final java.lang.Throwable cause) {
|
||||
super(message);
|
||||
if ((cause != null))
|
||||
super.initCause(cause);
|
||||
}
|
||||
}
|
||||
@StandardException(access = AccessLevel.PROTECTED) class NoArgsException extends Exception {
|
||||
public NoArgsException() {
|
||||
super();
|
||||
}
|
||||
protected @java.lang.SuppressWarnings("all") NoArgsException(final java.lang.String message) {
|
||||
this(message, null);
|
||||
}
|
||||
protected @java.lang.SuppressWarnings("all") NoArgsException(final java.lang.Throwable cause) {
|
||||
this(((cause != null) ? cause.getMessage() : null), cause);
|
||||
}
|
||||
protected @java.lang.SuppressWarnings("all") NoArgsException(final java.lang.String message, final java.lang.Throwable cause) {
|
||||
super(message);
|
||||
if ((cause != null))
|
||||
super.initCause(cause);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import lombok.AccessLevel;
|
||||
import lombok.experimental.StandardException;
|
||||
|
||||
@StandardException class EmptyException extends Exception {
|
||||
}
|
||||
@StandardException class NoArgsException extends Exception {
|
||||
@StandardException(access = AccessLevel.PROTECTED) class NoArgsException extends Exception {
|
||||
public NoArgsException() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
4 The serializable class EmptyException does not declare a static final serialVersionUID field of type long
|
||||
6 The serializable class NoArgsException does not declare a static final serialVersionUID field of type long
|
||||
@@ -10,9 +10,19 @@
|
||||
|
||||
<@f.overview>
|
||||
<p>
|
||||
This annotation is intended to be used on subclasses of <code>java.util.Throwable</code>. For each of the four constructors in <code>Throwable</code>, it will generate a corresponding constructor in the target class, that simply forwards its argument to its super-counterpart.
|
||||
Put this annotation on your own exception types (new classes that <code>extends Exception</code> or anything else that inherits from <code>Throwable</code>. This annotation will then generate up to 4 constructors:
|
||||
</p><ul>
|
||||
<li>A no-args constructor (<code>MyException()</code>), representing no message, and no cause.</li>
|
||||
<li>A message-only constructor (<code>MyException(String message)</code>), representing the provided message, and no cause.</li>
|
||||
<li>A cause-only constructor (<code>MyException(Throwable cause)</code>), which will copy the message from the cause, if there is one, and uses the provided cause.</li>
|
||||
<li>A full constructor (<code>MyException(String message, Throwable cause)</code>).</li>
|
||||
</ul>
|
||||
<p>
|
||||
Each constructor forwards to the full constructor; you can write any or all of these constructors manually in which case lombok
|
||||
will not generate it. The full constructor, if it needs to be generated, will invoke <code>super(message);</code> and will then
|
||||
invoke <code>super.initCause(cause);</code> if the cause is not null.
|
||||
</p><p>
|
||||
If any of those constructors is manually overriden, it is simply skipped. This allows applying special treatment such as annotations.
|
||||
There are few reasons <em>not</em> to put this annotation on all your custom exceptions.
|
||||
</p>
|
||||
</@f.overview>
|
||||
|
||||
@@ -30,7 +40,14 @@
|
||||
|
||||
<@f.smallPrint>
|
||||
<p>
|
||||
Although such situation is unlikely to occur, this annotation can technically be applied to any class for which all four expected constructors are defined in a superclass.
|
||||
Lombok will not check if you extend an actual exception type.
|
||||
</p><p>
|
||||
Lombok does not require that the class you inherit from has the <code>Throwable cause</code> variants, because not all exceptions
|
||||
have these. However, the `<code>Parent(String message)</code>` constructor as well as the no-args constructor <em>must</em> exist.
|
||||
</p><p>
|
||||
There is a very slight functional difference: Normally, invoking <code>new SomeException(message, null)</code> will initialize
|
||||
the cause to be <em>no cause</em>, and this cannot be later changed by invoking <code>initCause</code>. However, lombok's
|
||||
standard exceptions <strong>do</strong> let you overwrite an explicit no-cause with <code>initCause</code> later.
|
||||
</p>
|
||||
</@f.smallPrint>
|
||||
</@f.scaffold>
|
||||
@@ -77,7 +77,7 @@
|
||||
</@main.feature>
|
||||
|
||||
<@main.feature title="@StandardException" href="StandardException">
|
||||
TODO
|
||||
Standard.. Exceptional? This is not just an oxymoron, it's convenient!
|
||||
</@main.feature>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
public class ExampleException extends Exception {
|
||||
public ExampleException() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
public ExampleException(String message) {
|
||||
super(message);
|
||||
this(message, null);
|
||||
}
|
||||
|
||||
public ExampleException(Throwable cause) {
|
||||
super(cause);
|
||||
this(cause != null ? cause.getMessage() : null, cause);
|
||||
}
|
||||
|
||||
public ExampleException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
super(message);
|
||||
if (cause != null) super.initCause(cause);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user