diff options
-rw-r--r-- | doc/changelog.markdown | 1 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleGetter.java | 1 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleSetter.java | 4 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/JavacHandlerUtil.java | 91 | ||||
-rw-r--r-- | usage_examples/GetterSetterExample_post.jpage | 22 | ||||
-rw-r--r-- | usage_examples/GetterSetterExample_pre.jpage | 14 | ||||
-rw-r--r-- | website/features/GetterSetter.html | 2 |
7 files changed, 134 insertions, 1 deletions
diff --git a/doc/changelog.markdown b/doc/changelog.markdown index c9dafc61..75825d77 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -2,6 +2,7 @@ Lombok Changelog ---------------- ### v0.11.9 (Edgy Guinea Pig) +* FEATURE: javadoc on fields will now be copied to generated getters / setters. There are ways to specify separate javadoc for the field, the setter, and the getter, and `@param` and `@return` are handled appropriately. Addresses feature request [Issue #59](https://code.google.com/p/projectlombok/issues/detail?id=59). [@Getter and @Setter documentation](http://projectlombok.org/features/GetterSetter.html). * CHANGE: The desugaring of @Getter(lazy=true) is now less object creation intensive. Documentation has been updated to reflect what the new desugaring looks like. [@Getter(lazy=true) documentation](http://projectlombok.org/features/GetterLazy.html). * PROMOTION: `@Value` has been promoted from experimental to the main package with no changes. The 'old' experimental one is still around but is deprecated, and is an alias for the new main package one. [@Value documentation](http://projectlombok.org/features/Value.html). * FEATURE: {Experimental} `@Builder` support. One of our earliest feature request issues, [Issue #16](https://code.google.com/p/projectlombok/issues/detail?id=16), has finally been addressed. [@Builder documentation](http://projectlombok.org/features/experimental/Builder.html). diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index 413404c0..51642f86 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -253,6 +253,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { if (toClearOfMarkers != null) recursiveSetGeneratedBy(toClearOfMarkers, null); decl.mods.annotations = decl.mods.annotations.appendList(delegates); + copyJavadoc(field, decl, CopyJavadoc.GETTER); return decl; } diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index 29728eae..282e6c2f 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -252,8 +252,10 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(chainDots(field, "java", "lang", "Deprecated"), List.<JCExpression>nil())); } - return recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType, + JCMethodDecl decl = recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType, methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source); + copyJavadoc(field, decl, CopyJavadoc.SETTER); + return decl; } private static class JCNoType extends Type implements NoType { diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 1784be90..87493e39 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -31,6 +31,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; +import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.AccessLevel; @@ -1163,4 +1164,94 @@ public class JavacHandlerUtil { // This is somewhat unsafe, but it's better than outright throwing an exception here. Returning null will just cause an exception down the pipeline. return (JCExpression) in; } + + private static final Pattern SECTION_FINDER = Pattern.compile("^\\s*\\**\\s*[-*][-*]+\\s*(GETTER|SETTER)\\s*[-*][-*]+\\s*\\**\\s*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + + private static String stripLinesWithTagFromJavadoc(String javadoc, String regexpFragment) { + Pattern p = Pattern.compile("^\\s*\\**\\s*" + regexpFragment + "\\s*\\**\\s*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(javadoc); + return m.replaceAll(""); + } + + private static String[] splitJavadocOnSectionIfPresent(String javadoc, String sectionName) { + Matcher m = SECTION_FINDER.matcher(javadoc); + int getterSectionHeaderStart = -1; + int getterSectionStart = -1; + int getterSectionEnd = -1; + while (m.find()) { + if (m.group(1).equalsIgnoreCase(sectionName)) { + getterSectionStart = m.end() + 1; + getterSectionHeaderStart = m.start(); + } else if (getterSectionStart != -1) { + getterSectionEnd = m.start(); + } + } + + if (getterSectionStart != -1) { + if (getterSectionEnd != -1) { + return new String[] {javadoc.substring(getterSectionStart, getterSectionEnd), javadoc.substring(0, getterSectionHeaderStart) + javadoc.substring(getterSectionEnd)}; + } else { + return new String[] {javadoc.substring(getterSectionStart), javadoc.substring(0, getterSectionHeaderStart)}; + } + } + + return null; + } + + public static enum CopyJavadoc { + VERBATIM, GETTER { + @Override public String[] split(String javadoc) { + // step 1: Check if there is a 'GETTER' section. If yes, that becomes the new one and we strip that from the original. + String[] out = splitJavadocOnSectionIfPresent(javadoc, "GETTER"); + if (out != null) return out; + // failing that, create a copy, but strip @return from the original and @param from the copy. + String copy = javadoc; + javadoc = stripLinesWithTagFromJavadoc(javadoc, "@returns?\\s+.*"); + copy = stripLinesWithTagFromJavadoc(copy, "@param(?:eter)?\\s+.*"); + return new String[] {copy, javadoc}; + } + }, + SETTER { + @Override public String[] split(String javadoc) { + // step 1: Check if there is a 'SETTER' section. If yes, that becomes the new one and we strip that from the original. + String[] out = splitJavadocOnSectionIfPresent(javadoc, "SETTER"); + if (out != null) return out; + // failing that, create a copy, but strip @param from the original and @return from the copy. + String copy = javadoc; + javadoc = stripLinesWithTagFromJavadoc(javadoc, "@param(?:eter)?\\s+.*"); + copy = stripLinesWithTagFromJavadoc(copy, "@returns?\\s+.*"); + return new String[] {copy, javadoc}; + } + }; + + /** Splits the javadoc into the section to be copied (ret[0]) and the section to replace the original with (ret[1]) */ + public String[] split(String javadoc) { + return new String[] {javadoc, javadoc}; + } + } + + /** + * 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. 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(JavacNode from, JCTree to, CopyJavadoc copyMode) { + if (copyMode == null) copyMode = CopyJavadoc.VERBATIM; + try { + JCCompilationUnit cu = ((JCCompilationUnit) from.top().get()); + if (cu.docComments != null) { + String javadoc = cu.docComments.get(from.get()); + + if (javadoc != null) { + String[] filtered = copyMode.split(javadoc); + cu.docComments.put(to, filtered[0]); + cu.docComments.put(from.get(), filtered[1]); + } + } + } catch (Exception ignore) {} + } } diff --git a/usage_examples/GetterSetterExample_post.jpage b/usage_examples/GetterSetterExample_post.jpage index b99ba7e1..241a3a4e 100644 --- a/usage_examples/GetterSetterExample_post.jpage +++ b/usage_examples/GetterSetterExample_post.jpage @@ -1,19 +1,41 @@ public class GetterSetterExample { + /** + * Age of the person. Water is wet. + */ private int age = 10; + + /** + * Name of the person. + */ private String name; @Override public String toString() { return String.format("%s (age: %d)", name, age); } + /** + * Age of the person. Water is wet. + * + * @return The current value of this person's age. Circles are round. + */ public int getAge() { return age; } + /** + * Age of the person. Water is wet. + * + * @param age New value for this person's age. Sky is blue. + */ public void setAge(int age) { this.age = age; } + /** + * Changes the name of this person. + * + * @param name The new value. + */ protected void setName(String name) { this.name = name; } diff --git a/usage_examples/GetterSetterExample_pre.jpage b/usage_examples/GetterSetterExample_pre.jpage index 9ef0532c..4183aa5d 100644 --- a/usage_examples/GetterSetterExample_pre.jpage +++ b/usage_examples/GetterSetterExample_pre.jpage @@ -3,7 +3,21 @@ import lombok.Getter; import lombok.Setter; public class GetterSetterExample { + /** + * Age of the person. Water is wet. + * + * @param age New value for this person's age. Sky is blue. + * @return The current value of this person's age. Circles are round. + */ @Getter @Setter private int age = 10; + + /** + * Name of the person. + * -- SETTER -- + * Changes the name of this person. + * + * @param name The new value. + */ @Setter(AccessLevel.PROTECTED) private String name; @Override public String toString() { diff --git a/website/features/GetterSetter.html b/website/features/GetterSetter.html index c78b03bd..7e2ff226 100644 --- a/website/features/GetterSetter.html +++ b/website/features/GetterSetter.html @@ -30,6 +30,8 @@ behaviour of a <code>@Getter</code>, <code>@Setter</code> or <code>@Data</code> annotation on a class. </p><p> To put annotations on the generated method, you can use <code>onMethod=@_({@AnnotationsHere})</code>; to put annotations on the only parameter of a generated setter method, you can use <code>onParam=@_({@AnnotationsHere})</code>. Be careful though! This is an experimental feature. For more details see the documentation on the <a href="experimental/onX.html">onX</a> feature. + </p><p> + <em>NEW in lombok v1.12.0:</em> javadoc on the field will now be copied to generated getters and setters. Normally, all text is copied, and <code>@return</code> is <em>moved</em> to the getter, whilst <code>@param</code> lines are <em>moved</em> to the setter. Moved means: Deleted from the field's javadoc. It is also possible to define unique text for each getter/setter. To do that, you create a 'section' named <code>GETTER</code> and/or <code>SETTER</code>. A section is a line in your javadoc containing 2 or more dashes, then the text 'GETTER' or 'SETTER', followed by 2 or more dashes, and nothing else on the line. If you use sections, <code>@return</code> and <code>@param</code> stripping for that section is no longer done (move the <code>@return</code> or <code>@param</code> line into the section). </p> </div> <div class="snippets"> |