diff options
author | Reinier Zwitserloot <reinier@zwitserloot.com> | 2013-05-23 21:12:01 +0200 |
---|---|---|
committer | Reinier Zwitserloot <reinier@zwitserloot.com> | 2013-06-16 13:20:53 +0200 |
commit | 2d76b1d22dea1e78326ebafdb48967512183cede (patch) | |
tree | 56e75b81132b50de5567d75adfa4b49af471e849 | |
parent | 4e521c558ed7355244dd1cb9c8d94bd5a9cb462d (diff) | |
download | lombok-2d76b1d22dea1e78326ebafdb48967512183cede.tar.gz lombok-2d76b1d22dea1e78326ebafdb48967512183cede.tar.bz2 lombok-2d76b1d22dea1e78326ebafdb48967512183cede.zip |
First steps Builder support
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleBuilder.java | 71 | ||||
-rw-r--r-- | src/core/lombok/experimental/Builder.java | 112 | ||||
-rw-r--r-- | src/utils/lombok/core/JavaIdentifiers.java | 57 |
3 files changed, 240 insertions, 0 deletions
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java new file mode 100644 index 00000000..13271165 --- /dev/null +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2013 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 static lombok.eclipse.handlers.EclipseHandlerUtil.*; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; + +import lombok.AccessLevel; +import lombok.core.AnnotationValues; +import lombok.core.ImmutableList; +import lombok.core.JavaIdentifiers; +import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.EclipseNode; +import lombok.experimental.Builder; + +public class HandleBuilder extends EclipseAnnotationHandler<Builder> { + @Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) { + String builderMethodName = annotation.getInstance().builderMethodName(); + if (builderMethodName == null) builderMethodName = "builder"; + if (builderMethodName.length() == 0) { + annotationNode.addError("builderMethodName cannot be the empty string."); + return; + } + + if (!JavaIdentifiers.isValidJavaIdentifier(builderMethodName)) { + annotationNode.addError("builderMethodName must be a valid java method name."); + return; + } + + EclipseNode parent = annotationNode.up(); + + if (parent.get() instanceof ConstructorDeclaration) { + + } + + if (parent.get() instanceof MethodDeclaration) { + + } + + if (parent.get() instanceof TypeDeclaration) { + // TODO: How do we ensure this one will 'win' over the implicit constructors generated by @Data and @Value. + new HandleConstructor().generateAllArgsConstructor(parent, AccessLevel.PRIVATE, null, true, Collections.<Annotation>emptyList(), ast); + } + } +} diff --git a/src/core/lombok/experimental/Builder.java b/src/core/lombok/experimental/Builder.java new file mode 100644 index 00000000..b6667462 --- /dev/null +++ b/src/core/lombok/experimental/Builder.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2013 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.experimental; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * The builder annotation creates a so-called 'builder' aspect to the class that is annotated or the class + * that contains a member which is annotated with {@code @Builder}. + * <p> + * If a member is annotated, it must be either a constructor or a static method. If a class is annotated, + * then a private constructor is generated with all fields as arguments + * (as if {@code @AllArgsConstructor(AccessLevel.PRIVATE)} is present + * on the class), and it is as if this constructor has been annotated with {@code @Builder} instead. + * <p> + * The effect of {@code @Builder} is that an inner class is generated named <code><strong>T</strong>Builder</code>, + * with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the static + * method named {@code builder()} which is also generated for you in the class itself (not in the builder class). + * <p> + * The <code><strong>T</strong>Builder</code> class contains 1 method for each parameter of the annotated + * constructor / static method (each field, when annotating a class), which returns the builder itself. + * The builder also has a <code>build()</code> method which returns a completed instance of the original type, + * created by passing all parameters as set via the various other methods in the builder to the constructor + * or static method that was annotated with {@code @Builder}. The return type of this method will be the same + * as the relevant class, unless a static method has been annotated, in which case it'll be equal to the + * return type of that method. + * <p> + * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Builder.html">the project lombok features page for @Builder</a>. + * <p> + * <p> + * Before: + * + * <pre> + * @Builder + * class Example { + * private int foo; + * private final String bar; + * } + * </pre> + * + * After: + * + * <pre> + * class Example<T> { + * private T foo; + * private final String bar; + * + * private Example(T foo, String bar) { + * this.foo = foo; + * this.bar = bar; + * } + * + * public static <T> ExampleBuilder<T> builder() { + * return new ExampleBuilder<T>(); + * } + * + * public static class ExampleBuilder<T> { + * private T foo; + * private String bar; + * + * private ExampleBuilder() {} + * + * public ExampleBuilder foo(T foo) { + * this.foo = foo; + * return this; + * } + * + * public ExampleBuilder bar(String bar) { + * this.bar = bar; + * return this; + * } + * + * @java.lang.Override public String toString() { + * return "ExampleBuilder(foo = " + foo + ", bar = " + bar + ")"; + * } + * + * public Example build() { + * return new Example(foo, bar); + * } + * } + * } + * </pre> + */ +@Target({TYPE, METHOD, CONSTRUCTOR}) +@Retention(SOURCE) +public @interface Builder { + /** Name of the static method that creates a new builder instance. Default: {@code builder}. */ + String builderMethodName() default "builder"; +} diff --git a/src/utils/lombok/core/JavaIdentifiers.java b/src/utils/lombok/core/JavaIdentifiers.java new file mode 100644 index 00000000..dfec8815 --- /dev/null +++ b/src/utils/lombok/core/JavaIdentifiers.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2013 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.core; + +/** + * Utility functions for validating potential java verifiers. + */ +public class JavaIdentifiers { + private JavaIdentifiers() {} + + private static final ImmutableList<String> KEYWORDS = ImmutableList.of( + "public", "private", "protected", + "default", "switch", "case", + "for", "do", "goto", "const", "strictfp", "while", "if", "else", + "byte", "short", "int", "long", "float", "double", "void", "boolean", "char", + "null", "false", "true", + "continue", "break", "return", "instanceof", + "synchronized", "volatile", "transient", "final", "static", + "interface", "class", "extends", "implements", "throws", + "throw", "catch", "try", "finally", "abstract", "assert", + "enum", "import", "package", "native", "new", "super", "this"); + + public static boolean isValidJavaIdentifier(String identifier) { + if (identifier == null) return false; + if (identifier.isEmpty()) return false; + + if (!Character.isJavaIdentifierStart(identifier.charAt(0))) return false; + for (int i = 1; i < identifier.length(); i++) { + if (!Character.isJavaIdentifierPart(identifier.charAt(i))) return false; + } + + return !isKeyword(identifier); + } + + public static boolean isKeyword(String keyword) { + return KEYWORDS.contains(keyword); + } +} |