Skip to content

Commit

Permalink
builder @singleton now works fully in eclipse as well! feature done!
Browse files Browse the repository at this point in the history
  • Loading branch information
rzwitserloot committed Jan 19, 2015
1 parent d6fe0a2 commit 519f95c
Show file tree
Hide file tree
Showing 22 changed files with 711 additions and 261 deletions.
11 changes: 11 additions & 0 deletions doc/changelog.markdown
Expand Up @@ -4,6 +4,17 @@ Lombok Changelog
### v1.14.9.shadow "<del>Edgy</del> Shadowy Guinea Pig"
* Added a launcher to the lombok boot process which removes the need for `-Xbootclasspath` to be in your `eclipse.ini` file, and removes all non-public API and third party dependencies (such as ASM) from the lombok jar, thus removing them from your IDE's auto complete offerings in any project that uses lombok. For those debugging lombok, the launcher enables hot code replace which makes debugging a lot easier, as previously one was required to shut down the IDE, rebuild the jar, and relaunch. Add `-Dshadow.override.lombok=/path/to/lombok/bin` to the launch target for hot code replace.

* Builder __TODO TODO TODO TODO DO NOT SHIP YET__:

* features web page
* warnings / errors when you attempt to handroll some / all of the methods/fields that @Singular would have generated. We are most likely not going to support it.
* The 'I use guava' switch in l.config
* Disable auto-singular in l.config
* Review if there are nay potentially breaking changes in the pipeline for builder BEFORE moving it out of experimental.
* Make sure you cover the fact that builder has moved on from experimental in this issue, and on the features page /doc!

* __TODO TODO TODO TODO DO NOT SHIP YET__: At least modify the deprecated warning of the constructorProperties thing that we're gonna kill it soon.

### v1.14.8 (September 15th, 2014)
* PERFORMANCE: The configuration system typically hit the filesystem twice per read configuration key instead of hardly ever. This is a continuation of [Issue #682](https://code.google.com/p/projectlombok/issues/detail?id=682).

Expand Down
21 changes: 20 additions & 1 deletion src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
Expand Up @@ -8,9 +8,12 @@
import java.util.List;
import java.util.Map;

import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
Expand All @@ -24,6 +27,8 @@
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;

import lombok.core.LombokImmutableList;
Expand Down Expand Up @@ -101,14 +106,28 @@ public static final class SingularData {
private final List<TypeReference> typeArgs;
private final String targetFqn;
private final EclipseSingularizer singularizer;
private final ASTNode source;

public SingularData(EclipseNode annotation, char[] singularName, char[] pluralName, List<TypeReference> typeArgs, String targetFqn, EclipseSingularizer singularizer) {
public SingularData(EclipseNode annotation, char[] singularName, char[] pluralName, List<TypeReference> typeArgs, String targetFqn, EclipseSingularizer singularizer, ASTNode source) {
this.annotation = annotation;
this.singularName = singularName;
this.pluralName = pluralName;
this.typeArgs = typeArgs;
this.targetFqn = targetFqn;
this.singularizer = singularizer;
this.source = source;
}

public void setGeneratedByRecursive(ASTNode target) {
SetGeneratedByVisitor visitor = new SetGeneratedByVisitor(source);

if (target instanceof AbstractMethodDeclaration) {
((AbstractMethodDeclaration) target).traverse(visitor, (ClassScope) null);
} else if (target instanceof FieldDeclaration) {
((FieldDeclaration) target).traverse(visitor, (MethodScope) null);
} else {
target.traverse(visitor, null);
}
}

public EclipseNode getAnnotation() {
Expand Down
42 changes: 24 additions & 18 deletions src/core/lombok/eclipse/handlers/HandleBuilder.java
Expand Up @@ -62,6 +62,7 @@
import org.eclipse.jdt.internal.compiler.ast.UnaryExpression;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.mangosdk.spi.ProviderFor;
Expand All @@ -71,6 +72,7 @@
import lombok.ConfigurationKeys;
import lombok.Singular;
import lombok.core.AST.Kind;
import lombok.core.handlers.HandlerUtil;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.eclipse.Eclipse;
Expand All @@ -89,6 +91,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {

private static final boolean toBoolean(Object expr, boolean defaultValue) {
if (expr == null) return defaultValue;
if (expr instanceof FalseLiteral) return false;
if (expr instanceof TrueLiteral) return true;
return ((Boolean) expr).booleanValue();
}

Expand Down Expand Up @@ -153,7 +157,7 @@ private static class BuilderFieldData {
BuilderFieldData bfd = new BuilderFieldData();
bfd.name = removePrefixFromField(fieldNode);
bfd.type = fd.type;
bfd.singularData = getSingularData(fieldNode);
bfd.singularData = getSingularData(fieldNode, ast);
builderFields.add(bfd);
allFields.add(fieldNode);
}
Expand Down Expand Up @@ -232,7 +236,7 @@ private static class BuilderFieldData {
Argument arg = (Argument) param.get();
bfd.name = arg.name;
bfd.type = arg.type;
bfd.singularData = getSingularData(param);
bfd.singularData = getSingularData(param, ast);
builderFields.add(bfd);
}
}
Expand All @@ -253,12 +257,13 @@ private static class BuilderFieldData {
}
}

generateBuilderFields(builderType, builderFields);
generateBuilderFields(builderType, builderFields, ast);
if (addCleaning) {
FieldDeclaration cleanDecl = new FieldDeclaration(CLEAN_FIELD_NAME, 0, -1);
cleanDecl.declarationSourceEnd = -1;
cleanDecl.modifiers = ClassFileConstants.AccPrivate;
cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
System.out.println("INJECTING: cleaning");
injectField(builderType, cleanDecl);
}

Expand All @@ -274,7 +279,7 @@ private static class BuilderFieldData {
}

if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
MethodDeclaration md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning);
MethodDeclaration md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast);
if (md != null) injectMethod(builderType, md);
}

