Skip to content

Commit

Permalink
Use immutables to make code cleaner.
Browse files Browse the repository at this point in the history
Man immutables is such a good library.
  • Loading branch information
daicoden committed Jul 20, 2017
1 parent b98703d commit 59cc17c
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 117 deletions.
Expand Up @@ -14,8 +14,6 @@
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import org.immutables.value.Value.Default;
import org.immutables.value.Value.Derived;
import org.immutables.value.Value.Immutable;
import org.immutables.value.Value.Lazy;
Expand Down Expand Up @@ -52,7 +50,7 @@ protected List<Strategy> getStrategies() {
// Order here matters. We want first party to be found first.
return Arrays.asList(
ImmutableFirstPartyStrategy.of(valueAttribute()),
ThirdPartyStaticBuilderStrategy.of(valueAttribute())
ThirdPartyAttributeBuilderStrategy.of(valueAttribute())
);
}

Expand Down Expand Up @@ -175,7 +173,7 @@ ValueType attributeValueType() {
* implement a new strategy for that use case...
*/
@Immutable(builder = false)
abstract static class ThirdPartyStaticBuilderStrategy implements Strategy {
abstract static class ThirdPartyAttributeBuilderStrategy implements Strategy {

/**
* Guaranteed not null if isAttributeBuilder is true.
Expand Down Expand Up @@ -205,11 +203,10 @@ public AttributeBuilderDescriptor getAttributeBuilderDescriptor() {
}

ValueToBuilderTarget target;
ExecutableElement copyMethod = builderModel().getCopyMethod();

ExecutableElement builderMethod = builderModel().getBuilderMethod();
ExecutableElement buildMethod = builderModel().getBuildMethod();
TypeElement attributeBuilderType = builderModel().getBuilderType();
ExecutableElement copyMethod = builderModel().copyMethod();
ExecutableElement builderMethod = builderModel().builderMethod();
ExecutableElement buildMethod = builderModel().buildMethod();
TypeElement attributeBuilderType = builderModel().builderType();

if (copyMethod.getKind() == ElementKind.CONSTRUCTOR) {
target = ValueToBuilderTarget.BUILDER_CONSTRUCTOR;
Expand Down Expand Up @@ -256,76 +253,76 @@ public AttributeBuilderDescriptor getAttributeBuilderDescriptor() {
/**
* @return strategy which has an AttributeBuilderDescriptor if attributeValue is an attributeBuilder.
*/
static ThirdPartyStaticBuilderStrategy of(ValueAttribute valueAttribute) {
static ThirdPartyAttributeBuilderStrategy of(ValueAttribute valueAttribute) {
TypeElement attributeValueType = valueAttribute.containedTypeElement;
if (attributeValueType == null) {
return ImmutableThirdPartyStaticBuilderStrategy.of(null, null);
return ImmutableThirdPartyAttributeBuilderStrategy.of(null, null);
}

// Map of possible builder class to needed methods.
Map<TypeElement, AttributeBuilderThirdPartyModel> partiallyBuiltModels = new HashMap<>();
Map<TypeElement, AttributeBuilderThirdPartyModel.Creator> partiallyBuiltModels = new HashMap<>();
for (Element possibleBuilderMethodCopyMethodOrClass
: attributeValueType.getEnclosedElements()) {
AttributeBuilderThirdPartyModel newBuilderModel = new AttributeBuilderThirdPartyModel();
AttributeBuilderThirdPartyModel.Creator newBuilderModel = ModifiableCreator.create();

if (isPossibleBuilderClass(possibleBuilderMethodCopyMethodOrClass, valueAttribute)) {
newBuilderModel.setBuilderType((TypeElement) possibleBuilderMethodCopyMethodOrClass);
newBuilderModel.builderType((TypeElement) possibleBuilderMethodCopyMethodOrClass);
} else if (isPossibleBuilderMethod(possibleBuilderMethodCopyMethodOrClass, true, valueAttribute)) {
newBuilderModel
.setBuilderMethod((ExecutableElement) possibleBuilderMethodCopyMethodOrClass);
.builderMethod((ExecutableElement) possibleBuilderMethodCopyMethodOrClass);
} else if (isPossibleCopyMethod(valueAttribute,
possibleBuilderMethodCopyMethodOrClass, true)) {
newBuilderModel
.setCopyMethod((ExecutableElement) possibleBuilderMethodCopyMethodOrClass);
.copyMethod((ExecutableElement) possibleBuilderMethodCopyMethodOrClass);
}

// We found something on the loop interesting
if (newBuilderModel.getBuilderType() != null) {
AttributeBuilderThirdPartyModel maybeCompleteModel;
if (newBuilderModel.findBuilderType() != null) {
AttributeBuilderThirdPartyModel.Creator maybeCompleteModel;

if (partiallyBuiltModels.containsKey(newBuilderModel.getBuilderType())) {
AttributeBuilderThirdPartyModel partiallyBuiltModel = partiallyBuiltModels
.get(newBuilderModel.getBuilderType());
if (partiallyBuiltModels.containsKey(newBuilderModel.findBuilderType())) {
AttributeBuilderThirdPartyModel.Creator partiallyBuiltModel = partiallyBuiltModels
.get(newBuilderModel.findBuilderType());
partiallyBuiltModel.mergeFrom(newBuilderModel);
maybeCompleteModel = partiallyBuiltModel;
} else {
processPossibleBuilder(valueAttribute, newBuilderModel);
partiallyBuiltModels.put(newBuilderModel.getBuilderType(), newBuilderModel);
partiallyBuiltModels.put(newBuilderModel.findBuilderType(), newBuilderModel);
maybeCompleteModel = newBuilderModel;
}

if (maybeCompleteModel.complete()) {
return ImmutableThirdPartyStaticBuilderStrategy.of(maybeCompleteModel, valueAttribute.containedTypeElement);
return ImmutableThirdPartyAttributeBuilderStrategy.of(maybeCompleteModel.toImmutable(), valueAttribute.containedTypeElement);
}
}
}

return ImmutableThirdPartyStaticBuilderStrategy.of(null, null);
return ImmutableThirdPartyAttributeBuilderStrategy.of(null, null);
}


// NB: because of the null checks here, we will prefer using static initialization
// from the value object, but eh, doesn't really work that well because we may
// break out of the value loop if this call to processPossibleBuilder completes the model.
private static void processPossibleBuilder(ValueAttribute valueAttribute,
AttributeBuilderThirdPartyModel builderModel) {
AttributeBuilderThirdPartyModel.Creator builderModel) {
for (Element possibleBuildMethodOrConstructor
: builderModel.getBuilderType().getEnclosedElements()) {
: builderModel.findBuilderType().getEnclosedElements()) {

if (builderModel.getBuildMethod() == null
if (builderModel.buildMethod() == null
&& isPossibleBuildMethod(valueAttribute,
possibleBuildMethodOrConstructor)) {
builderModel.setBuildMethod((ExecutableElement) possibleBuildMethodOrConstructor);
builderModel.buildMethod((ExecutableElement) possibleBuildMethodOrConstructor);
}

if (builderModel.getBuilderMethod() == null
if (builderModel.builderMethod() == null
&& isPossibleBuilderMethod(possibleBuildMethodOrConstructor, false, valueAttribute)) {
builderModel.setBuilderMethod((ExecutableElement) possibleBuildMethodOrConstructor);
builderModel.builderMethod((ExecutableElement) possibleBuildMethodOrConstructor);
}

if (builderModel.getCopyMethod() == null
if (builderModel.copyMethod() == null
&& isPossibleCopyMethod(valueAttribute, possibleBuildMethodOrConstructor, false)) {
builderModel.setCopyMethod((ExecutableElement) possibleBuildMethodOrConstructor);
builderModel.copyMethod((ExecutableElement) possibleBuildMethodOrConstructor);
}
}
}
Expand Down
Expand Up @@ -5,123 +5,129 @@
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import org.immutables.value.Value.Immutable;
import org.immutables.value.Value.Modifiable;
import org.immutables.value.Value.Style;

/**
* Helper class to detect a builder by adding methods as discovered in stages.
* TODO: maybe bring in mutable?
*/
class AttributeBuilderThirdPartyModel {

@Immutable
abstract class AttributeBuilderThirdPartyModel {
// Must be instance
@Nullable private ExecutableElement buildMethod;
protected abstract ExecutableElement buildMethod();

// Constructor, Static, or Instance
@Nullable private ExecutableElement copyMethod;
protected abstract ExecutableElement copyMethod();


// Constructor or Static
@Nullable private ExecutableElement builderMethod;
protected abstract ExecutableElement builderMethod();

@Nullable private TypeElement builderType;

public ExecutableElement getBuildMethod() {
return buildMethod;
}
protected abstract TypeElement builderType();

public void setBuildMethod(ExecutableElement buildMethod) {
this.buildMethod = buildMethod;
}
@Modifiable
@Style(set = "*")
abstract static class Creator extends AttributeBuilderThirdPartyModel {

public ExecutableElement getBuilderMethod() {
return builderMethod;
}
@Override @Nullable
protected abstract ExecutableElement buildMethod();

public void setBuilderMethod(ExecutableElement builderMethod) {
this.builderMethod = builderMethod;
}
@Override @Nullable
protected abstract ExecutableElement copyMethod();

public ExecutableElement getCopyMethod() {
return copyMethod;
}
@Override @Nullable
protected abstract ExecutableElement builderMethod();

public void setCopyMethod(ExecutableElement copyMethod) {
this.copyMethod = copyMethod;
}
@Override @Nullable
protected abstract TypeElement builderType();

@Nullable
public TypeElement getBuilderType() {
if (buildMethod != null) {
return getBuilderTypeFromBuildMethod();
}
protected abstract AttributeBuilderThirdPartyModel buildMethod(ExecutableElement buildMethod);

if (copyMethod != null) {
return getBuilderTypeFromCopyMethod();
}
protected abstract AttributeBuilderThirdPartyModel copyMethod(ExecutableElement copyMethod);

if (builderMethod != null) {
return getBuilderTypeFromBuilderMethod();
}
protected abstract AttributeBuilderThirdPartyModel builderMethod(ExecutableElement buildMethod);

if (builderType != null) {
return builderType;
}
protected abstract AttributeBuilderThirdPartyModel builderType(TypeElement builderType);

return null;
}
@Nullable
public TypeElement findBuilderType() {
if (buildMethod() != null) {
return getBuilderTypeFromBuildMethod();
}

public void setBuilderType(@Nullable TypeElement builderType) {
this.builderType = builderType;
}
if (copyMethod() != null) {
return getBuilderTypeFromCopyMethod();
}

public void mergeFrom(AttributeBuilderThirdPartyModel toCopyFrom) {
if (buildMethod == null) {
buildMethod = toCopyFrom.buildMethod;
}
if (builderMethod() != null) {
return getBuilderTypeFromBuilderMethod();
}

if (copyMethod == null) {
copyMethod = toCopyFrom.copyMethod;
}
if (builderType() != null) {
return builderType();
}

if (builderMethod == null) {
builderMethod = toCopyFrom.builderMethod;
return null;
}
}

private TypeElement getBuilderTypeFromBuilderMethod() {
if (builderMethod.getKind() == ElementKind.CONSTRUCTOR) {
return (TypeElement) builderMethod.getEnclosingElement();
} else {
return (TypeElement) ((DeclaredType) builderMethod.getReturnType()).asElement();
public void mergeFrom(AttributeBuilderThirdPartyModel toCopyFrom) {
if (buildMethod() == null) {
buildMethod(toCopyFrom.buildMethod());
}

if (copyMethod() == null) {
copyMethod(toCopyFrom.copyMethod());
}

if (builderMethod() == null) {
builderMethod(toCopyFrom.builderMethod());
}
}
}

private TypeElement getBuilderTypeFromBuildMethod() {
return (TypeElement) buildMethod.getEnclosingElement();
}
private TypeElement getBuilderTypeFromBuilderMethod() {
if (builderMethod().getKind() == ElementKind.CONSTRUCTOR) {
return (TypeElement) builderMethod().getEnclosingElement();
} else {
return (TypeElement) ((DeclaredType) builderMethod().getReturnType()).asElement();
}
}

private TypeElement getBuilderTypeFromCopyMethod() {
if (copyMethod.getKind() == ElementKind.CONSTRUCTOR) {
return (TypeElement) copyMethod.getEnclosingElement();
} else {
return (TypeElement) ((DeclaredType) copyMethod.getReturnType()).asElement();
private TypeElement getBuilderTypeFromBuildMethod() {
return (TypeElement) buildMethod().getEnclosingElement();
}
}

public boolean complete() {
if (builderMethod != null && buildMethod != null && copyMethod != null) {
boolean transitiveEquality = getBuilderTypeFromBuilderMethod()
.equals(getBuilderTypeFromBuildMethod());
transitiveEquality = transitiveEquality && getBuilderTypeFromBuildMethod()
.equals(getBuilderTypeFromCopyMethod());
if (builderType != null) {
transitiveEquality =
transitiveEquality && getBuilderTypeFromCopyMethod().equals(builderType);
private TypeElement getBuilderTypeFromCopyMethod() {
if (copyMethod().getKind() == ElementKind.CONSTRUCTOR) {
return (TypeElement) copyMethod().getEnclosingElement();
} else {
return (TypeElement) ((DeclaredType) copyMethod().getReturnType()).asElement();
}
}

if (!transitiveEquality) {
throw new AssertionError();
public boolean complete() {
if (builderMethod() != null && buildMethod() != null && copyMethod() != null) {
boolean transitiveEquality = getBuilderTypeFromBuilderMethod()
.equals(getBuilderTypeFromBuildMethod());
transitiveEquality = transitiveEquality && getBuilderTypeFromBuildMethod()
.equals(getBuilderTypeFromCopyMethod());
if (builderType() != null) {
transitiveEquality =
transitiveEquality && getBuilderTypeFromCopyMethod().equals(builderType());
}

if (!transitiveEquality) {
throw new AssertionError();
}

return true;
}

return true;
return false;
}

return false;
// Should this be auto-genned? Though we still have to set the builder type.
public AttributeBuilderThirdPartyModel toImmutable() {
builderType(findBuilderType());
return ImmutableAttributeBuilderThirdPartyModel.copyOf(this);
}
}
}

0 comments on commit 59cc17c

Please sign in to comment.