Skip to content

Commit

Permalink
#433 Builder.Constructor
Browse files Browse the repository at this point in the history
  • Loading branch information
elucash committed Aug 23, 2016
1 parent 186cb1f commit 4543a5c
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 15 deletions.
23 changes: 23 additions & 0 deletions builder/src/org/immutables/builder/Builder.java
Expand Up @@ -55,6 +55,29 @@
@Target(ElementType.METHOD)
public @interface Factory {}

/**
* The same as {@link Factory}, but for constructors rather than static methods.
*
* <pre>
* class Sum {
* {@literal @}Builder.Constructor
* Sum(int a, int b) {
* return a + b;
* }
* }
* ... // use generated builder
* Sum sum = new SumBuilder()
* .a(111)
* .b(222)
* .build();
* </pre>
* <p>
* Class level and package level style annotations fully supported (see {@link Style}).
*/
@Documented
@Target(ElementType.CONSTRUCTOR)
public @interface Constructor {}

/**
* Factory method parameter might be turned into builder parameter using this annotation.
*
Expand Down
26 changes: 26 additions & 0 deletions builder/test/org/immutables/builder/fixture/Pogo.java
@@ -0,0 +1,26 @@
package org.immutables.builder.fixture;

import java.lang.annotation.RetentionPolicy;
import org.immutables.builder.Builder;

public class Pogo {
final int a;
final String b;
final RetentionPolicy policy;

@Builder.Constructor
public Pogo(@Builder.Parameter int a, String b, @Builder.Switch RetentionPolicy policy) {
this.a = a;
this.b = b;
this.policy = policy;
}

public static void main(String... args) {
Pogo pogo = new PogoBuilder(1)
.b("a")
.runtimePolicy()
.build();

pogo.toString();
}
}
Expand Up @@ -15,6 +15,7 @@
*/
package org.immutables.value.processor;

import org.immutables.value.processor.meta.FConstructorMirror;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Multimap;
import java.util.Set;
Expand All @@ -33,12 +34,13 @@
import org.immutables.value.processor.meta.ValueUmbrellaMirror;