Expand All @@ -287,17 +292,18 @@ private static class BuilderFieldData {
if (md != null) injectMethod(builderType, md);
}

if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType));
if (addCleaning) {
MethodDeclaration cleanMethod = generateCleanMethod(builderFields, builderType, ast);
if (cleanMethod != null) injectMethod(builderType, cleanMethod);
}

if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams, ast);
if (md != null) injectMethod(tdParent, md);
}

builderType.get().traverse(new SetGeneratedByVisitor(ast), null);
}

private MethodDeclaration generateCleanMethod(List<BuilderFieldData> builderFields, EclipseNode builderType) {
private MethodDeclaration generateCleanMethod(List<BuilderFieldData> builderFields, EclipseNode builderType, ASTNode source) {
List<Statement> statements = new ArrayList<Statement>();

for (BuilderFieldData bfd : builderFields) {
Expand All @@ -309,16 +315,17 @@ private MethodDeclaration generateCleanMethod(List<BuilderFieldData> builderFiel
FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0);
thisUnclean.receiver = new ThisReference(0, 0);
statements.add(new Assignment(thisUnclean, new FalseLiteral(0, 0), 0));
MethodDeclaration decl =new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
MethodDeclaration decl = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
decl.selector = CLEAN_METHOD_NAME;
decl.modifiers = ClassFileConstants.AccPrivate;
decl.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
decl.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0);
decl.statements = statements.toArray(new Statement[0]);
decl.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
return decl;
}

public MethodDeclaration generateBuildMethod(String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning) {
public MethodDeclaration generateBuildMethod(String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) {
MethodDeclaration out = new MethodDeclaration(
((CompilationUnitDeclaration) type.top().get()).compilationResult);
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
Expand Down Expand Up @@ -381,6 +388,7 @@ public MethodDeclaration generateBuildMethod(String name, char[] staticName, Typ
}
}
out.statements = statements.isEmpty() ? null : statements.toArray(new Statement[statements.size()]);
out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
return out;
}

Expand All @@ -403,16 +411,14 @@ public MethodDeclaration generateBuilderMethod(String builderMethodName, String
return out;
}

public void generateBuilderFields(EclipseNode builderType, List<BuilderFieldData> builderFields) {
int len = builderFields.size();
public void generateBuilderFields(EclipseNode builderType, List<BuilderFieldData> builderFields, ASTNode source) {
List<EclipseNode> existing = new ArrayList<EclipseNode>();
for (EclipseNode child : builderType.down()) {
if (child.getKind() == Kind.FIELD) existing.add(child);
}

top:
for (int i = len - 1; i >= 0; i--) {
BuilderFieldData bfd = builderFields.get(i);
for (BuilderFieldData bfd : builderFields) {
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType));
} else {
Expand All @@ -428,6 +434,7 @@ public void generateBuilderFields(EclipseNode builderType, List<BuilderFieldData
fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
fd.modifiers = ClassFileConstants.AccPrivate;
fd.type = copyType(bfd.type);
fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null);
bfd.createdFields.add(injectField(builderType, fd));
}
}
Expand Down Expand Up @@ -457,8 +464,7 @@ private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, EclipseNo
if (Arrays.equals(name, existingName)) return;
}

