Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] add generics support #105

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

<!-- Test Dependencies -->
<junit.version>4.10</junit.version>
<compile-testing.version>0.5</compile-testing.version>
<compile-testing.version>0.15</compile-testing.version>
<truth.version>0.13</truth.version>
</properties>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.hannesdorfmann.fragmentargs.processor;

import com.hannesdorfmann.fragmentargs.annotation.Arg;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand All @@ -11,7 +13,9 @@
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;

/**
* Simple data holder class for fragment args of a certain fragment
Expand Down Expand Up @@ -156,6 +160,49 @@ public boolean isInnerClass() {
return classElement.getEnclosingElement().getKind() == ElementKind.CLASS;
}

/**
* Builds a type parameter string
* e.g. {@literal <V extends Serializable & Parcelable, H extends Object>}
*/
public String getTypeParametersString() {
StringBuilder parametersBuilder = new StringBuilder();
Iterator<? extends TypeParameterElement> typeParametersIterator = classElement.getTypeParameters().iterator();

if(typeParametersIterator.hasNext()) {
parametersBuilder.append("<");

while(typeParametersIterator.hasNext()) {
TypeParameterElement parameterElement = typeParametersIterator.next();

// e.g. "V"
parametersBuilder.append(parameterElement.getSimpleName());

Iterator<? extends TypeMirror> boundsIterator = parameterElement.getBounds().iterator();

if(boundsIterator.hasNext()) {
parametersBuilder.append(" extends ");

while (boundsIterator.hasNext()) {
TypeMirror typeMirror = boundsIterator.next();
parametersBuilder.append(typeMirror.toString());

if(boundsIterator.hasNext()){
parametersBuilder.append(" & ");
}
}
}

if(typeParametersIterator.hasNext()) {
parametersBuilder.append(", ");
}
}

parametersBuilder.append(">");
}

return parametersBuilder.toString();
}

