diff options
Diffstat (limited to 'src/core/lombok/eclipse')
3 files changed, 260 insertions, 5 deletions
diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index 753c489c..12a3c315 100755 --- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -250,6 +250,8 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH setGeneratedBy(hashCodeCacheDecl.type, source); } + private static final char[] HASH_CODE = "hashCode".toCharArray(), FLOAT_TO_INT_BITS = "floatToIntBits".toCharArray(), DOUBLE_TO_LONG_BITS = "doubleToLongBits".toCharArray(); + public MethodDeclaration createHashCode(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> members, boolean callSuper, boolean cacheHashCode, ASTNode source, FieldAccess fieldAccess) { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; @@ -373,7 +375,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH floatToIntBits.sourceStart = pS; floatToIntBits.sourceEnd = pE; setGeneratedBy(floatToIntBits, source); floatToIntBits.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA_LANG_FLOAT); - floatToIntBits.selector = "floatToIntBits".toCharArray(); + floatToIntBits.selector = FLOAT_TO_INT_BITS; floatToIntBits.arguments = new Expression[] { fieldAccessor }; statements.add(createResultCalculation(source, floatToIntBits)); } else if (Arrays.equals(TypeConstants.DOUBLE, token)) { @@ -382,7 +384,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH doubleToLongBits.sourceStart = pS; doubleToLongBits.sourceEnd = pE; setGeneratedBy(doubleToLongBits, source); doubleToLongBits.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA_LANG_DOUBLE); - doubleToLongBits.selector = "doubleToLongBits".toCharArray(); + doubleToLongBits.selector = DOUBLE_TO_LONG_BITS; doubleToLongBits.arguments = new Expression[] { fieldAccessor }; statements.add(createLocalDeclaration(source, dollarFieldName, TypeReference.baseTypeReference(TypeIds.T_long, 0), doubleToLongBits)); SingleNameReference copy1 = new SingleNameReference(dollarFieldName, p); @@ -406,7 +408,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH hashCodeCall.sourceStart = pS; hashCodeCall.sourceEnd = pE; setGeneratedBy(hashCodeCall, source); hashCodeCall.receiver = copy1; - hashCodeCall.selector = "hashCode".toCharArray(); + hashCodeCall.selector = HASH_CODE; NullLiteral nullLiteral = new NullLiteral(pS, pE); setGeneratedBy(nullLiteral, source); EqualExpression objIsNull = new EqualExpression(copy2, nullLiteral, OperatorIds.EQUAL_EQUAL); diff --git a/src/core/lombok/eclipse/handlers/HandleStandardException.java b/src/core/lombok/eclipse/handlers/HandleStandardException.java new file mode 100755 index 00000000..def6e495 --- /dev/null +++ b/src/core/lombok/eclipse/handlers/HandleStandardException.java @@ -0,0 +1,253 @@ +/* + * 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 + * 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.experimental.StandardException; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +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.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.handlers.EclipseHandlerUtil.*; + +@Provides +public class HandleStandardException extends EclipseAnnotationHandler<StandardException> { + @Override + public void handle(AnnotationValues<StandardException> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.STANDARD_EXCEPTION_FLAG_USAGE, "@StandardException"); + + EclipseNode typeNode = annotationNode.up(); + if (!isClass(typeNode)) { + annotationNode.addError("@StandardException is only supported on a class"); + return; + } + + TypeDeclaration classDef = (TypeDeclaration) typeNode.get(); + if (classDef.superclass == null) { + annotationNode.addError("@StandardException requires that you extend a Throwable type"); + return; + } + + AccessLevel access = annotation.getInstance().access(); + + generateNoArgsConstructor(typeNode, access, annotationNode); + generateMsgOnlyConstructor(typeNode, access, annotationNode); + generateCauseOnlyConstructor(typeNode, access, annotationNode); + generateFullConstructor(typeNode, access, annotationNode); + } + + 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 hasConstructor(EclipseNode node, Class<?>... paramTypes) { + node = upToTypeNode(node); + + 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 ((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[] 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 final char[][] JAVA_BEANS_CONSTRUCTORPROPERTIES = new char[][] { "java".toCharArray(), "beans".toCharArray(), "ConstructorProperties".toCharArray() }; + 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); + ann.declarationSourceEnd = pE; + + ArrayInitializer fieldNames = new ArrayInitializer(); + fieldNames.sourceStart = pS; + fieldNames.sourceEnd = pE; + fieldNames.expressions = new Expression[(msgParam && causeParam) ? 2 : 1]; + + int ctr = 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++; + } + + ann.memberValue = fieldNames; + setGeneratedBy(ann, source); + setGeneratedBy(ann.memberValue, source); + return new Annotation[] { ann }; + } + + @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) typeNode.get()); + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long) pS << 32 | pE; + + boolean addConstructorProperties; + if ((!msgParam && !causeParam) || isLocalType(typeNode)) { + 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)); + } + + 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 = pS; + constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = pE; + constructor.arguments = null; + + List<Argument> params = new ArrayList<Argument>(); + + 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); + } + 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) constructorProperties = createConstructorProperties(source, msgParam, causeParam); + 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; + } +} diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java index 72491277..171402b3 100644 --- a/src/core/lombok/eclipse/handlers/HandleToString.java +++ b/src/core/lombok/eclipse/handlers/HandleToString.java @@ -161,12 +161,12 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> { String typeName = getTypeName(type); boolean isEnum = type.isEnumType(); - + char[] suffix = ")".toCharArray(); String infixS = ", "; char[] infix = infixS.toCharArray(); int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long)pS << 32 | pE; + long p = (long) pS << 32 | pE; final int PLUS = OperatorIds.PLUS; String prefix; |