Skip to content

Commit

Permalink
builder switchers and parameters
Browse files Browse the repository at this point in the history
+ refactoring
  • Loading branch information
elucash committed Mar 5, 2015
1 parent e6f276a commit 83eadf5
Show file tree
Hide file tree
Showing 15 changed files with 596 additions and 48 deletions.
37 changes: 37 additions & 0 deletions builder/pom.xml
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>immutables</artifactId>
<groupId>org.immutables</groupId>
<version>2.0-SNAPSHOT</version>
</parent>
<artifactId>builder</artifactId>
<name>${project.groupId}.${project.artifactId}</name>
<dependencies>
<dependency>
<groupId>org.immutables.dependency</groupId>
<artifactId>utility</artifactId>
<version>${project.version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.immutables</groupId>
<artifactId>testing</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value-standalone</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
117 changes: 117 additions & 0 deletions builder/src/org/immutables/builder/Builder.java
@@ -0,0 +1,117 @@
package org.immutables.builder;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.immutables.value.Value.Immutable;
import org.immutables.value.Value.Style;

/**
* <em>
* This umbrella annotaion does nothing. Use nested annotations, such as {@literal @}{@code Builder.Factory}
* to generate builders for arbitrary static factory methods.
* and is used for static factory methods to generate arbitrary builders.
* Immutable values as {@link Immutable Value.Immutable} generate builder by default, unless
* turned off using {@literal @}{@link Immutable#builder() Value.Immutable(builder=false)}</em>
* @see Factory
*/
@Target({})
public @interface Builder {
/**
* Annotate static factory methods that produce some value (non-void, non-private) to create
* builder out of constructor parameters.
*
* <pre>
* class Sum {
* {@literal @}Builder.Factory
* static Integer sum(int a, int b) {
* return a + b;
* }
* }
* ... // use generated builder
* Integer result = new SumBuilder()
* .a(111)
* .b(222)
* .build();
* </pre>
* <p>
* Class level and package level style annotations fully supported (see {@link Style}).
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface Factory {}

/**
* Factory method parameter might be turned into builder parameter using this annotation.
*
* <pre>
* class NodeFactory {
* {@literal @}Builder.Factory
* static Node node({@literal @}Builder.Parameter Object value, Optional<Node> left, Optional<Node> right) {
* return ...
* }
* }
* ... // notice the constructor parameter generated
* Integer result = new NodeBuilder(new Object())
* .left(node1)
* .right(node2)
* .build();
* </pre>
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.PARAMETER)
public @interface Parameter {}

/**
* Applicable only to enum parameters, this annotation turns parameters into switcher methods on
* builder. Each switcher method applies corresponding constant value. Switch methods are named
* after parameter name prefixed with properly cased (case transformed) enum constant name.
*
* <pre>
* class BulbFactory {
* enum Switcher {
* OFF, ON
* }
* {@literal @}Builder.Factory
* static Bulb bulb({@literal @}Builder.Switch Switcher light) {
* return ...
* }
* }
* ... // notice the switcher methods instead of enum initializer
* Bulb b = new BulbBuilder()
* .onLight() // set to Switcher.ON
* .offLight() // set to Switcher.OFF
* .build();
* </pre>
* <p>
* If proper {@link #defaultOrdinal()} value is specified, then one of the state will be
* considered the default. If no default is specified then it is mandatory to call switcher method
* once, otherwise switcher method may be omitted.
*
* <pre>
* class BulbFactory {
* enum Switcher {
* OFF, ON
* }
* {@literal @}Builder.Factory
* static Bulb bulb({@literal @}Builder.Switch(defaultOrdinal = 0) Switcher light) {
* return ...
* }
* }
* ... // notice no 'onLight' generated
* Bulb b = new BulbBuilder() // default is Switcher.OFF
* .onLight() // set to Switcher.ON
* .build();
* </pre>
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.PARAMETER)
public @interface Switch {
int defaultOrdinal() default -1;
}
}
@@ -0,0 +1,42 @@
/*
Copyright 2014 Immutables Authors and Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.immutables.builder.fixture;

import org.immutables.builder.fixture.ConcatBuilder;
import org.immutables.builder.fixture.SumBuilder;
import java.lang.reflect.Modifier;
import org.junit.Test;
import static org.immutables.check.Checkers.*;

public class FactoryBuilderTest {

@Test
public void generatedPublicAsOfStyleAnnotation() {
check(Modifier.isPublic(SumBuilder.class.getModifiers()));
check(Modifier.isPublic(ConcatBuilder.class.getModifiers()));
}

@Test(expected = IllegalStateException.class)
public void missingAttributesCheck() {
new SumBuilder().build();
}

@Test
public void invokesStaticFactory() {
check(new SumBuilder().a(2).b(3).build()).is(5);
check(new ConcatBuilder().addNumbers(1, 2).addStrings("a", "b").build()).isOf("a", "b", 1, 2);
}
}
@@ -0,0 +1,53 @@
package org.immutables.builder.fixture;

import org.immutables.builder.fixture.Factory1Builder;
import org.immutables.builder.fixture.Factory2Builder;
import org.immutables.builder.fixture.Factory3Builder;
import org.immutables.builder.fixture.Factory4Builder;
import org.immutables.builder.fixture.Factory5Builder;
import java.lang.annotation.RetentionPolicy;
import org.junit.Test;
import static org.immutables.check.Checkers.*;

public class FactoryParametersAndSwitcherTest {

@Test
public void parameters() {

check(new Factory1Builder(null)
.theory(1)
.reality("1")
.build()).is("1 != 1, null");

check(new Factory2Builder(2, "2").build()).is("2 != 2");

check(Factory3Builder.newBuilder(3).reality("3").build()).is("3 != 3");
}

@Test
public void switcher() {

check(Factory4Builder.newBuilder(4)
.runtimePolicy()
.sourcePolicy()
.classPolicy()
.build()).is("" + RetentionPolicy.CLASS + 4);

check(Factory4Builder.newBuilder(42)
.sourcePolicy()
.runtimePolicy()
.build()).is("" + RetentionPolicy.RUNTIME + 42);

try {
Factory4Builder.newBuilder(44).build();
check(false);
} catch (IllegalStateException ex) {
}
}

@Test
public void switcherDefaults() {
check(Factory5Builder.newBuilder().build()).is("" + RetentionPolicy.SOURCE.toString());
check(Factory5Builder.newBuilder().runtimePolicy().build()).is("" + RetentionPolicy.RUNTIME);
}
}
@@ -0,0 +1,39 @@
package org.immutables.builder.fixture;

import java.lang.annotation.RetentionPolicy;
import javax.annotation.Nullable;
import org.immutables.builder.Builder;
import org.immutables.value.Value;

@Value.Style
class FactoryParameters {
@Builder.Factory
public static String factory1(int theory, String reality, @Nullable @Builder.Parameter Void evidence) {
return theory + " != " + reality + ", " + evidence;
}

@Builder.Factory
public static String factory2(@Builder.Parameter int theory, @Builder.Parameter String reality) {
return theory + " != " + reality;
}

}

@Value.Style(builder = "newBuilder")
class FactoryParametersAndSwitchers {

@Builder.Factory
public static String factory3(@Builder.Parameter int theory, String reality) {
return theory + " != " + reality;
}

@Builder.Factory
public static String factory4(@Builder.Parameter int value, @Builder.Switch RetentionPolicy policy) {
return policy + "" + value;
}

@Builder.Factory
public static String factory5(@Builder.Switch(defaultOrdinal = 0) RetentionPolicy policy) {
return policy.toString();
}
}
@@ -0,0 +1,71 @@
/*
Copyright 2014 Immutables Authors and Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.immutables.builder.fixture;

import org.immutables.builder.fixture.ConcatBuilder;
import org.immutables.builder.fixture.SumBuilder;
import org.immutables.builder.fixture.SuperstringBuilder;
import com.google.common.collect.Iterables;
import java.util.List;
import java.util.SortedSet;
import javax.annotation.Nullable;
import org.immutables.builder.Builder;
import org.immutables.value.Value;

/**
* Builders for simple attributes, collection, generic and primitive variations.
* Builders are public as of style annotation.
*/
class ImprovisedFactories {

@Builder.Factory
public static String superstring(int theory, String reality, @Nullable Void evidence) {
return theory + " != " + reality + ", " + evidence;
}

@Builder.Factory
public static Iterable<Object> concat(List<String> strings, @Value.NaturalOrder SortedSet<Integer> numbers) {
return Iterables.<Object>concat(strings, numbers);
}

@Builder.Factory
public static int sum(int a, int b) {
return a + b;
}

void use() {

int sumOf1and2 = new SumBuilder()
.a(1)
.b(2)
.build();

String superstring = new SuperstringBuilder()
.theory(0)
.reality("")
.evidence(null)
.build();

Iterable<Object> concat = new ConcatBuilder()
.addStrings(superstring)
.addNumbers(4, 2, 3)
.build();

concat.toString();

String.valueOf(sumOf1and2);
}
}

0 comments on commit 83eadf5

Please sign in to comment.