public Set<ArgumentAnnotatedField> getAll() {
Set<ArgumentAnnotatedField> all = new HashSet<ArgumentAnnotatedField>(getRequiredFields());
all.addAll(getOptionalFields());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
Expand Down Expand Up @@ -614,7 +615,7 @@ public boolean process(Set<? extends TypeElement> type, RoundEnvironment env) {
jw.emitAnnotation(builderAnnotation);
}

jw.beginType(builderName, "class", EnumSet.of(Modifier.PUBLIC, Modifier.FINAL));
jw.beginType(builderName + fragment.getTypeParametersString(), "class", EnumSet.of(Modifier.PUBLIC, Modifier.FINAL));

if (!fragment.getBundlerVariableMap().isEmpty()) {
jw.emitEmptyLine();
Expand Down Expand Up @@ -649,7 +650,7 @@ public boolean process(Set<? extends TypeElement> type, RoundEnvironment env) {

if (!required.isEmpty()) {
jw.emitEmptyLine();
writeNewFragmentWithRequiredMethod(builderName, fragmentClass, jw, args);
writeNewFragmentWithRequiredMethod(builderName, fragment, jw, args);
}

Set<ArgumentAnnotatedField> optionalArguments = fragment.getOptionalFields();
Expand Down Expand Up @@ -831,11 +832,11 @@ private void writeAutoMapping(Map<String, String> mapping, Element[] element)
}
}

private void writeNewFragmentWithRequiredMethod(String builder, TypeElement element,
private void writeNewFragmentWithRequiredMethod(String builder, AnnotatedFragment fragment,
JavaWriter jw, String[] args) throws IOException {

if (supportAnnotations) jw.emitAnnotation("NonNull");
jw.beginMethod(element.getQualifiedName().toString(), "new" + element.getSimpleName(),
jw.beginMethod(fragment.getTypeParametersString() + fragment.getQualifiedName(), "new" + fragment.getSimpleName(),
EnumSet.of(Modifier.STATIC, Modifier.PUBLIC), args);
StringBuilder argNames = new StringBuilder();
for (int i = 1; i < args.length; i += 2) {
Expand Down Expand Up @@ -868,7 +869,7 @@ private void writeInjectMethod(JavaWriter jw, TypeElement element,
String fragmentType = supportAnnotations ? "@NonNull " + element.getSimpleName().toString()
: element.getSimpleName().toString();

jw.beginMethod("void", "injectArguments",
jw.beginMethod(fragment.getTypeParametersString() + "void", "injectArguments",
EnumSet.of(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC),
fragmentType, "fragment");

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.hannesdorfmann.fragmentargs.processor;

import org.junit.Test;

import static com.hannesdorfmann.fragmentargs.processor.CompileTest.assertClassCompilesWithoutError;

public class GenericsTest {

@Test
public void classWithGenerics() {
assertClassCompilesWithoutError("ClassWithGenerics.java", "ClassWithGenericsBuilder.java");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class InnerClassTest {

@Test
public void innerClass() {
assertClassCompilesWithoutError("ClassWithInnerClass.java", "ClassWithInnerClassBuilder.java");
assertClassCompilesWithoutError("ClassWithInnerClass.java", "ClassWithInnerClass$$InnerClassBuilder.java");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,17 @@
package com.hannesdorfmann.fragmentargs.processor;

import com.google.testing.compile.JavaFileObjects;

import org.junit.Test;

import static com.google.common.truth.Truth.assert_;
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
import static com.hannesdorfmann.fragmentargs.processor.CompileTest.assertClassCompilesWithoutError;

public class ProtectedAccessTest {

private static final String[] PROCESSOR_OPTIONS
= new String[]{"-AfragmentArgsSupportAnnotations=false"};

@Test
public void protectedField() {
assert_().about(javaSource())
.that(JavaFileObjects.forResource("ClassWithProtectedField.java"))
.withCompilerOptions(PROCESSOR_OPTIONS)
.processedWith(new ArgProcessor())
.compilesWithoutError();
assertClassCompilesWithoutError("ClassWithProtectedField.java", "ClassWithProtectedFieldBuilder.java");
}

@Test
public void protectedSetter() {
assert_().about(javaSource())
.that(JavaFileObjects.forResource("ClassWithProtectedSetter.java"))
.withCompilerOptions(PROCESSOR_OPTIONS)
.processedWith(new ArgProcessor())
.compilesWithoutError();
assertClassCompilesWithoutError("ClassWithProtectedSetter.java", "ClassWithProtectedSetterBuilder.java");
}
}
49 changes: 49 additions & 0 deletions processor/src/test/resources/ClassWithGenerics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.hannesdorfmann.fragmentargstest.test;

import android.os.Parcelable;

import java.io.Serializable;
import java.util.ArrayList;

@com.hannesdorfmann.fragmentargs.annotation.FragmentWithArgs
public class ClassWithGenerics<PS extends Parcelable & Serializable, P extends Parcelable, S extends Serializable> extends android.app.Fragment {

@com.hannesdorfmann.fragmentargs.annotation.Arg
ArrayList<PS> genericList;

@com.hannesdorfmann.fragmentargs.annotation.Arg
ArrayList<P> parcelableList;

@com.hannesdorfmann.fragmentargs.annotation.Arg
ArrayList<S> serializableList;

@com.hannesdorfmann.fragmentargs.annotation.Arg
PS generic;

@com.hannesdorfmann.fragmentargs.annotation.Arg
P parcelable;

@com.hannesdorfmann.fragmentargs.annotation.Arg(required = false)
P optionalParcelable;

@com.hannesdorfmann.fragmentargs.annotation.Arg
S serializable;

@com.hannesdorfmann.fragmentargs.annotation.Arg(required = false)
S optionalSerializable;

@com.hannesdorfmann.fragmentargs.annotation.Arg
private S privateSerializableGeneric;

@com.hannesdorfmann.fragmentargs.annotation.Arg
private P privateParcelableGeneric;

public void setPrivateSerializableGeneric(S privateSerializableGeneric) {
this.privateSerializableGeneric = privateSerializableGeneric;
}

public void setPrivateParcelableGeneric(P privateParcelableGeneric) {
this.privateParcelableGeneric = privateParcelableGeneric;
}

}
114 changes: 114 additions & 0 deletions processor/src/test/resources/ClassWithGenericsBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.hannesdorfmann.fragmentargstest.test;

import android.os.Bundle;

public final class ClassWithGenericsBuilder<PS extends android.os.Parcelable & java.io.Serializable, P extends android.os.Parcelable, S extends java.io.Serializable> {

private final Bundle mArguments = new Bundle();

public ClassWithGenericsBuilder(PS generic, java.util.ArrayList<PS> genericList, P parcelable, java.util.ArrayList<P> parcelableList, P privateParcelableGeneric, S privateSerializableGeneric, S serializable, java.util.ArrayList<S> serializableList) {

mArguments.putParcelable("generic", generic);

mArguments.putParcelableArrayList("genericList", genericList);

mArguments.putParcelable("parcelable", parcelable);

mArguments.putParcelableArrayList("parcelableList", parcelableList);

mArguments.putParcelable("privateParcelableGeneric", privateParcelableGeneric);

mArguments.putSerializable("privateSerializableGeneric", privateSerializableGeneric);

mArguments.putSerializable("serializable", serializable);

mArguments.putSerializable("serializableList", serializableList);
}

public static <PS extends android.os.Parcelable & java.io.Serializable, P extends android.os.Parcelable, S extends java.io.Serializable>ClassWithGenerics newClassWithGenerics(PS generic, java.util.ArrayList<PS> genericList, P parcelable, java.util.ArrayList<P> parcelableList, P privateParcelableGeneric, S privateSerializableGeneric, S serializable, java.util.ArrayList<S> serializableList) {
return new ClassWithGenericsBuilder(generic, genericList, parcelable, parcelableList, privateParcelableGeneric, privateSerializableGeneric, serializable, serializableList).build();
}

public ClassWithGenericsBuilder optionalParcelable(P optionalParcelable) {

if (optionalParcelable != null) {
mArguments.putParcelable("optionalParcelable", optionalParcelable);
}
return this;
}

public ClassWithGenericsBuilder optionalSerializable(S optionalSerializable) {

if (optionalSerializable != null) {
mArguments.putSerializable("optionalSerializable", optionalSerializable);
}
return this;
}

public Bundle buildBundle() {
return new Bundle(mArguments);
}

public static final <PS extends android.os.Parcelable & java.io.Serializable, P extends android.os.Parcelable, S extends java.io.Serializable>void injectArguments(ClassWithGenerics fragment) {
Bundle args = fragment.getArguments();
if (args == null) {
throw new IllegalStateException("No arguments set. Have you set up this Fragment with the corresponding FragmentArgs Builder? ");
}

if (!args.containsKey("privateSerializableGeneric")) {
throw new IllegalStateException("required argument privateSerializableGeneric is not set");
}
S value0 = (S) args.getSerializable("privateSerializableGeneric");
fragment.setPrivateSerializableGeneric(value0);

if (!args.containsKey("genericList")) {
throw new IllegalStateException("required argument genericList is not set");
}
fragment.genericList = args.getParcelableArrayList("genericList");

if (!args.containsKey("parcelable")) {
throw new IllegalStateException("required argument parcelable is not set");
}
fragment.parcelable = args.getParcelable("parcelable");

if (!args.containsKey("parcelableList")) {
throw new IllegalStateException("required argument parcelableList is not set");
}
fragment.parcelableList = args.getParcelableArrayList("parcelableList");

if (args != null && args.containsKey("optionalParcelable")) {
fragment.optionalParcelable = args.getParcelable("optionalParcelable");
}

if (!args.containsKey("privateParcelableGeneric")) {
throw new IllegalStateException("required argument privateParcelableGeneric is not set");
}
P value1 = args.getParcelable("privateParcelableGeneric");
fragment.setPrivateParcelableGeneric(value1);

if (!args.containsKey("serializable")) {
throw new IllegalStateException("required argument serializable is not set");
}
fragment.serializable = (S) args.getSerializable("serializable");

if (!args.containsKey("generic")) {
throw new IllegalStateException("required argument generic is not set");
}
fragment.generic = args.getParcelable("generic");

if (!args.containsKey("serializableList")) {
throw new IllegalStateException("required argument serializableList is not set");
}
fragment.serializableList = (java.util.ArrayList<S>) args.getSerializable("serializableList");

if (args != null && args.containsKey("optionalSerializable")) {
fragment.optionalSerializable = (S) args.getSerializable("optionalSerializable");
}
}

public ClassWithGenerics build() {
ClassWithGenerics fragment = new ClassWithGenerics();
fragment.setArguments(mArguments);
return fragment;
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
package com.hannesdorfmann.fragmentargs.processor.test;

import android.os.Bundle;
import com.hannesdorfmann.fragmentargs.processor.test.ClassWithInnerClass.InnerClass;

public final class ClassWithInnerClassBuilder {
public final class ClassWithInnerClass$$InnerClassBuilder {

private final Bundle mArguments = new Bundle();

public ClassWithInnerClassBuilder(String arg) {
public ClassWithInnerClass$$InnerClassBuilder(String arg) {

mArguments.putString("arg", arg);
}

public static ClassWithInnerClass newClassWithInnerClass(String arg) {
return new ClassWithInnerClassBuilder(arg).build();
public static InnerClass newInnerClass(String arg) {
return new ClassWithInnerClass$$InnerClassBuilder(arg).build();
}

public Bundle buildBundle() {
return new Bundle(mArguments);
}

public static final void injectArguments(ClassWithInnerClass fragment) {
public static final void injectArguments(InnerClass fragment) {
Bundle args = fragment.getArguments();
if (args == null) {
throw new IllegalStateException("No arguments set. Have you set up this Fragment with the corresponding FragmentArgs Builder? ");
Expand All @@ -31,8 +32,8 @@ public static final void injectArguments(ClassWithInnerClass fragment) {
fragment.arg = args.getString("arg");
}

public ClassWithInnerClass build() {
ClassWithInnerClass fragment = new ClassWithInnerClass();
public InnerClass build() {
InnerClass fragment = new InnerClass();
fragment.setArguments(mArguments);
return fragment;
}
Expand Down
Loading