diff options
Diffstat (limited to 'src/core/lombok/eclipse/handlers')
6 files changed, 181 insertions, 3 deletions
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 58e18699..d85c2ee8 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -39,6 +39,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.eclipse.jdt.core.Signature; +import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; @@ -94,6 +96,7 @@ import org.eclipse.jdt.internal.compiler.ast.TypeParameter; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.CaptureBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; @@ -103,6 +106,7 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding; +import org.eclipse.jdt.internal.core.CompilationUnit; import lombok.AccessLevel; import lombok.ConfigurationKeys; @@ -2628,4 +2632,150 @@ public class EclipseHandlerUtil { public static TypeReference createTypeReference(String typeName, ASTNode source) { return generateQualifiedTypeRef(source, fromQualifiedName(typeName)); } + + public static String getDocComment(CompilationUnitDeclaration cud, ASTNode node) { + ICompilationUnit compilationUnit = cud.compilationResult.compilationUnit; + if (node instanceof FieldDeclaration) { + FieldDeclaration fieldDeclaration = (FieldDeclaration) node; + char[] rawContent = CharOperation.subarray(compilationUnit.getContents(), fieldDeclaration.declarationSourceStart, fieldDeclaration.declarationSourceEnd); + String rawContentString = new String(rawContent); + int startIndex = rawContentString.indexOf("/**"); + int endIndex = rawContentString.indexOf("*/"); + if (startIndex != -1 && endIndex != -1) { + /* Remove all leading asterisks */ + return rawContentString.substring(startIndex + 3, endIndex).replaceAll("(?m)^\\s*\\* ?", "").trim(); + } + } + return null; + } + + public static void setDocComment(CompilationUnitDeclaration cud, EclipseNode eclipseNode, String doc) { + setDocComment(cud, (TypeDeclaration) upToTypeNode(eclipseNode).get(), eclipseNode.get(), doc); + } + + public static void setDocComment(CompilationUnitDeclaration cud, TypeDeclaration type, ASTNode node, String doc) { + if (cud.compilationResult.compilationUnit instanceof CompilationUnit) { + CompilationUnit compilationUnit = (CompilationUnit) cud.compilationResult.compilationUnit; + Map<String, String> docs = CompilationUnit_javadoc.setIfAbsent(compilationUnit, new HashMap<String, String>()); + + if (node instanceof AbstractMethodDeclaration) { + AbstractMethodDeclaration methodDeclaration = (AbstractMethodDeclaration) node; + String signature = getSignature(type, methodDeclaration); + /* Add javadoc start marker, add leading asterisks to each line, add javadoc end marker */ + docs.put(signature, String.format("/**%n%s%n */", doc.replaceAll("(?m)^", " * "))); + } + } + } + + public static String getSignature(TypeDeclaration type, AbstractMethodDeclaration methodDeclaration) { + StringBuilder sb = new StringBuilder(); + sb.append(type.name); + sb.append("."); + sb.append(methodDeclaration.selector); + sb.append("("); + Argument[] arguments = methodDeclaration.arguments; + if (arguments != null) { + for (Argument argument : arguments) { + String signature = Signature.createTypeSignature(argument.type.getLastToken(), false); + sb.append(signature); + } + } + sb.append(")"); + return sb.toString(); + } + + public static enum CopyJavadoc { + VERBATIM { + @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { + return getDocComment(cu, node.get()); + } + }, + GETTER { + @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { + final ASTNode n = node.get(); + String javadoc = getDocComment(cu, n); + // step 1: Check if there is a 'GETTER' section. If yes, that becomes the new method's javadoc. + String out = getJavadocSection(javadoc, "GETTER"); + final boolean sectionBased = out != null; + if (!sectionBased) { + out = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), "@param(?:eter)?\\s+.*"); + } + return out; + } + }, + SETTER { + @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { + return applySetter(cu, node, "SETTER"); + } + }, + WITH { + @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { + return addReturnsUpdatedSelfIfNeeded(applySetter(cu, node, "WITH|WITHER")); + } + }, + WITH_BY { + @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { + return applySetter(cu, node, "WITHBY|WITH_BY"); + } + }; + + public abstract String apply(final CompilationUnitDeclaration cu, final EclipseNode node); + + private static String applySetter(final CompilationUnitDeclaration cu, EclipseNode node, String sectionName) { + final ASTNode n = node.get(); + String javadoc = getDocComment(cu, n); + // step 1: Check if there is a 'SETTER' section. If yes, that becomes the new method's javadoc. + String out = getJavadocSection(javadoc, sectionName); + final boolean sectionBased = out != null; + if (!sectionBased) { + out = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), "@returns?\\s+.*"); + } + return shouldReturnThis(node) ? addReturnsThisIfNeeded(out) : out; + } + } + + /** + * Copies javadoc on one node to the other. + * + * This one is a shortcut for {@link EclipseHandlerUtil#copyJavadoc(EclipseNode, ASTNode, TypeDeclaration, CopyJavadoc, boolean)} + * if source and target node are in the same type. + */ + public static void copyJavadoc(EclipseNode from, ASTNode to, CopyJavadoc copyMode) { + copyJavadoc(from, to, (TypeDeclaration) upToTypeNode(from).get(), copyMode, false); + } + + /** + * Copies javadoc on one node to the other. + * + * This one is a shortcut for {@link EclipseHandlerUtil#copyJavadoc(EclipseNode, ASTNode, TypeDeclaration, CopyJavadoc, boolean)} + * if source and target node are in the same type. + */ + public static void copyJavadoc(EclipseNode from, ASTNode to, CopyJavadoc copyMode, boolean forceAddReturn) { + copyJavadoc(from, to, (TypeDeclaration) upToTypeNode(from).get(), copyMode, forceAddReturn); + } + + public static void copyJavadoc(EclipseNode from, ASTNode to, TypeDeclaration type, CopyJavadoc copyMode) { + copyJavadoc(from, to, type, copyMode, false); + } + + /** + * Copies javadoc on one node to the other. + * + * in 'GETTER' copyMode, first a 'GETTER' segment is searched for. If it exists, that will become the javadoc for the 'to' node, and this section is + * stripped out of the 'from' node. If no 'GETTER' segment is found, then the entire javadoc is taken minus any {@code @param} lines and other sections. + * any {@code @return} lines are stripped from 'from'. + * + * in 'SETTER' mode, stripping works similarly to 'GETTER' mode, except {@code param} are copied and stripped from the original and {@code @return} are skipped. + */ + public static void copyJavadoc(EclipseNode from, ASTNode to, TypeDeclaration type, CopyJavadoc copyMode, boolean forceAddReturn) { + if (copyMode == null) copyMode = CopyJavadoc.VERBATIM; + try { + CompilationUnitDeclaration cud = ((CompilationUnitDeclaration) from.top().get()); + String newJavadoc = copyMode.apply(cud, from); + if (newJavadoc != null) { + if (forceAddReturn) newJavadoc = addReturnsThisIfNeeded(newJavadoc); + setDocComment(cud, type, to, newJavadoc); + } + } catch (Exception ignore) {} + } } diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 801fe7e7..9e6c28e8 100755 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -21,8 +21,8 @@ */ package lombok.eclipse.handlers; -import static lombok.eclipse.Eclipse.*; import static lombok.core.handlers.HandlerUtil.*; +import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.lang.reflect.Modifier; @@ -30,6 +30,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -84,14 +86,16 @@ import lombok.ConfigurationKeys; import lombok.Singular; import lombok.ToString; import lombok.core.AST.Kind; -import lombok.core.handlers.HandlerUtil; -import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.core.configuration.CheckerFrameworkVersion; +import lombok.core.handlers.HandlerUtil; +import lombok.core.handlers.HandlerUtil.FieldAccess; +import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.eclipse.handlers.EclipseHandlerUtil.CopyJavadoc; import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; @@ -965,9 +969,29 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { typeReference.annotations[0] = new Annotation[] {ann}; setter.receiver = new Receiver(new char[] { 't', 'h', 'i', 's' }, 0, typeReference, null, Modifier.FINAL); } + if (sourceNode.up().getKind() == Kind.METHOD) { + copyJavadocFromParam(originalFieldNode.up(), setter, td, paramName.toString()); + } else { + copyJavadoc(originalFieldNode, setter, td, CopyJavadoc.SETTER, true); + } injectMethod(builderType, setter); } + private void copyJavadocFromParam(EclipseNode from, MethodDeclaration to, TypeDeclaration type, String param) { + try { + CompilationUnitDeclaration cud = (CompilationUnitDeclaration) from.top().get(); + String methodComment = getDocComment(cud, from.get()); + if (methodComment == null) return; + + Pattern pattern = Pattern.compile("@param " + param + " (\\S|\\s)+?(?=^ ?@)", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(methodComment); + if (matcher.find()) { + String newJavadoc = addReturnsThisIfNeeded(matcher.group()); + setDocComment(cud, type, to, newJavadoc); + } + } catch (Exception ignore) {} + } + public EclipseNode makeBuilderClass(boolean isStatic, EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source, AccessLevel access) { TypeDeclaration parent = (TypeDeclaration) tdParent.get(); TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java index 9cd1e2a1..4d7d84ce 100644 --- a/src/core/lombok/eclipse/handlers/HandleGetter.java +++ b/src/core/lombok/eclipse/handlers/HandleGetter.java @@ -296,6 +296,7 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> { } method.traverse(new SetGeneratedByVisitor(source), parent.scope); + copyJavadoc(fieldNode, method, CopyJavadoc.GETTER); return method; } diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index cb2ca3bf..83c6eef2 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -263,6 +263,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> { if (returnType != null && returnStatement != null) createRelevantNonNullAnnotation(sourceNode, method); method.traverse(new SetGeneratedByVisitor(source), parent.scope); + copyJavadoc(fieldNode, method, CopyJavadoc.SETTER, returnStatement != null); return method; } } diff --git a/src/core/lombok/eclipse/handlers/HandleWith.java b/src/core/lombok/eclipse/handlers/HandleWith.java index 83357710..dce193d8 100644 --- a/src/core/lombok/eclipse/handlers/HandleWith.java +++ b/src/core/lombok/eclipse/handlers/HandleWith.java @@ -290,6 +290,7 @@ public class HandleWith extends EclipseAnnotationHandler<With> { EclipseHandlerUtil.createRelevantNonNullAnnotation(fieldNode, method); method.traverse(new SetGeneratedByVisitor(source), parent.scope); + copyJavadoc(fieldNode, method, CopyJavadoc.WITH); return method; } } diff --git a/src/core/lombok/eclipse/handlers/HandleWithBy.java b/src/core/lombok/eclipse/handlers/HandleWithBy.java index 5f229aaf..f56004f6 100644 --- a/src/core/lombok/eclipse/handlers/HandleWithBy.java +++ b/src/core/lombok/eclipse/handlers/HandleWithBy.java @@ -373,6 +373,7 @@ public class HandleWithBy extends EclipseAnnotationHandler<WithBy> { createRelevantNonNullAnnotation(fieldNode, method); method.traverse(new SetGeneratedByVisitor(source), parent.scope); + copyJavadoc(fieldNode, method, CopyJavadoc.WITH_BY); return method; } } |