Skip to content

Commit cd4c640

Browse files
author
Shintaro Katafuchi
committed
Merge pull request #29 from hotchemi/fix-14
Changed @RuntimePermissions to TypeMirror-based class validation
2 parents 8c65aa6 + b3f262f commit cd4c640

File tree

11 files changed

+194
-60
lines changed

11 files changed

+194
-60
lines changed

permissionsdispatcher-processor/src/main/java/permissions/dispatcher/processor/ClassType.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@ enum ClassType {
1010
this.activity = activity;
1111
}
1212

13-
static ClassType getClassType(String className) {
14-
if (className.endsWith("Activity")) {
13+
public String getActivity() {
14+
return activity;
15+
}
16+
17+
static ClassType getClassType(String className, TypeResolver resolver) {
18+
if (resolver.isSubTypeOf(className, ConstantsProvider.ACTIVITY)) {
1519
return ACTIVITY;
16-
} else if (className.endsWith("Fragment")) {
20+
} else if (resolver.isSubTypeOf(className, ConstantsProvider.V4FRAGMENT)) {
1721
return FRAGMENT;
1822
}
1923
return null;
2024
}
2125

22-
public String getActivity() {
23-
return activity;
24-
}
25-
2626
}

permissionsdispatcher-processor/src/main/java/permissions/dispatcher/processor/ConstantsProvider.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ interface ConstantsProvider {
66

77
ClassName ACTIVITY_COMPAT = ClassName.get("android.support.v4.app", "ActivityCompat");
88
ClassName PERMISSION_UTILS = ClassName.get("permissions.dispatcher", "PermissionUtils");
9+
String ACTIVITY = "android.app.Activity";
10+
String V4FRAGMENT = "android.support.v4.app.Fragment";
911
String CLASS_SUFFIX = "PermissionsDispatcher";
1012
String METHOD_SUFFIX = "WithCheck";
1113
String REQUEST_CODE_PREFIX = "REQUEST_";

permissionsdispatcher-processor/src/main/java/permissions/dispatcher/processor/PermissionsProcessor.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
import javax.annotation.processing.RoundEnvironment;
1717
import javax.lang.model.SourceVersion;
1818
import javax.lang.model.element.TypeElement;
19+
import javax.lang.model.type.TypeMirror;
20+
import javax.lang.model.util.Elements;
21+
import javax.lang.model.util.Types;
1922
import javax.tools.Diagnostic;
2023

2124
import permissions.dispatcher.RuntimePermissions;
@@ -24,7 +27,7 @@
2427
import static permissions.dispatcher.processor.Utils.getAnnotatedClasses;
2528

2629
@AutoService(Processor.class)
27-
public class PermissionsProcessor extends AbstractProcessor {
30+
public class PermissionsProcessor extends AbstractProcessor implements TypeResolver {
2831

2932
private Filer filer;
3033

@@ -49,7 +52,7 @@ public synchronized void init(ProcessingEnvironment env) {
4952

5053
@Override
5154
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
52-
List<RuntimePermissionsAnnotatedElement> classes = getAnnotatedClasses(env);
55+
List<RuntimePermissionsAnnotatedElement> classes = getAnnotatedClasses(env, this);
5356
for (RuntimePermissionsAnnotatedElement clazz : classes) {
5457
JavaFile javaFile = createJavaFile(clazz);
5558
try {
@@ -61,4 +64,12 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
6164
return true;
6265
}
6366

67+
@Override
68+
public boolean isSubTypeOf(String subTypeClass, String superTypeClass) {
69+
Types types = processingEnv.getTypeUtils();
70+
Elements elements = processingEnv.getElementUtils();
71+
TypeMirror subType = types.getDeclaredType(elements.getTypeElement(subTypeClass));
72+
TypeMirror superType = types.getDeclaredType(elements.getTypeElement(superTypeClass));
73+
return types.isSubtype(subType, superType);
74+
}
6475
}

permissionsdispatcher-processor/src/main/java/permissions/dispatcher/processor/RuntimePermissionsAnnotatedElement.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ class RuntimePermissionsAnnotatedElement {
3232

3333
private final List<ExecutableElement> deniedPermissionsMethods;
3434

35-
RuntimePermissionsAnnotatedElement(TypeElement element) {
35+
RuntimePermissionsAnnotatedElement(TypeElement element, TypeResolver resolver) {
3636
String qualifiedName = element.getQualifiedName().toString();
3737
packageName = Utils.getPackageName(qualifiedName);
3838
className = Utils.getClassName(qualifiedName);
39-
checkClassName(className);
40-
classType = ClassType.getClassType(className);
39+
classType = ClassType.getClassType(qualifiedName, resolver);
40+
checkClassType(classType);
4141
needsPermissionMethods = findMethods(element, NeedsPermission.class);
4242
validateNeedsPermissionMethods();
4343
needsPermissionsMethods = findMethods(element, NeedsPermissions.class);
@@ -122,8 +122,8 @@ public ExecutableElement getDeniedPermissionFromValue(String value) {
122122
return findDeniedPermissionFromValue(value, deniedPermissionMethods);
123123
}
124124

125-
public ExecutableElement getDeniedPermissionFromValue(String[] value) {
126-
return findDeniedPermissionFromValue(value, deniedPermissionsMethods);
125+
public ExecutableElement getDeniedPermissionsFromValue(String[] value) {
126+
return findDeniedPermissionsFromValue(value, deniedPermissionsMethods);
127127
}
128128

129129
public ExecutableElement getShowsRationaleFromValue(String value) {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package permissions.dispatcher.processor;
2+
3+
interface TypeResolver {
4+
5+
boolean isSubTypeOf(String subTypeClass, String superTypeClass);
6+
7+
}

permissionsdispatcher-processor/src/main/java/permissions/dispatcher/processor/Utils.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ final class Utils {
3232
private Utils() {
3333
}
3434

35-
static List<RuntimePermissionsAnnotatedElement> getAnnotatedClasses(RoundEnvironment env) {
35+
static List<RuntimePermissionsAnnotatedElement> getAnnotatedClasses(RoundEnvironment env, TypeResolver resolver) {
3636
List<RuntimePermissionsAnnotatedElement> models = new ArrayList<>();
3737
Set<? extends Element> elements = env.getElementsAnnotatedWith(RuntimePermissions.class);
3838
for (Element element : elements) {
39-
models.add(new RuntimePermissionsAnnotatedElement((TypeElement) element));
39+
models.add(new RuntimePermissionsAnnotatedElement((TypeElement) element, resolver));
4040
}
4141
return models;
4242
}
@@ -82,7 +82,7 @@ static ExecutableElement findDeniedPermissionFromValue(String value, List<Execut
8282
return null;
8383
}
8484

85-
static ExecutableElement findDeniedPermissionFromValue(String[] value, List<ExecutableElement> elements) {
85+
static ExecutableElement findDeniedPermissionsFromValue(String[] value, List<ExecutableElement> elements) {
8686
for (ExecutableElement element : elements) {
8787
DeniedPermissions annotation = element.getAnnotation(DeniedPermissions.class);
8888
if (deepEquals(value, annotation.value())) {
@@ -111,15 +111,19 @@ static <A extends Annotation> List<String> getValueFromAnnotation(ExecutableElem
111111
}
112112

113113
static ExecutableElement findDeniedPermissionFromElement(RuntimePermissionsAnnotatedElement element, ExecutableElement method) {
114+
Annotation annotation = method.getAnnotation(NeedsPermission.class);
115+
if (annotation == null) {
116+
annotation = method.getAnnotation(NeedsPermissions.class);
117+
}
118+
List<String> annotationValues = getValueFromAnnotation(method, annotation.annotationType());
119+
if (isEmpty(annotationValues)) {
120+
return null;
121+
}
114122
ExecutableElement deniedPermission;
115-
// Check presence of @NeedsPermission first
116-
List<String> annotationValues = Utils.getValueFromAnnotation(method, NeedsPermission.class);
117-
if (!annotationValues.isEmpty()) {
123+
if (Objects.equals(annotation.annotationType(), NeedsPermission.class)) {
118124
deniedPermission = element.getDeniedPermissionFromValue(annotationValues.get(0));
119125
} else {
120-
// Check presence of @NeedsPermissions next
121-
annotationValues = Utils.getValueFromAnnotation(method, NeedsPermissions.class);
122-
deniedPermission = element.getDeniedPermissionFromValue(annotationValues.toArray(new String[annotationValues.size()]));
126+
deniedPermission = element.getDeniedPermissionsFromValue(annotationValues.toArray(new String[annotationValues.size()]));
123127
}
124128
return deniedPermission;
125129
}

permissionsdispatcher-processor/src/main/java/permissions/dispatcher/processor/Validator.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ static void checkNeedsPermissionsSize(List<ExecutableElement> permission, List<E
2828
}
2929
}
3030

31-
static void checkClassName(String name) {
32-
if (name.endsWith("Activity") || name.endsWith("Fragment")) {
31+
static void checkClassType(ClassType classType) {
32+
if (classType != null) {
3333
return;
3434
}
35-
throw new WrongClassException("Annotated class must be finished with 'Activity' or 'Fragment'");
35+
throw new WrongClassException("Annotated class must be a subclass of 'android.app.Activity' or 'android.support.v4.app.Fragment'");
3636
}
3737

3838
static void checkDuplicatedValue(List<ExecutableElement> methods, Class<? extends Annotation> clazz) {

permissionsdispatcher-processor/src/test/java/permissions/dispatcher/processor/ClassTypeTest.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,4 @@ public void getActivityTest() {
1515
assertThat(ClassType.FRAGMENT.getActivity()).isEqualTo("target.getActivity()");
1616
}
1717

18-
@Test
19-
public void getClassTypeTest() {
20-
assertThat(ClassType.getClassType("MainActivity")).isEqualTo(ClassType.ACTIVITY);
21-
assertThat(ClassType.getClassType("MainFragment")).isEqualTo(ClassType.FRAGMENT);
22-
assertThat(ClassType.getClassType("FragmentMain")).isNull();
23-
assertThat(ClassType.getClassType("ActivityMain")).isNull();
24-
}
25-
2618
}

permissionsdispatcher-processor/src/test/java/permissions/dispatcher/processor/PermissionsProcessorTest.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,21 @@ public void twoDeniedPermission() {
9797
assertJavaSource(actual, expect);
9898
}
9999

100+
@Test
101+
public void nestedSuperClassActivity() {
102+
JavaFileObject actual = forSourceLines(DEFAULT_CLASS, Source.NestedSuperClassActivity.ACTUAL);
103+
JavaFileObject expect = forSourceLines(DEFAULT_CLASS + CLASS_SUFFIX, Source.NestedSuperClassActivity.EXPECT);
104+
assertJavaSource(actual, expect);
105+
}
106+
107+
@Test
108+
public void nestedSuperClassFragment() {
109+
String className = "MainFragment";
110+
JavaFileObject actual = forSourceLines(className, Source.NestedSuperClassFragment.ACTUAL);
111+
JavaFileObject expect = forSourceLines(className + CLASS_SUFFIX, Source.NestedSuperClassFragment.EXPECT);
112+
assertJavaSource(actual, expect);
113+
}
114+
100115
@Test
101116
public void deniedPermissionWithoutNeedsPermission() {
102117
expectedException.expect(RuntimeException.class);
@@ -152,11 +167,11 @@ public void zeroPermission() {
152167
}
153168

154169
@Test
155-
public void wrongClassName() {
170+
public void wrongSuperClassType() {
156171
expectedException.expect(RuntimeException.class);
157-
expectedException.expectMessage("Annotated class must be finished with 'Activity' or 'Fragment'");
172+
expectedException.expectMessage("Annotated class must be a subclass of 'android.app.Activity' or 'android.support.v4.app.Fragment'");
158173
String className = "MainUtils";
159-
JavaFileObject actual = forSourceLines(className, Source.WrongClassName.ACTUAL);
174+
JavaFileObject actual = forSourceLines(className, Source.WrongSuperClassType.ACTUAL);
160175
JavaFileObject expect = forSourceLines(className + CLASS_SUFFIX, Source.EMPTY);
161176
assertJavaSource(actual, expect);
162177
}

permissionsdispatcher-processor/src/test/java/permissions/dispatcher/processor/ValidatorTest.java

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
import javax.lang.model.element.ExecutableElement;
1212

13-
import static permissions.dispatcher.processor.Validator.checkClassName;
1413
import static permissions.dispatcher.processor.Validator.checkNeedsPermissionsSize;
1514

1615
/**
@@ -34,24 +33,4 @@ public void invalidEmpty() {
3433
checkNeedsPermissionsSize(null, null);
3534
}
3635

37-
@Test
38-
public void validCheckClassName() {
39-
checkClassName("MainActivity");
40-
checkClassName("MainFragment");
41-
}
42-
43-
@Test
44-
public void invalidCheckClassName1() {
45-
expectedException.expect(RuntimeException.class);
46-
expectedException.expectMessage("Annotated class must be finished with 'Activity' or 'Fragment'");
47-
checkClassName("ActivityMain");
48-
}
49-
50-
@Test
51-
public void invalidCheckClassName2() {
52-
expectedException.expect(RuntimeException.class);
53-
expectedException.expectMessage("Annotated class must be finished with 'Activity' or 'Fragment'");
54-
checkClassName("FragmentMain");
55-
}
56-
5736
}

0 commit comments

Comments
 (0)