/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint;
import static com.android.tools.lint.ExternalAnnotationRepository.FN_ANNOTATIONS_XML;
import static com.google.common.base.Charsets.UTF_8;
import static java.io.File.separatorChar;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.testutils.SdkTestCase;
import com.android.tools.lint.client.api.JavaParser.DefaultTypeDescriptor;
import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
import com.android.tools.lint.client.api.JavaParser.ResolvedField;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
import com.android.tools.lint.client.api.JavaParser.ResolvedPackage;
import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.LintUtilsTest;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import lombok.ast.ClassDeclaration;
import lombok.ast.ForwardingAstVisitor;
import lombok.ast.MethodDeclaration;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;
public class ExternalAnnotationRepositoryTest extends SdkTestCase {
@Nullable
private ExternalAnnotationRepository getSdkAnnotations() {
File annotations = findSrcRelativeDir("tools/adt/idea/android/annotations");
if (annotations != null) {
List<File> files = Collections.singletonList(annotations);
ExternalAnnotationRepository manager = ExternalAnnotationRepository.create(null, files);
assertNotNull(manager);
return manager;
} else {
// Can't find it when running from Gradle; ignore for now
//fail("Could not find annotations database");
}
return null;
}
@Nullable
private ExternalAnnotationRepository getExternalAnnotations(@NonNull String pkg,
@NonNull String contents) throws IOException {
File dir = Files.createTempDir();
try {
File pkgDir = new File(dir, pkg.replace('.', separatorChar));
boolean mkdirs = pkgDir.mkdirs();
assertTrue(mkdirs);
Files.write(contents, new File(pkgDir, FN_ANNOTATIONS_XML), UTF_8);
List<File> files = Collections.singletonList(dir);
ExternalAnnotationRepository manager = ExternalAnnotationRepository.create(null, files);
assertNotNull(manager);
return manager;
} finally {
deleteFile(dir);
}
}
public void testFields() throws Exception {
ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<root>\n"
+ " <item name=\"android.graphics.Color\">\n"
+ " <annotation name=\"android.support.annotation.Annotation1\" />\n"
+ " </item>\n"
+ " <item name=\"android.graphics.Color BLUE\">\n"
+ " <annotation name=\"android.support.annotation.Annotation3\" />\n"
+ " </item>\n"
+ " <item name=\"android.graphics.Color TRANSPARENT\">\n"
+ " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ " </item>\n"
+ "</root>\n");
assertNotNull(manager);
ResolvedClass cls = createClass("android.graphics.Color");
assertNotNull(manager.getAnnotation(cls, "android.support.annotation.Annotation1"));
ResolvedField blueField = createField("android.graphics.Color", "BLUE");
ResolvedField transparentField = createField("android.graphics.Color", "TRANSPARENT");
assertNotNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation3"));
assertNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation4"));
assertNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation5"));
assertNotNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation5"));
assertNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation3"));
assertNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation4"));
}
public void testMethods1() throws Exception {
ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<root>\n"
+ " <item name=\"android.graphics.Color int HSVToColor(float[]) 0\">\n"
+ " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ " </item>\n"
+ " <item name=\"android.graphics.Color int HSVToColor(int, float[]) 1\">\n"
+ " <annotation name=\"android.support.annotation.Annotation7\">\n"
+ " <val name=\"value\" val=\"3\" />\n"
+ " </annotation>\n"
+ " </item>\n"
+ " <item name=\"android.graphics.Color int alpha(int)\">\n"
+ " <annotation name=\"android.support.annotation.Annotation3\" />\n"
+ " </item>\n"
+ " <item name=\"android.graphics.Color int argb(int, int, int, int)\">\n"
+ " <annotation name=\"android.support.annotation.Annotation3\" />\n"
+ " </item>\n"
+ " <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode) 4\">\n"
+ " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ " </item>\n"
+ " <item name=\"android.graphics.ArrayAdapter ArrayAdapter(android.content.Context, int, int, java.util.List<T>) 3\">\n"
+ " <annotation name=\"android.support.annotation.Annotation4\" />\n"
+ " </item>"
+ "</root>\n");
assertNotNull(manager);
ResolvedMethod method1 = createMethod("android.graphics.Color", "int", "HSVToColor",
"float[]");
assertNotNull(manager.getAnnotation(method1, 0, "android.support.annotation.Annotation5"));
// Generic types
ResolvedMethod method2 = createConstructor("android.graphics.ArrayAdapter", "ArrayAdapter",
"android.content.Context, int, int, java.util.List<T>");
assertNotNull(manager.getAnnotation(method2, 3, "android.support.annotation.Annotation4"));
// Raw types
method2 = createConstructor("android.graphics.ArrayAdapter", "ArrayAdapter",
"android.content.Context, int, int, java.util.List");
assertNotNull(manager.getAnnotation(method2, 3, "android.support.annotation.Annotation4"));
}
public void testMethods2() throws Exception {
ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<root>\n"
+ " <item name=\"test.pkg.Test java.lang.Object myMethod()\">\n"
+ " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ " <annotation name=\"android.support.annotation.Annotation6\">\n"
+ " <val name=\"suggest\" val=\""#other(String,String)"\" />\n"
+ " </annotation>\n"
+ " </item>\n"
+ " <item name=\"test.pkg.Test java.lang.Object myMethod(int) 0\">\n"
+ " <annotation name=\"android.support.annotation.Annotation7\">\n"
+ " <val name=\"value\" val=\"3\" />\n"
+ " </annotation>\n"
+ " </item>\n"
+ " <item name=\"test.pkg.Test java.lang.Object myMethod(int[]) 0\">\n"
+ " <annotation name=\"android.support.annotation.Annotation3\" />\n"
+ " </item>\n"
+ " <item name=\"test.pkg.Test java.lang.Object myMethod(int,java.lang.Object) 1\">\n"
+ " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ " </item>\n"
+ " <item name=\"test.pkg.Test java.lang.Object myMethod(android.content.Context, int, int, java.util.List<T>) 0\">\n"
+ " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ " </item>\n"
+ " <item name=\"test.pkg.Test java.lang.Object myMethod(android.content.Context, int, int, java.util.List<T>) 1\">\n"
+ " <annotation name=\"android.support.annotation.Annotation3\" />\n"
+ " </item>\n"
+ " <item name=\"test.pkg.Test java.lang.Object myMethod(java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.String>>,int) 0\">\n"
+ " <annotation name=\"android.support.annotation.Annotation4\" />\n"
+ " </item>\n"
+ "</root>\n");
assertNotNull(manager);
ResolvedMethod method;
method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "");
assertNotNull(manager.getAnnotation(method, "android.support.annotation.Annotation5"));
assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
assertNotNull(manager.getAnnotation(method, "android.support.annotation.Annotation6"));
method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "int");
assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation7"));
method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "int[]");
assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation3"));
assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation3"));
method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
"int,java.lang.Object");
assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation5"));
method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
"android.content.Context, int, int, java.util.List<T>");
assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation5"));
assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation3"));
assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation5"));
assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation3"));
assertNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation5"));
method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
"android.content.Context, int, int, java.util.List");
assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation5"));
assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation3"));
method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
Arrays.asList("java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.String>>",
"int"), false);
assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
"java.util.Map,int");
assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
}
// test intdef!
public void testAnnotationAttributes() throws Exception {
ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<root>\n"
+ " <item name=\"android.graphics.Color int HSVToColor(float[]) 0\">\n"
+ " <annotation name=\"android.support.annotation.Annotation7\">\n"
+ " <val name=\"value\" val=\"3\" />\n"
+ " </annotation>\n"
+ " </item>\n"
+ " <item name=\"android.graphics.Color int HSVToColor(int, float[]) 1\">\n"
+ " <annotation name=\"android.support.annotation.Annotation7\">\n"
+ " <val name=\"value\" val=\"3\" />\n"
+ " </annotation>\n"
+ " </item>\n"
+ " <item name=\"android.graphics.Canvas void drawLines(float[], android.graphics.Paint) 0\">\n"
+ " <annotation name=\"android.support.annotation.Annotation7\">\n"
+ " <val name=\"min\" val=\"4\" />\n"
+ " <val name=\"multiple\" val=\"2\" />\n"
+ " </annotation>\n"
+ " <annotation name=\"android.support.annotation.Annotation4\" />\n"
+ " </item>\n"
+ " <item name=\"android.graphics.Canvas int saveLayer(android.graphics.RectF, android.graphics.Paint, int) 2\">\n"
+ " <annotation name=\"android.support.annotation.Annotation8\">\n"
+ " <val name=\"value\" val=\"{android.graphics.Canvas.MATRIX_SAVE_FLAG, android.graphics.Canvas.CLIP_SAVE_FLAG, android.graphics.Canvas.HAS_ALPHA_LAYER_SAVE_FLAG, android.graphics.Canvas.FULL_COLOR_LAYER_SAVE_FLAG, android.graphics.Canvas.CLIP_TO_LAYER_SAVE_FLAG, android.graphics.Canvas.ALL_SAVE_FLAG}\" />\n"
+ " <val name=\"flag\" val=\"true\" />\n"
+ " </annotation>\n"
+ " </item>\n"
+ "</root>\n");
assertNotNull(manager);
ResolvedMethod method;
ResolvedAnnotation annotation;
// Size 1
method = createMethod("android.graphics.Color", "int", "HSVToColor", "int, float[]");
assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation7"));
assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation7"));
annotation = manager.getAnnotation(method, 1, "android.support.annotation.Annotation7");
assertNotNull(annotation);
assertEquals(3L, annotation.getValue());
assertEquals(3L, annotation.getValue("value"));
//noinspection ConstantConditions
assertEquals(3, ((Number)annotation.getValue("value")).intValue());
assertNotNull(annotation);
// Size 2
method = createMethod("android.graphics.Canvas", "void", "drawLines", "float[], android.graphics.Paint");
assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
annotation = manager.getAnnotation(method, 0, "android.support.annotation.Annotation7");
assertNotNull(annotation);
assertEquals(4L, annotation.getValue("min"));
assertEquals(2L, annotation.getValue("multiple"));
assertNotNull(annotation);
// Intdef
method = createMethod("android.graphics.Canvas", "int", "saveLayer",
"android.graphics.RectF, android.graphics.Paint, int");
annotation = manager.getAnnotation(method, 2, "android.support.annotation.Annotation8");
assertNotNull(annotation);
assertEquals(true, annotation.getValue("flag"));
Object[] values = (Object[]) annotation.getValue("value");
assertNotNull(values);
assertEquals(6, values.length);
assertTrue(values[0] instanceof ResolvedField);
assertFalse(values[0].equals(createField("android.graphics.Canvas", "WRONG_NAME")));
assertEquals(values[0], createField("android.graphics.Canvas", "MATRIX_SAVE_FLAG"));
assertEquals(values[1], createField("android.graphics.Canvas", "CLIP_SAVE_FLAG"));
assertEquals(values[2], createField("android.graphics.Canvas", "HAS_ALPHA_LAYER_SAVE_FLAG"));
assertEquals(values[3], createField("android.graphics.Canvas", "FULL_COLOR_LAYER_SAVE_FLAG"));
assertEquals(values[4], createField("android.graphics.Canvas", "CLIP_TO_LAYER_SAVE_FLAG"));
assertEquals(values[5], createField("android.graphics.Canvas", "ALL_SAVE_FLAG"));
ResolvedField field = (ResolvedField)values[0];
assertEquals("android.graphics.Canvas.MATRIX_SAVE_FLAG", field.getSignature());
assertEquals("android.graphics.Canvas", field.getContainingClassName());
assertEquals("MATRIX_SAVE_FLAG", field.getName());
}
public void testConstructors() throws Exception {
ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<root>\n"
+ " <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode) 4\">\n"
+ " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ " </item>\n"
+ "</root>\n");
assertNotNull(manager);
ResolvedMethod method = createConstructor("android.graphics.RadialGradient",
"RadialGradient",
"float, float, float, int[], float[], android.graphics.Shader.TileMode");
assertNull(manager.getAnnotation(method, 4, "android.support.annotation.Annotation4"));
assertNotNull(manager.getAnnotation(method, 4, "android.support.annotation.Annotation5"));
}
public void testVarArgs() throws Exception {
ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<root>\n"
+ " <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int...) 3\">\n"
+ " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ " </item>\n"
+ " <item name=\"android.graphics.Bitmap android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]) 2\">\n"
+ " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ " </item>\n"
+ "</root>\n");
assertNotNull(manager);
// Match "..." in external annotation with ... in code lookup
ResolvedMethod method = createConstructor("android.graphics.RadialGradient",
"RadialGradient",
"float, float, float, int...");
assertNotNull(manager.getAnnotation(method, 3, "android.support.annotation.Annotation5"));
// Match "..." in external annotation with [] in code lookup
method = createConstructor("android.graphics.RadialGradient",
"RadialGradient",
"float, float, float, int[]");
assertNotNull(manager.getAnnotation(method, 3, "android.support.annotation.Annotation5"));
// Match "[]" in external annotation with [] in code lookup
method = createMethod("android.graphics.Bitmap",
"android.graphics.Bitmap",
"extractAlpha",
"android.graphics.Paint, int[]");
assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.Annotation5"));
// Match "[]" in external annotation with ... in code lookup
method = createMethod("android.graphics.Bitmap",
"android.graphics.Bitmap",
"extractAlpha",
"android.graphics.Paint, int...");
assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.Annotation5"));
}
public void testPackage() throws Exception {
ExternalAnnotationRepository manager = getExternalAnnotations("foo.bar.baz", ""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<root>\n"
+ " <item name=\"foo.bar.baz.package-info\">\n"
+ " <annotation name=\"my.pkg.MyAnnotation\"/>\n"
+ " </item>\n"
+ "</root>\n");
assertNotNull(manager);
ResolvedClass cls = createClass("foo.bar.baz.AdView");
ResolvedPackage pkg = cls.getPackage();
assertNotNull(pkg);
assertNull(manager.getAnnotation(pkg, "foo.bar.Baz"));
assertNotNull(manager.getAnnotation(pkg, "my.pkg.MyAnnotation"));
}
public void testMatchWithEcj() throws Exception {
try {
ExternalAnnotationRepository manager = getExternalAnnotations("test.pkg", ""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<root>\n"
+ " <item name=\"test.pkg.Test\">\n"
+ " <annotation name=\"android.support.annotation.Annotation1\" />\n"
+ " </item>\n"
+ " <item name=\"test.pkg.Test.Inner\">\n"
+ " <annotation name=\"android.support.annotation.Annotation2\" />\n"
+ " </item>\n"
+ " <item name=\"test.pkg.Test void foo(int, int[], int...)\">\n"
+ " <annotation name=\"android.support.annotation.Annotation6\" />\n"
+ " </item>\n"
+ " <item name=\"test.pkg.Test void foo(int, int[], int...) 0\">\n"
+ " <annotation name=\"android.support.annotation.Annotation3\" />\n"
+ " </item>\n"
+ " <item name=\"test.pkg.Test void foo(int, int[], int...) 1\">\n"
+ " <annotation name=\"android.support.annotation.Annotation3\" />\n"
+ " <annotation name=\"android.support.annotation.Annotation4\" />\n"
+ " </item>\n"
+ " <item name=\"test.pkg.Test void foo(int, int[], int...) 2\">\n"
+ " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ " </item>\n"
+ "</root>\n");
assertNotNull(manager);
ExternalAnnotationRepository.set(manager);
String source =
"package test.pkg;\n" +
"\n" +
"public class Test {\n" +
" public void foo(int a, int[] b, int... c) {\n" +
" }\n" +
" public static class Inner {\n" +
" }\n" +
"}\n";
final JavaContext context = LintUtilsTest.parse(source,
new File("src/test/pkg/Test.java"));
assertNotNull(context);
Node unit = context.getCompilationUnit();
assertNotNull(unit);
unit.accept(new ForwardingAstVisitor() {
@Override
public boolean visitClassDeclaration(ClassDeclaration node) {
ResolvedNode resolved = context.resolve(node);
assertNotNull(resolved);
assertTrue(resolved.getClass().getName(), resolved instanceof ResolvedClass);
ResolvedClass cls = (ResolvedClass) resolved;
if (cls.getName().endsWith(".Inner")) {
assertNull(cls.getAnnotation("android.support.annotation.Annotation1"));
assertNotNull(cls.getAnnotation("android.support.annotation.Annotation2"));
} else {
assertNotNull(cls.getAnnotation("android.support.annotation.Annotation1"));
assertNull(cls.getAnnotation("android.support.annotation.Annotation2"));
}
return super.visitClassDeclaration(node);
}
@Override
public boolean visitMethodDeclaration(MethodDeclaration node) {
ResolvedNode resolved = context.resolve(node);
assertNotNull(resolved);
assertTrue(resolved.getClass().getName(), resolved instanceof ResolvedMethod);
ResolvedMethod method = (ResolvedMethod) resolved;
assertNull(method.getAnnotation("android.support.annotation.Annotation5"));
assertNotNull(method.getAnnotation("android.support.annotation.Annotation6"));
assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation3", 0));
assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation4", 1));
assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation3", 1));
assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation5", 2));
return super.visitMethodDeclaration(node);
}
});
} finally {
ExternalAnnotationRepository.set(null);
}
}
public void testMergeParameters() throws Exception {
try {
ExternalAnnotationRepository manager = getExternalAnnotations("test.pkg", ""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<root>\n"
+ " <item name=\"test.pkg.Test.Parent void testMethod(int)\">\n"
+ " <annotation name=\"android.support.annotation.Annotation1\" />\n"
+ " </item>\n"
+ " <item name=\"test.pkg.Test.Child void testMethod(int)\">\n"
+ " <annotation name=\"android.support.annotation.Annotation2\" />\n"
+ " </item>\n"
+ "</root>\n");
assertNotNull(manager);
ExternalAnnotationRepository.set(manager);
String source = ""
+ "package test.pkg;\n"
+ "\n"
+ "public class Test {\n"
+ " public void test(Child child) {\n"
+ " child.testMethod(5);\n"
+ " }\n"
+ "\n"
+ " public static class Parent {\n"
+ " public void testMethod(int parameter) {\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " public static class Child extends Parent {\n"
+ " @Override\n"
+ " public void testMethod(int parameter) {\n"
+ " }\n"
+ " }\n"
+ "}\n";
final JavaContext context = LintUtilsTest.parse(source,
new File("src/test/pkg/Test.java"));
assertNotNull(context);
Node unit = context.getCompilationUnit();
assertNotNull(unit);
final AtomicBoolean foundMethod = new AtomicBoolean();
unit.accept(new ForwardingAstVisitor() {
@Override
public boolean visitMethodInvocation(MethodInvocation node) {
foundMethod.set(true);
assertEquals("testMethod", node.astName().astValue());
ResolvedNode resolved = context.resolve(node);
assertTrue(resolved instanceof ResolvedMethod);
ResolvedMethod method = (ResolvedMethod)resolved;
List<ResolvedAnnotation> annotations =
Lists.newArrayList(method.getAnnotations());
assertEquals(3, annotations.size());
Collections.sort(annotations,
new Comparator<ResolvedAnnotation>() {
@Override
public int compare(ResolvedAnnotation a1,
ResolvedAnnotation a2) {
return a1.getName().compareTo(a2.getName());
}
});
assertEquals("android.support.annotation.Annotation1", annotations.get(0).getName());
assertEquals("android.support.annotation.Annotation2", annotations.get(1).getName());
assertEquals("java.lang.Override", annotations.get(2).getName());
return super.visitMethodInvocation(node);
}
});
assertTrue(foundMethod.get());
} finally {
ExternalAnnotationRepository.set(null);
}
}
public void testSdkAnnotations() throws Exception {
ExternalAnnotationRepository manager = getSdkAnnotations();
if (manager == null) {
// Can't find it when running from Gradle; ignore for now
return;
}
ResolvedMethod method = createMethod("android.view.LayoutInflater", "android.view.View",
"createView", "java.lang.String, java.lang.String, android.util.AttributeSet");
assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.NonNull"));
}
private static ResolvedClass createClass(String name) {
ResolvedClass mock = mock(ResolvedClass.class);
when(mock.getName()).thenReturn(name);
when(mock.getSignature()).thenReturn(name);
assertTrue(name, name.indexOf('.') != -1);
ResolvedPackage pkg = createPackage(name.substring(0, name.lastIndexOf('.')));
when(mock.getPackage()).thenReturn(pkg);
return mock;
}
private static ResolvedMethod createConstructor(String containingClass, String name,
String parameters) {
return createMethod(containingClass, null, name, parameters, true);
}
public static ResolvedMethod createMethod(String containingClass, String returnType,
String name, String parameters) {
return createMethod(containingClass, returnType, name, parameters, false);
}
public static ResolvedMethod createMethod(String containingClass, String returnType,
String name, String parameters, boolean isConstructor) {
return createMethod(containingClass, returnType, name,
Splitter.on(',').trimResults().split(parameters), isConstructor);
}
public static ResolvedMethod createMethod(String containingClass, String returnType,
String name, Iterable<String> parameters, boolean isConstructor) {
ResolvedMethod mock = mock(ResolvedMethod.class);
when(mock.isConstructor()).thenReturn(isConstructor);
when(mock.getName()).thenReturn(name);
if (!isConstructor) {
DefaultTypeDescriptor typeDescriptor = new DefaultTypeDescriptor(returnType);
when(mock.getReturnType()).thenReturn(typeDescriptor);
}
ResolvedClass cls = createClass(containingClass);
when(mock.getContainingClass()).thenReturn(cls);
int index = 0;
for (String argument : parameters) {
TypeDescriptor typeDescriptor = new DefaultTypeDescriptor(argument);
when(mock.getArgumentType(index)).thenReturn(typeDescriptor);
index++;
}
when(mock.getArgumentCount()).thenReturn(index);
return mock;
}
public static ResolvedField createField(String containingClass, String name) {
ResolvedField mock = mock(ResolvedField.class);
when(mock.getName()).thenReturn(name);
ResolvedClass cls = createClass(containingClass);
when(mock.getContainingClass()).thenReturn(cls);
return mock;
}
public static ResolvedPackage createPackage(String pkgName) {
ResolvedPackage mock = mock(ResolvedPackage.class);
when(mock.getName()).thenReturn(pkgName);
return mock;
}
}