@SupportedAnnotationTypes({
FactoryMirror.QUALIFIED_NAME,
ImmutableMirror.QUALIFIED_NAME,
EnclosingMirror.QUALIFIED_NAME,
IncludeMirror.QUALIFIED_NAME,
ModifiableMirror.QUALIFIED_NAME,
ValueUmbrellaMirror.QUALIFIED_NAME
ValueUmbrellaMirror.QUALIFIED_NAME,
FactoryMirror.QUALIFIED_NAME,
FConstructorMirror.QUALIFIED_NAME
})
public final class Processor extends AbstractGenerator {
@Override
Expand Down
Expand Up @@ -23,6 +23,9 @@ private BuilderMirrors() {}
@Mirror.Annotation("org.immutables.builder.Builder.Factory")
public @interface Factory {}

@Mirror.Annotation("org.immutables.builder.Builder.Constructor")
public @interface FConstructor {}

@Mirror.Annotation("org.immutables.builder.Builder.Parameter")
public @interface FParameter {}

Expand Down
Expand Up @@ -15,6 +15,7 @@
*/
package org.immutables.value.processor.meta;

import org.immutables.value.processor.meta.Proto.Protoclass.Kind;
import org.immutables.value.processor.meta.ValueMirrors.Style.ImplementationVisibility;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
Expand Down Expand Up @@ -52,7 +53,10 @@ public abstract class Constitution {

@Value.Lazy
public Generics generics() {
return new Generics(protoclass(), protoclass().sourceElement());
return new Generics(protoclass(),
protoclass().kind() == Kind.DEFINED_CONSTRUCTOR
? protoclass().sourceElement().getEnclosingElement()
: protoclass().sourceElement());
}

@Value.Derived
Expand Down Expand Up @@ -165,6 +169,17 @@ public NameForms typeValue() {
: typeImmutable();
}
if (isFactory()) {
if (protoclass().kind() == Kind.DEFINED_CONSTRUCTOR) {
return ImmutableConstitution.NameForms.builder()
.simple(protoclass().declaringType().get().element().getSimpleName().toString())
.relativeRaw(protoclass().declaringType().get().name())
.genericArgs(generics().args())
.relativeAlreadyQualified(true)
.packageOf(implementationPackage())
.visibility(protoclass().visibility())
.build();
}

ExecutableElement method = (ExecutableElement) protoclass().sourceElement();
String type = method.getReturnType().toString();

Expand Down Expand Up @@ -220,6 +235,10 @@ public boolean hasEnclosingNonvalue() {
*/
@Value.Lazy
public NameForms typeAbstract() {
if (protoclass().kind() == Kind.DEFINED_CONSTRUCTOR) {
return typeValue();
}

List<String> classSegments = Lists.newArrayListWithExpectedSize(2);
Element e = SourceNames.collectClassSegments(protoclass().sourceElement(), classSegments);
verify(e instanceof PackageElement);
Expand Down Expand Up @@ -354,6 +373,10 @@ private boolean isConstantNamingEquals(Naming naming, String name) {
@Value.Lazy
public AppliedNameForms factoryOf() {
if (isFactory()) {
String invoke = protoclass().kind() == Kind.DEFINED_CONSTRUCTOR
? "new"
: protoclass().sourceElement().getSimpleName().toString();

return ImmutableConstitution.NameForms.builder()
.simple(protoclass().declaringType().get().element().getSimpleName().toString())
.relativeRaw(protoclass().declaringType().get().name())
Expand All @@ -362,8 +385,9 @@ public AppliedNameForms factoryOf() {
.packageOf(implementationPackage())
.visibility(protoclass().visibility())
.build()
.applied(protoclass().sourceElement().getSimpleName().toString());
.applied(invoke);
}

return applyFactoryNaming(names().namings.of);
}

Expand Down Expand Up @@ -566,7 +590,7 @@ private String combineApplied(boolean qualifyWithPackage) {
: (base + '.' + genericArgs() + applied());
}
}

public static abstract class AbstractNameForms {
private static final String PUBLIC_MODIFIER_PREFIX = "public ";
private static final String PRIVATE_MODIFIER_PREFIX = "private ";
Expand Down
Expand Up @@ -15,15 +15,18 @@
*/
package org.immutables.value.processor.meta;

import javax.lang.model.element.ElementKind;
import com.google.common.base.Functions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.util.List;
import javax.annotation.Nullable;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Parameterizable;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import org.immutables.value.processor.encode.Instantiator;
import org.immutables.value.processor.encode.Instantiator.InstantiationCreator;
import org.immutables.value.processor.meta.Proto.Protoclass;

Expand All @@ -43,6 +46,9 @@ final class FactoryMethodAttributesCollector {

void collect() {
ExecutableElement factoryMethodElement = (ExecutableElement) protoclass.sourceElement();
Parameterizable element = (Parameterizable) (factoryMethodElement.getKind() == ElementKind.CONSTRUCTOR
? factoryMethodElement.getEnclosingElement()
: type.element);

for (VariableElement parameter : factoryMethodElement.getParameters()) {
TypeMirror returnType = parameter.asType();
Expand All @@ -60,19 +66,18 @@ void collect() {
attributes.add(attribute);
}

// Not supported currently for factory builders until well tested
@Nullable InstantiationCreator instantiationCreator = null;
// Instantiator encodingInstantiator = protoclass.encodingInstantiator();
// @Nullable InstantiationCreator instantiationCreator =
// encodingInstantiator.creatorFor((Parameterizable) type.element);
Instantiator encodingInstantiator = protoclass.encodingInstantiator();

@Nullable InstantiationCreator instantiationCreator =
encodingInstantiator.creatorFor(element);

for (ValueAttribute attribute : attributes) {
attribute.initAndValidate(instantiationCreator);
}

// if (instantiationCreator != null) {
// type.additionalImports(instantiationCreator.imports);
// }
if (instantiationCreator != null) {
type.additionalImports(instantiationCreator.imports);
}

type.attributes.addAll(attributes);
type.throwing = extractThrowsClause(factoryMethodElement);
Expand Down
29 changes: 27 additions & 2 deletions value-processor/src/org/immutables/value/processor/meta/Proto.java
Expand Up @@ -900,6 +900,7 @@ boolean verifiedFactory(ExecutableElement element) {
return false;
}
if (!isTopLevel()
|| element.getKind() != ElementKind.METHOD
|| element.getReturnType().getKind() == TypeKind.VOID
|| element.getModifiers().contains(Modifier.PRIVATE)
|| !element.getModifiers().contains(Modifier.STATIC)) {
Expand All @@ -914,6 +915,24 @@ boolean verifiedFactory(ExecutableElement element) {
return true;
}

boolean verifiedConstructor(ExecutableElement element) {
if (!FConstructorMirror.isPresent(element)) {
return false;
}
if (!isTopLevel()
|| element.getKind() != ElementKind.CONSTRUCTOR
|| element.getModifiers().contains(Modifier.PRIVATE)) {
report().withElement(element)
.annotationNamed(FConstructorMirror.simpleName())
.error("@%s annotated element should be non-private constructor in a top level type",
FConstructorMirror.simpleName(),
element.getSimpleName());
return false;
}

return true;
}

/**
* Some validations, not exhaustive.
*/
Expand Down Expand Up @@ -1375,14 +1394,19 @@ public Optional<DeclaringType> enclosingOf() {
}

TypeNames createTypeNames() {
return styles().forType(sourceElement().getSimpleName().toString());
Element sourceElement = sourceElement();
if (sourceElement.getKind() == ElementKind.CONSTRUCTOR) {
sourceElement = sourceElement.getEnclosingElement();
}
return styles().forType(sourceElement.getSimpleName().toString());
}

public enum Kind {
INCLUDED_IN_PACKAGE,
INCLUDED_ON_TYPE,
INCLUDED_IN_TYPE,
DEFINED_FACTORY,
DEFINED_CONSTRUCTOR,
DEFINED_TYPE,
DEFINED_TYPE_AND_COMPANION,
DEFINED_COMPANION,
Expand Down Expand Up @@ -1454,7 +1478,8 @@ public boolean isModifiable() {
}

public boolean isFactory() {
return this == DEFINED_FACTORY;
return this == DEFINED_FACTORY
|| this == DEFINED_CONSTRUCTOR;
}

public boolean isEnclosingOnly() {
Expand Down
11 changes: 11 additions & 0 deletions value-processor/src/org/immutables/value/processor/meta/Round.java
Expand Up @@ -186,6 +186,7 @@ void collect(Element element) {
collectIncludedAndDefinedBy((TypeElement) element);
break;
case METHOD:
case CONSTRUCTOR:
collectDefinedBy((ExecutableElement) element);
break;
case PACKAGE:
Expand All @@ -210,6 +211,16 @@ void collectDefinedBy(ExecutableElement element) {
.kind(Kind.DEFINED_FACTORY)
.build()));
}

if (declaringType.verifiedConstructor(element)) {
builder.add(interners.forProto(ImmutableProto.Protoclass.builder()
.environment(environment())
.packageOf(declaringType.packageOf())
.sourceElement(wrapElement(element))
.declaringType(declaringType)
.kind(Kind.DEFINED_CONSTRUCTOR)
.build()));
}
}

void collectIncludedBy(PackageElement element) {
Expand Down

0 comments on commit 4543a5c

Please sign in to comment.