Skip to content

Commit

Permalink
Value.Default annotation is redundant for Java 8 default interface
Browse files Browse the repository at this point in the history
method #125
  • Loading branch information
elucash committed Jun 30, 2015
1 parent 89aea68 commit ad64b08
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 13 deletions.
9 changes: 9 additions & 0 deletions value-annotations/src/org/immutables/value/Value.java
Expand Up @@ -531,6 +531,15 @@
*/ */
boolean allParameters() default false; boolean allParameters() default false;


/**
* This funny-named named attribute, when enabled makes default accessor methods defined in
* interfaces/traits to behave as if they annotated as {@literal @}{@link Value.Default}.
* This is not a default behaviour to preserve compatibility and also to have an choice to not
* opt-in for this new functionality when not needed.
* @return if consider default method accessors as {@literal @}{@code Value.Default}
*/
boolean defaultAsDefault() default false;

/** /**
* List type of annotations to copy over from abstract value type to immutable implementation * List type of annotations to copy over from abstract value type to immutable implementation
* class. Very often this functionality is not needed when annoatations are declared as * class. Very often this functionality is not needed when annoatations are declared as
Expand Down
15 changes: 15 additions & 0 deletions value-fixture/src/org/immutables/fixture/DefaultAsDefault.java
@@ -0,0 +1,15 @@
package org.immutables.fixture;

import org.immutables.value.Value;

@Value.Immutable
@Value.Style(defaultAsDefault = true)
public interface DefaultAsDefault {
default int a() {
return 1;
}

default int b() {
return 2;
}
}
10 changes: 10 additions & 0 deletions value-fixture/test/org/immutables/fixture/ValuesTest.java
Expand Up @@ -102,6 +102,16 @@ public void resetCollectionTest() {
check(a).is(b); check(a).is(b);
} }


@Test
public void defaultAsDefault() {
DefaultAsDefault d = ImmutableDefaultAsDefault.builder()
.b(1)
.build();

check(d.a()).is(1);
check(d.b()).is(1);
}

@Test @Test
public void extendsBuilderIfaceValue() { public void extendsBuilderIfaceValue() {
ExtendedBuilderInterface ifc = ExtendedBuilderInterface.builder() ExtendedBuilderInterface ifc = ExtendedBuilderInterface.builder()
Expand Down
Expand Up @@ -188,10 +188,17 @@ private void processGenerationCandidateMethod(ExecutableElement attributeMethodC
} }
} }


if (isDiscoveredAttribute(attributeMethodCandidate)) { boolean useDefaultAsDefault = type.constitution.style().defaultAsDefault();

if (isDiscoveredAttribute(attributeMethodCandidate, useDefaultAsDefault)) {
TypeMirror returnType = resolveReturnType(attributeMethodCandidate); TypeMirror returnType = resolveReturnType(attributeMethodCandidate);


ValueAttribute attribute = new ValueAttribute(); ValueAttribute attribute = new ValueAttribute();
attribute.reporter = reporter;
attribute.returnType = returnType;
attribute.names = deriveNames(name.toString());
attribute.element = attributeMethodCandidate;
attribute.containingType = type;


boolean isFinal = isFinal(attributeMethodCandidate); boolean isFinal = isFinal(attributeMethodCandidate);
boolean isAbstract = isAbstract(attributeMethodCandidate); boolean isAbstract = isAbstract(attributeMethodCandidate);
Expand Down Expand Up @@ -239,8 +246,17 @@ private void processGenerationCandidateMethod(ExecutableElement attributeMethodC
.error("Annotated attribute '%s' will be overriden and cannot be final", name); .error("Annotated attribute '%s' will be overriden and cannot be final", name);
} else if (defaultAnnotationPresent) { } else if (defaultAnnotationPresent) {
attribute.isGenerateDefault = true; attribute.isGenerateDefault = true;

if (useDefaultAsDefault && attribute.isInterfaceDefaultMethod()) {
report(attributeMethodCandidate)
.annotationNamed(DefaultMirror.simpleName())
.warning("@Value.Default annotation is superflous for default annotation attribute"
+ " when 'defaultAsDefault' style is enabled");
}
} else if (derivedAnnotationPresent) { } else if (derivedAnnotationPresent) {
attribute.isGenerateDerived = true; attribute.isGenerateDerived = true;
} else if (useDefaultAsDefault) {
attribute.isGenerateDefault = attribute.isInterfaceDefaultMethod();
} }


if (LazyMirror.isPresent(attributeMethodCandidate)) { if (LazyMirror.isPresent(attributeMethodCandidate)) {
Expand All @@ -252,14 +268,10 @@ private void processGenerationCandidateMethod(ExecutableElement attributeMethodC
.error("@Value.Lazy attribute '%s' cannot be @Value.Derived or @Value.Default", name); .error("@Value.Lazy attribute '%s' cannot be @Value.Derived or @Value.Default", name);
} else { } else {
attribute.isGenerateLazy = true; attribute.isGenerateLazy = true;
attribute.isGenerateDefault = false;
} }
} }


attribute.reporter = reporter;
attribute.returnType = returnType;
attribute.names = deriveNames(name.toString());
attribute.element = attributeMethodCandidate;
attribute.containingType = type;
attributes.add(attribute); attributes.add(attribute);


// Compute this eagerly here, for no strong reason // Compute this eagerly here, for no strong reason
Expand Down Expand Up @@ -333,10 +345,12 @@ private static boolean isFinal(Element element) {
return element.getModifiers().contains(Modifier.FINAL); return element.getModifiers().contains(Modifier.FINAL);
} }


private static boolean isDiscoveredAttribute(ExecutableElement attributeMethodCandidate) { private static boolean isDiscoveredAttribute(ExecutableElement attributeMethodCandidate, boolean isDefaultAsDefault) {
return attributeMethodCandidate.getParameters().isEmpty() return attributeMethodCandidate.getParameters().isEmpty()
&& attributeMethodCandidate.getReturnType().getKind() != TypeKind.VOID && attributeMethodCandidate.getReturnType().getKind() != TypeKind.VOID
&& (isAbstract(attributeMethodCandidate) || hasGenerateAnnotation(attributeMethodCandidate)); && (isAbstract(attributeMethodCandidate)
|| hasGenerateAnnotation(attributeMethodCandidate)
|| isDefaultAsDefault);
} }


private static boolean hasGenerateAnnotation(ExecutableElement attributeMethodCandidate) { private static boolean hasGenerateAnnotation(ExecutableElement attributeMethodCandidate) {
Expand Down
Expand Up @@ -983,6 +983,7 @@ public StyleInfo apply(StyleMirror input) {
ToImmutableInfo.FUNCTION.apply(input.defaults()), ToImmutableInfo.FUNCTION.apply(input.defaults()),
input.strictBuilder(), input.strictBuilder(),
input.allParameters(), input.allParameters(),
input.defaultAsDefault(),
input.jdkOnly(), input.jdkOnly(),
ImmutableSet.copyOf(input.passAnnotationsName()), ImmutableSet.copyOf(input.passAnnotationsName()),
input.visibility()); input.visibility());
Expand Down
Expand Up @@ -118,6 +118,10 @@ public Class<? extends Annotation> annotationType() {
@Override @Override
public abstract boolean allParameters(); public abstract boolean allParameters();


@Value.Parameter
@Override
public abstract boolean defaultAsDefault();

@Value.Parameter @Value.Parameter
@Override @Override
public abstract boolean jdkOnly(); public abstract boolean jdkOnly();
Expand Down
Expand Up @@ -324,25 +324,29 @@ public boolean isGuavaImmutableDeclared() {
} }


@Nullable @Nullable
private CharSequence defaultInterface; private String defaultInterface;


public CharSequence defaultInterface() { public String defaultInterface() {
if (defaultInterface == null) { if (defaultInterface == null) {
defaultInterface = inferDefaultInterface(); defaultInterface = inferDefaultInterface();
} }
return defaultInterface; return defaultInterface;
} }


private CharSequence inferDefaultInterface() { private String inferDefaultInterface() {
if (element.getEnclosingElement().getKind() == ElementKind.INTERFACE if (isInterfaceDefaultMethod()) {
&& !element.getModifiers().contains(Modifier.ABSTRACT)) {
if (containingType.element.getKind() == ElementKind.INTERFACE) { if (containingType.element.getKind() == ElementKind.INTERFACE) {
return containingType.typeAbstract().relative(); return containingType.typeAbstract().relative();
} }
} }
return ""; return "";
} }


public boolean isInterfaceDefaultMethod() {
return element.getEnclosingElement().getKind() == ElementKind.INTERFACE
&& !element.getModifiers().contains(Modifier.ABSTRACT);
}

public boolean isGenerateEnumMap() { public boolean isGenerateEnumMap() {
return typeKind.isEnumMap(); return typeKind.isEnumMap();
} }
Expand Down
Expand Up @@ -121,6 +121,8 @@ private ValueMirrors() {}


boolean allParameters() default false; boolean allParameters() default false;


boolean defaultAsDefault() default false;

boolean jdkOnly() default false; boolean jdkOnly() default false;


Class<? extends Annotation>[] passAnnotations() default {}; Class<? extends Annotation>[] passAnnotations() default {};
Expand Down
9 changes: 9 additions & 0 deletions value/src/org/immutables/value/Value.java
Expand Up @@ -531,6 +531,15 @@
*/ */
boolean allParameters() default false; boolean allParameters() default false;


/**
* This funny-named named attribute, when enabled makes default accessor methods defined in
* interfaces/traits to behave as if they annotated as {@literal @}{@link Value.Default}.
* This is not a default behaviour to preserve compatibility and also to have an choice to not
* opt-in for this new functionality when not needed.
* @return if consider default method accessors as {@literal @}{@code Value.Default}
*/
boolean defaultAsDefault() default false;

/** /**
* List type of annotations to copy over from abstract value type to immutable implementation * List type of annotations to copy over from abstract value type to immutable implementation
* class. Very often this functionality is not needed when annoatations are declared as * class. Very often this functionality is not needed when annoatations are declared as
Expand Down

0 comments on commit ad64b08

Please sign in to comment.