diff --git a/pom.xml b/pom.xml index 4d6ed60..34ddee8 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ 4.10 - 0.5 + 0.15 0.13 diff --git a/processor/src/main/java/com/hannesdorfmann/fragmentargs/processor/AnnotatedFragment.java b/processor/src/main/java/com/hannesdorfmann/fragmentargs/processor/AnnotatedFragment.java index 05f832f..1949598 100644 --- a/processor/src/main/java/com/hannesdorfmann/fragmentargs/processor/AnnotatedFragment.java +++ b/processor/src/main/java/com/hannesdorfmann/fragmentargs/processor/AnnotatedFragment.java @@ -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; @@ -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 @@ -156,6 +160,49 @@ public boolean isInnerClass() { return classElement.getEnclosingElement().getKind() == ElementKind.CLASS; } + /** + * Builds a type parameter string + * e.g. {@literal } + */ + public String getTypeParametersString() { + StringBuilder parametersBuilder = new StringBuilder(); + Iterator typeParametersIterator = classElement.getTypeParameters().iterator(); + + if(typeParametersIterator.hasNext()) { + parametersBuilder.append("<"); + + while(typeParametersIterator.hasNext()) { + TypeParameterElement parameterElement = typeParametersIterator.next(); + + // e.g. "V" + parametersBuilder.append(parameterElement.getSimpleName()); + + Iterator 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 getAll() { Set all = new HashSet(getRequiredFields()); all.addAll(getOptionalFields()); diff --git a/processor/src/main/java/com/hannesdorfmann/fragmentargs/processor/ArgProcessor.java b/processor/src/main/java/com/hannesdorfmann/fragmentargs/processor/ArgProcessor.java index 07c341c..181c56a 100644 --- a/processor/src/main/java/com/hannesdorfmann/fragmentargs/processor/ArgProcessor.java +++ b/processor/src/main/java/com/hannesdorfmann/fragmentargs/processor/ArgProcessor.java @@ -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; @@ -614,7 +615,7 @@ public boolean process(Set 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(); @@ -649,7 +650,7 @@ public boolean process(Set type, RoundEnvironment env) { if (!required.isEmpty()) { jw.emitEmptyLine(); - writeNewFragmentWithRequiredMethod(builderName, fragmentClass, jw, args); + writeNewFragmentWithRequiredMethod(builderName, fragment, jw, args); } Set optionalArguments = fragment.getOptionalFields(); @@ -831,11 +832,11 @@ private void writeAutoMapping(Map 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) { @@ -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"); diff --git a/processor/src/test/java/com/hannesdorfmann/fragmentargs/processor/GenericsTest.java b/processor/src/test/java/com/hannesdorfmann/fragmentargs/processor/GenericsTest.java new file mode 100644 index 0000000..91a2bc1 --- /dev/null +++ b/processor/src/test/java/com/hannesdorfmann/fragmentargs/processor/GenericsTest.java @@ -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"); + } + +} diff --git a/processor/src/test/java/com/hannesdorfmann/fragmentargs/processor/InnerClassTest.java b/processor/src/test/java/com/hannesdorfmann/fragmentargs/processor/InnerClassTest.java index be3dbd0..9ceec30 100644 --- a/processor/src/test/java/com/hannesdorfmann/fragmentargs/processor/InnerClassTest.java +++ b/processor/src/test/java/com/hannesdorfmann/fragmentargs/processor/InnerClassTest.java @@ -8,7 +8,7 @@ public class InnerClassTest { @Test public void innerClass() { - assertClassCompilesWithoutError("ClassWithInnerClass.java", "ClassWithInnerClassBuilder.java"); + assertClassCompilesWithoutError("ClassWithInnerClass.java", "ClassWithInnerClass$$InnerClassBuilder.java"); } @Test diff --git a/processor/src/test/java/com/hannesdorfmann/fragmentargs/processor/ProtectedAccessTest.java b/processor/src/test/java/com/hannesdorfmann/fragmentargs/processor/ProtectedAccessTest.java index cd183fa..cba3f9d 100644 --- a/processor/src/test/java/com/hannesdorfmann/fragmentargs/processor/ProtectedAccessTest.java +++ b/processor/src/test/java/com/hannesdorfmann/fragmentargs/processor/ProtectedAccessTest.java @@ -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"); } } diff --git a/processor/src/test/resources/ClassWithGenerics.java b/processor/src/test/resources/ClassWithGenerics.java new file mode 100644 index 0000000..2189c30 --- /dev/null +++ b/processor/src/test/resources/ClassWithGenerics.java @@ -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 extends android.app.Fragment { + + @com.hannesdorfmann.fragmentargs.annotation.Arg + ArrayList genericList; + + @com.hannesdorfmann.fragmentargs.annotation.Arg + ArrayList

parcelableList; + + @com.hannesdorfmann.fragmentargs.annotation.Arg + ArrayList 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; + } + +} \ No newline at end of file diff --git a/processor/src/test/resources/ClassWithGenericsBuilder.java b/processor/src/test/resources/ClassWithGenericsBuilder.java new file mode 100644 index 0000000..ae0758d --- /dev/null +++ b/processor/src/test/resources/ClassWithGenericsBuilder.java @@ -0,0 +1,114 @@ +package com.hannesdorfmann.fragmentargstest.test; + +import android.os.Bundle; + +public final class ClassWithGenericsBuilder { + + private final Bundle mArguments = new Bundle(); + + public ClassWithGenericsBuilder(PS generic, java.util.ArrayList genericList, P parcelable, java.util.ArrayList

parcelableList, P privateParcelableGeneric, S privateSerializableGeneric, S serializable, java.util.ArrayList 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 ClassWithGenerics newClassWithGenerics(PS generic, java.util.ArrayList genericList, P parcelable, java.util.ArrayList

parcelableList, P privateParcelableGeneric, S privateSerializableGeneric, S serializable, java.util.ArrayList 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 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) 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; + } +} \ No newline at end of file diff --git a/processor/src/test/resources/ClassWithInnerClassBuilder.java b/processor/src/test/resources/ClassWithInnerClass$$InnerClassBuilder.java similarity index 61% rename from processor/src/test/resources/ClassWithInnerClassBuilder.java rename to processor/src/test/resources/ClassWithInnerClass$$InnerClassBuilder.java index 5ed54d0..dc645ee 100644 --- a/processor/src/test/resources/ClassWithInnerClassBuilder.java +++ b/processor/src/test/resources/ClassWithInnerClass$$InnerClassBuilder.java @@ -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? "); @@ -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; } diff --git a/processor/src/test/resources/ClassWithInnerClass.java b/processor/src/test/resources/ClassWithInnerClass.java index 800b84c..a132b7c 100644 --- a/processor/src/test/resources/ClassWithInnerClass.java +++ b/processor/src/test/resources/ClassWithInnerClass.java @@ -1,11 +1,7 @@ package com.hannesdorfmann.fragmentargs.processor.test; -@com.hannesdorfmann.fragmentargs.annotation.FragmentWithArgs public class ClassWithInnerClass extends android.app.Fragment { - @com.hannesdorfmann.fragmentargs.annotation.Arg - String arg; - @com.hannesdorfmann.fragmentargs.annotation.FragmentWithArgs public static class InnerClass extends android.app.Fragment { diff --git a/processor/src/test/resources/ClassWithProtectedFieldBuilder.java b/processor/src/test/resources/ClassWithProtectedFieldBuilder.java new file mode 100644 index 0000000..2c652a7 --- /dev/null +++ b/processor/src/test/resources/ClassWithProtectedFieldBuilder.java @@ -0,0 +1,39 @@ +package com.hannesdorfmann.fragmentargs.processor.test; + +import android.os.Bundle; + +public final class ClassWithProtectedFieldBuilder { + + private final Bundle mArguments = new Bundle(); + + public ClassWithProtectedFieldBuilder(String protectedArg) { + + mArguments.putString("protectedArg", protectedArg); + } + + public static ClassWithProtectedField newClassWithProtectedField(String protectedArg) { + return new ClassWithProtectedFieldBuilder(protectedArg).build(); + } + + public Bundle buildBundle() { + return new Bundle(mArguments); + } + + public static final void injectArguments(ClassWithProtectedField 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("protectedArg")) { + throw new IllegalStateException("required argument protectedArg is not set"); + } + fragment.protectedArg = args.getString("protectedArg"); + } + + public ClassWithProtectedField build() { + ClassWithProtectedField fragment = new ClassWithProtectedField(); + fragment.setArguments(mArguments); + return fragment; + } +} \ No newline at end of file diff --git a/processor/src/test/resources/ClassWithProtectedSetterBuilder.java b/processor/src/test/resources/ClassWithProtectedSetterBuilder.java new file mode 100644 index 0000000..07a0fd8 --- /dev/null +++ b/processor/src/test/resources/ClassWithProtectedSetterBuilder.java @@ -0,0 +1,40 @@ +package com.hannesdorfmann.fragmentargs.processor.test; + +import android.os.Bundle; + +public final class ClassWithProtectedSetterBuilder { + + private final Bundle mArguments = new Bundle(); + + public ClassWithProtectedSetterBuilder(String privateArg) { + + mArguments.putString("privateArg", privateArg); + } + + public static ClassWithProtectedSetter newClassWithProtectedSetter(String privateArg) { + return new ClassWithProtectedSetterBuilder(privateArg).build(); + } + + public Bundle buildBundle() { + return new Bundle(mArguments); + } + + public static final void injectArguments(ClassWithProtectedSetter 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("privateArg")) { + throw new IllegalStateException("required argument privateArg is not set"); + } + java.lang.String value0 = args.getString("privateArg"); + fragment.setPrivateArg(value0); + } + + public ClassWithProtectedSetter build() { + ClassWithProtectedSetter fragment = new ClassWithProtectedSetter(); + fragment.setArguments(mArguments); + return fragment; + } +} \ No newline at end of file