boolean isBoolean = isBoolean(fd.type);
String setterName = fluent ? fieldNode.getName() : toSetterName(builderType.getAst(), null, fieldNode.getName(), isBoolean);
String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName());

MethodDeclaration setter = HandleSetter.createSetter(td, fieldNode, setterName, chain, ClassFileConstants.AccPublic,
sourceNode, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
Expand Down Expand Up @@ -492,7 +498,7 @@ public EclipseNode makeBuilderClass(EclipseNode tdParent, String builderClassNam
*
* @param node The node (field or method param) to inspect for its name and potential {@code @Singular} annotation.
*/
private SingularData getSingularData(EclipseNode node) {
private SingularData getSingularData(EclipseNode node, ASTNode source) {
for (EclipseNode child : node.down()) {
if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Singular.class, child)) {
char[] pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((AbstractVariableDeclaration) node.get()).name;
Expand Down Expand Up @@ -534,7 +540,7 @@ private SingularData getSingularData(EclipseNode node) {
return null;
}

return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer);
return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source);
}
}

Expand Down
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2015 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.singulars;

import org.mangosdk.spi.ProviderFor;

import lombok.core.LombokImmutableList;
import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;

@ProviderFor(EclipseSingularizer.class)
public class EclipseGuavaMapSingularizer extends EclipseGuavaSingularizer {
// TODO cgcc.ImmutableMultimap, cgcc.ImmutableListMultimap, cgcc.ImmutableSetMultimap
// TODO cgcc.ImmutableClassToInstanceMap
// TODO cgcc.ImmutableRangeMap

@Override public LombokImmutableList<String> getSupportedTypes() {
return LombokImmutableList.of(
"com.google.common.collect.ImmutableMap",
"com.google.common.collect.ImmutableBiMap",
"com.google.common.collect.ImmutableSortedMap");
}

@Override protected boolean isMap() {
return true;
}
}
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2015 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.singulars;

import org.mangosdk.spi.ProviderFor;

import lombok.core.LombokImmutableList;
import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;

@ProviderFor(EclipseSingularizer.class)
public class EclipseGuavaSetListSingularizer extends EclipseGuavaSingularizer {
// TODO com.google.common.collect.ImmutableTable
// TODO com.google.common.collect.ImmutableRangeSet
// TODO com.google.common.collect.ImmutableMultiset and com.google.common.collect.ImmutableSortedMultiset
@Override public LombokImmutableList<String> getSupportedTypes() {
return LombokImmutableList.of(
"com.google.common.collect.ImmutableCollection",
"com.google.common.collect.ImmutableList",
"com.google.common.collect.ImmutableSet",
"com.google.common.collect.ImmutableSortedSet");
}

@Override protected boolean isMap() {
return false;
}
}

0 comments on commit 519f95c

Please sign in to comment.