aboutsummaryrefslogtreecommitdiff
path: root/src/core/lombok/eclipse/handlers/HandleStandardException.java
diff options
context:
space:
mode:
authorReinier Zwitserloot <r.zwitserloot@projectlombok.org>2021-04-16 06:40:35 +0200
committerReinier Zwitserloot <r.zwitserloot@projectlombok.org>2021-04-16 06:40:35 +0200
commit415e303ac283df8d694b311f4f8df08f6e79cf0e (patch)
tree99c4e502fdb90db2e8e1e27ebf855b236ac41b59 /src/core/lombok/eclipse/handlers/HandleStandardException.java
parent2e212de523407c8d9f4471fea573c6c70164513b (diff)
downloadlombok-415e303ac283df8d694b311f4f8df08f6e79cf0e.tar.gz
lombok-415e303ac283df8d694b311f4f8df08f6e79cf0e.tar.bz2
lombok-415e303ac283df8d694b311f4f8df08f6e79cf0e.zip
[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.
Diffstat (limited to 'src/core/lombok/eclipse/handlers/HandleStandardException.java')
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/HandleStandardException.java243
1 files changed, 121 insertions, 122 deletions
diff --git a/src/core/lombok/eclipse/handlers/HandleStandardException.java b/src/core/lombok/eclipse/handlers/HandleStandardException.java
index e7f25edb..1a2542f4 100755
--- a/src/core/lombok/eclipse/handlers/HandleStandardException.java
+++ b/src/core/lombok/eclipse/handlers/HandleStandardException.java
@@ -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;
+ if (!isClass(typeNode)) {
+ annotationNode.addError("@StandardException is only supported on a class.");
+ return;
}
- return true;
+ AccessLevel access = annotation.getInstance().access();
+
+ generateNoArgsConstructor(typeNode, access, annotationNode);
+ generateMsgOnlyConstructor(typeNode, access, annotationNode);
+ generateCauseOnlyConstructor(typeNode, access, annotationNode);
+ generateFullConstructor(typeNode, access, annotationNode);
}
-
- public void generateConstructor(
- EclipseNode typeNode, AccessLevel level, List<SuperParameter> parameters, boolean skipIfConstructorExists,
- EclipseNode sourceNode) {
+
+ 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;
- generate(typeNode, level, parameters, skipIfConstructorExists, sourceNode);
+ 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);
}
- 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 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;
- }
- }
}