/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.kernel.annotation;
import com.liferay.portal.kernel.test.rule.CodeCoverageAssertor;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
/**
* <p>
* <table>
* <tr>
* <th colspan = 3>
* Test Classes Inherited Hierarchy
* </th>
* </tr>
* <tr valign="top">
* <td>
* <pre>
* @Type(value = 5)
* OriginClass { -------->
* @Method(value = 5)
* @Mix(value = 5)
* originMethod1()
* originMethod2()
* }
* </pre>
* </td>
* <td>
* <pre>
* @Mix(value = 8)
* OriginInterface2 { -------->
* @Method(value = 8)
* originMethod2()
* }
* </pre>
* </td>
* <td>
* <pre>
* @Type(value = 9)
* OriginInterface1 {
* @Method(value = 9)
* @Mix(value = 9)
* originMethod1()
* }
* </pre>
* </td>
* </tr>
* <tr valign="top">
* <td>
* <pre>
* ^
* |
* </pre>
* </td>
* <td>
* <pre>
* </pre>
* </td>
* <td>
* <pre>
* ^
* |
* </pre>
* </td>
* </tr>
* <tr valign="top">
* <td>
* <pre>
* @Mix(value = 2)
* SuperClass { -------->
* @Method(value = 2)
* originMethod2()
* @Method(value = 2)
* superMethod1()
* superMethod2()
* }
* </pre>
* </td>
* <td>
* <pre>
* @Type(value = 6)
* SuperInterface2 { -------->
* @Method(value = 6)
* @Mix(value = 6)
* originMethod1()
* @Method(value = 6)
* @Mix(value = 6)
* superMethod2()
* }
* </pre>
* </td>
* <td>
* <pre>
* @Mix(value = 7)
* SuperInterface1 {
* @Method(value = 7)
* superMethod1()
* }
* </pre>
* </td>
* </tr>
* <tr valign="top">
* <td>
* <pre>
* ^
* |
* </pre>
* </td>
* <td>
* <pre>
* ^
* |
* </pre>
* </td>
* <td>
* <pre>
* </pre>
* </td>
* </tr>
* <tr valign="top">
* <td>
* <pre>
* @Type(value = 1)
* TestClass { -------->
* @Method(value = 1)
* @Method(value = 1)
* @Mix(value = 1)
* originMethod1()
* @Method(value = 1)
* @Mix(value = 1)
* superMethod2()
* @Method(value = 1)
* @Mix(value = 1)
* testMethod1()
* testMethod2()
* }
* </pre>
* </td>
* <td>
* <pre>
* @Mix(value = 3)
* TestInterface2 { -------->
* @Method(value = 3)
* superMethod1()
* @Method(value = 3)
* testMethod2()
* }
* </pre>
* </td>
* <td>
* <pre>
* @Type(value = 4)
* TestInterface1 {
* @Method(value = 4)
* @Mix(value = 4)
* testMethod1()
* }
* </pre>
* </td>
* </tr>
* </table>
* </p>
*
* @author Shuyang Zhou
*/
public class AnnotationLocatorTest {
@ClassRule
public static final CodeCoverageAssertor codeCoverageAssertor =
CodeCoverageAssertor.INSTANCE;
@Test
public void testClassListLocate() {
_classListLocate(TestClass.class, Arrays.asList(_type(1), _mix(2)));
_classListLocate(SuperClass.class, Arrays.asList(_mix(2), _type(5)));
_classListLocate(
TestInterface2.class, Arrays.asList(_mix(3), _type(4)));
_classListLocate(TestInterface1.class, Arrays.asList(_type(4)));
_classListLocate(OriginClass.class, Arrays.asList(_type(5), _mix(8)));
_classListLocate(
SuperInterface2.class, Arrays.asList(_type(6), _mix(7)));
_classListLocate(
SuperInterface1.class, Arrays.asList(_mix(7), _type(9)));
_classListLocate(
OriginInterface2.class, Arrays.asList(_mix(8), _type(9)));
_classListLocate(OriginInterface1.class, Arrays.asList(_type(9)));
}
@Test
public void testClassSingleLocate() {
_classSingleLocate(TestClass.class, 2, 1);
_classSingleLocate(SuperClass.class, 2, 5);
_classSingleLocate(TestInterface2.class, 3, 4);
_classSingleLocate(TestInterface1.class, -1, 4);
_classSingleLocate(OriginClass.class, 8, 5);
_classSingleLocate(SuperInterface2.class, 7, 6);
_classSingleLocate(SuperInterface1.class, 7, 9);
_classSingleLocate(OriginInterface2.class, 8, 9);
_classSingleLocate(OriginInterface1.class, -1, 9);
}
@Test
public void testConstructor() {
new AnnotationLocator();
}
@Test
public void testInheritedHierarchyWalking() throws Exception {
List<Class<?>> expectedClassHierarchy = Arrays.asList(
TestClass.class, SuperClass.class, TestInterface2.class,
TestInterface1.class, OriginClass.class, SuperInterface2.class,
SuperInterface1.class, OriginInterface2.class,
OriginInterface1.class);
List<Class<?>> actualClassHierarchy = new ArrayList<>();
Queue<Class<?>> queue = new LinkedList<>();
queue.offer(TestClass.class);
Class<?> clazz = null;
while ((clazz = queue.poll()) != null) {
actualClassHierarchy.add(clazz);
_QUEUE_SUPER_TYPES_METHOD.invoke(null, queue, clazz);
}
Assert.assertEquals(expectedClassHierarchy, actualClassHierarchy);
}
@Test
public void testMethodListLocate() {
_methodListLocate(
TestClass.class,
Arrays.asList(
new Annotation[] {_method(1), _mix(1), _type(1)},
new Annotation[] {_type(1), _method(2), _mix(2)},
new Annotation[] {_type(1), _method(2), _mix(2)},
new Annotation[] {_method(1), _mix(1), _type(1)},
new Annotation[] {_method(1), _mix(1), _type(1)},
new Annotation[] {_type(1), _method(3), _mix(3)}));
_methodListLocate(
SuperClass.class,
Arrays.asList(
new Annotation[] {_mix(2), _method(5), _type(5)},
new Annotation[] {_method(2), _mix(2), _type(5)},
new Annotation[] {_method(2), _mix(2), _type(6)},
new Annotation[] {_mix(2), _method(6), _type(6)},
new Annotation[0], new Annotation[0]));
_methodListLocate(
TestInterface2.class,
Arrays.asList(
new Annotation[] {_mix(3), _method(6), _type(6)},
new Annotation[0],
new Annotation[] {_method(3), _mix(3), _type(6)},
new Annotation[] {_mix(3), _method(6), _type(6)},
new Annotation[] {_mix(3), _method(4), _type(4)},
new Annotation[] {_method(3), _mix(3)}));
_methodListLocate(
TestInterface1.class,
Arrays.asList(
new Annotation[0], new Annotation[0], new Annotation[0],
new Annotation[0],
new Annotation[] {_method(4), _mix(4), _type(4)},
new Annotation[0]));
_methodListLocate(
OriginClass.class,
Arrays.asList(
new Annotation[] {_method(5), _mix(5), _type(5)},
new Annotation[] {_type(5), _method(8), _mix(8)},
new Annotation[0], new Annotation[0], new Annotation[0],
new Annotation[0]));
_methodListLocate(
SuperInterface2.class,
Arrays.asList(
new Annotation[] {_method(6), _mix(6), _type(6)},
new Annotation[0],
new Annotation[] {_type(6), _method(7), _mix(7)},
new Annotation[] {_method(6), _mix(6), _type(6)},
new Annotation[0], new Annotation[0]));
_methodListLocate(
SuperInterface1.class,
Arrays.asList(
new Annotation[] {_mix(7), _method(9), _type(9)},
new Annotation[0], new Annotation[] {_method(7), _mix(7)},
new Annotation[0], new Annotation[0], new Annotation[0]));
_methodListLocate(
OriginInterface2.class,
Arrays.asList(
new Annotation[] {_mix(8), _method(9), _type(9)},
new Annotation[] {_method(8), _mix(8)}, new Annotation[0],
new Annotation[0], new Annotation[0], new Annotation[0]));
_methodListLocate(
OriginInterface1.class,
Arrays.asList(
new Annotation[] {_method(9), _mix(9), _type(9)},
new Annotation[0], new Annotation[0], new Annotation[0],
new Annotation[0], new Annotation[0]));
}
@Test
public void testMethodSingleLocate() {
_methodSingleLocate(
TestClass.class, new int[] {1, 2, 2, 1, 1, 3},
new int[] {1, 2, 2, 1, 1, 3}, new int[] {1, 1, 1, 1, 1, 1});
_methodSingleLocate(
SuperClass.class, new int[] {5, 2, 2, 6, -1, -1},
new int[] {2, 2, 2, 2, -1, -1}, new int[] {5, 5, 6, 6, -1, -1});
_methodSingleLocate(
TestInterface2.class, new int[] {6, -1, 3, 6, 4, 3},
new int[] {3, -1, 3, 3, 3, 3}, new int[] {6, -1, 6, 6, 4, -1});
_methodSingleLocate(
TestInterface1.class, new int[] {-1, -1, -1, -1, 4, -1},
new int[] {-1, -1, -1, -1, 4, -1},
new int[] {-1, -1, -1, -1, 4, -1});
_methodSingleLocate(
OriginClass.class, new int[] {5, 8, -1, -1, -1, -1},
new int[] {5, 8, -1, -1, -1, -1}, new int[] {5, 5, -1, -1, -1, -1});
_methodSingleLocate(
SuperInterface2.class, new int[] {6, -1, 7, 6, -1, -1},
new int[] {6, -1, 7, 6, -1, -1}, new int[] {6, -1, 6, 6, -1, -1});
_methodSingleLocate(
SuperInterface1.class, new int[] {9, -1, 7, -1, -1, -1},
new int[] {7, -1, 7, -1, -1, -1},
new int[] {9, -1, -1, -1, -1, -1});
_methodSingleLocate(
OriginInterface2.class, new int[] {9, 8, -1, -1, -1, -1},
new int[] {8, 8, -1, -1, -1, -1},
new int[] {9, -1, -1, -1, -1, -1});
_methodSingleLocate(
OriginInterface1.class, new int[] {9, -1, -1, -1, -1, -1},
new int[] {9, -1, -1, -1, -1, -1},
new int[] {9, -1, -1, -1, -1, -1});
}
private void _classListLocate(
Class<?> clazz, List<? extends Annotation> expectedAnnotations) {
List<Annotation> actualAnnotations = AnnotationLocator.locate(clazz);
Assert.assertEquals(
clazz.getName(), expectedAnnotations.size(),
actualAnnotations.size());
for (int i = 0; i < expectedAnnotations.size(); i++) {
Annotation expectedAnnotation = expectedAnnotations.get(i);
Annotation actualAnnotation = actualAnnotations.get(i);
Assert.assertEquals(
clazz.getName(), expectedAnnotation.annotationType(),
actualAnnotation.annotationType());
if (expectedAnnotation.annotationType() == Mix.class) {
Mix expectedMix = (Mix)expectedAnnotation;
Mix actualMix = (Mix)actualAnnotation;
Assert.assertEquals(
"@Mix : " + clazz.getName(), expectedMix.value(),
actualMix.value());
}
else {
Type expectedType = (Type)expectedAnnotation;
Type actualType = (Type)actualAnnotation;
Assert.assertEquals(
"@Type : ", expectedType.value(), actualType.value());
}
}
}
private void _classSingleLocate(
Class<?> clazz, int expectedMixValue, int expectedTypeValue) {
Mix actualMix = AnnotationLocator.locate(clazz, Mix.class);
if (expectedMixValue == -1) {
Assert.assertNull("@Mix : " + clazz.getName(), actualMix);
}
else {
Assert.assertEquals(
"@Mix : " + clazz.getName(), expectedMixValue,
actualMix.value());
}
Type actualType = AnnotationLocator.locate(clazz, Type.class);
if (expectedTypeValue == -1) {
Assert.assertNull("@Type : " + clazz.getName(), actualType);
}
else {
Assert.assertEquals(
"@Type : " + clazz.getName(), expectedTypeValue,
actualType.value());
}
}
private Method _method(final int value) {
return new Method() {
@Override
public Class<? extends Annotation> annotationType() {
return Method.class;
}
@Override
public int value() {
return value;
}
};
}
private void _methodListLocate(
Class<?> clazz, List<Annotation[]> expectedAnnotationsList) {
for (int i = 0; i < _interfaceMethods.length; i++) {
Annotation[] expectedAnnotations = expectedAnnotationsList.get(i);
java.lang.reflect.Method method = _interfaceMethods[i];
List<Annotation> actualAnnotations = AnnotationLocator.locate(
method, clazz);
Assert.assertEquals(
method.getName() + "()@" + clazz.getName(),
expectedAnnotations.length, actualAnnotations.size());
for (int j = 0; j < expectedAnnotations.length; j++) {
Annotation expectedAnnotation = expectedAnnotations[j];
Annotation actualAnnotation = actualAnnotations.get(j);
Assert.assertEquals(
method.getName() + "()@" + clazz.getName(),
expectedAnnotation.annotationType(),
actualAnnotation.annotationType());
if (expectedAnnotation.annotationType() == Mix.class) {
Mix expectedMix = (Mix)expectedAnnotation;
Mix actualMix = (Mix)actualAnnotation;
Assert.assertEquals(
"@Mix : " + method.getName() + "()@" + clazz.getName(),
expectedMix.value(), actualMix.value());
}
else if (expectedAnnotation.annotationType() == Method.class) {
Method expectedType = (Method)expectedAnnotation;
Method actualMethod = (Method)actualAnnotation;
Assert.assertEquals(
"@Method : " + method.getName() + "()@" +
clazz.getName(),
expectedType.value(), actualMethod.value());
}
else {
Type expectedType = (Type)expectedAnnotation;
Type actualType = (Type)actualAnnotation;
Assert.assertEquals(
"@Type : " + method.getName() + "()@" + clazz.getName(),
expectedType.value(), actualType.value());
}
}
try {
method = clazz.getDeclaredMethod(
method.getName(), method.getParameterTypes());
actualAnnotations = AnnotationLocator.locate(method, null);
Assert.assertEquals(
method.getName() + "()@" + clazz.getName(),
expectedAnnotations.length, actualAnnotations.size());
for (int j = 0; j < expectedAnnotations.length; j++) {
Annotation expectedAnnotation = expectedAnnotations[j];
Annotation actualAnnotation = actualAnnotations.get(j);
Assert.assertEquals(
method.getName() + "()@" + clazz.getName(),
expectedAnnotation.annotationType(),
actualAnnotation.annotationType());
if (expectedAnnotation.annotationType() == Mix.class) {
Mix expectedMix = (Mix)expectedAnnotation;
Mix actualMix = (Mix)actualAnnotation;
Assert.assertEquals(
"@Mix : " + method.getName() + "()@" +
clazz.getName(),
expectedMix.value(), actualMix.value());
}
else if (expectedAnnotation.annotationType() ==
Method.class) {
Method expectedType = (Method)expectedAnnotation;
Method actualMethod = (Method)actualAnnotation;
Assert.assertEquals(
"@Method : " + method.getName() + "()@" +
clazz.getName(),
expectedType.value(), actualMethod.value());
}
else {
Type expectedType = (Type)expectedAnnotation;
Type actualType = (Type)actualAnnotation;
Assert.assertEquals(
"@Type : " + method.getName() + "()@" +
clazz.getName(),
expectedType.value(), actualType.value());
}
}
}
catch (Exception e) {
}
}
}
private void _methodSingleLocate(
Class<?> clazz, int[] expectedMethodValues, int[] expectedMixValues,
int[] expectedTypeValues) {
for (int i = 0; i < 6; i++) {
java.lang.reflect.Method method = _interfaceMethods[i];
int expectedMethodValue = expectedMethodValues[i];
Method methodAnnotation = AnnotationLocator.locate(
method, clazz, Method.class);
if (methodAnnotation == null) {
Assert.assertEquals(
"@Method : " + clazz.getName(), -1, expectedMethodValue);
continue;
}
Assert.assertEquals(
"@Method : " + method.getName() + "()@" + clazz.getName(),
expectedMethodValue, methodAnnotation.value());
try {
method = clazz.getDeclaredMethod(
method.getName(), method.getParameterTypes());
methodAnnotation = AnnotationLocator.locate(
method, null, Method.class);
Assert.assertEquals(
method.getName() + "()@" + clazz.getName(),
expectedMethodValue, methodAnnotation.value());
}
catch (Exception e) {
}
}
for (int i = 0; i < 6; i++) {
java.lang.reflect.Method method = _interfaceMethods[i];
int expectedMixValue = expectedMixValues[i];
Mix mixAnnotation = AnnotationLocator.locate(
method, clazz, Mix.class);
if (mixAnnotation == null) {
Assert.assertEquals(
"@Mix : " + clazz.getName(), -1, expectedMixValue);
continue;
}
Assert.assertEquals(
"@Mix : " + method.getName() + "()@" + clazz.getName(),
expectedMixValue, mixAnnotation.value());
try {
method = clazz.getDeclaredMethod(
method.getName(), method.getParameterTypes());
mixAnnotation = AnnotationLocator.locate(
method, null, Mix.class);
Assert.assertEquals(
method.getName() + "()@" + clazz.getName(),
expectedMixValue, mixAnnotation.value());
}
catch (Exception e) {
}
}
for (int i = 0; i < 6; i++) {
java.lang.reflect.Method method = _interfaceMethods[i];
int expectedTypeValue = expectedTypeValues[i];
Type typeAnnotation = AnnotationLocator.locate(
method, clazz, Type.class);
if (typeAnnotation == null) {
Assert.assertEquals(
"@Type : " + clazz.getName(), -1, expectedTypeValue);
continue;
}
Assert.assertEquals(
"@Type : " + method.getName() + "()@" + clazz.getName(),
expectedTypeValue, typeAnnotation.value());
try {
method = clazz.getDeclaredMethod(
method.getName(), method.getParameterTypes());
typeAnnotation = AnnotationLocator.locate(
method, null, Type.class);
Assert.assertEquals(
method.getName() + "()@" + clazz.getName(),
expectedTypeValue, typeAnnotation.value());
}
catch (Exception e) {
}
}
}
private Mix _mix(final int value) {
return new Mix() {
@Override
public Class<? extends Annotation> annotationType() {
return Mix.class;
}
@Override
public int value() {
return value;
}
};
}
private Type _type(final int value) {
return new Type() {
@Override
public Class<? extends Annotation> annotationType() {
return Type.class;
}
@Override
public int value() {
return value;
}
};
}
private static final java.lang.reflect.Method _QUEUE_SUPER_TYPES_METHOD;
private static final java.lang.reflect.Method[] _interfaceMethods;
static {
try {
_interfaceMethods = new java.lang.reflect.Method[6];
_interfaceMethods[0] = OriginInterface1.class.getDeclaredMethod(
"originMethod1");
_interfaceMethods[1] = OriginInterface2.class.getDeclaredMethod(
"originMethod2");
_interfaceMethods[2] = SuperInterface1.class.getDeclaredMethod(
"superMethod1");
_interfaceMethods[3] = SuperInterface2.class.getDeclaredMethod(
"superMethod2");
_interfaceMethods[4] = TestInterface1.class.getDeclaredMethod(
"testMethod1");
_interfaceMethods[5] = TestInterface2.class.getDeclaredMethod(
"testMethod2");
java.lang.reflect.Method queueSuperTypesMethod =
AnnotationLocator.class.getDeclaredMethod(
"_queueSuperTypes", Queue.class, Class.class);
queueSuperTypesMethod.setAccessible(true);
_QUEUE_SUPER_TYPES_METHOD = queueSuperTypesMethod;
}
catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
@Type(value = 5)
private static class OriginClass
implements OriginInterface2, OriginInterface1 {
@Method(value = 5)
@Mix(value = 5)
@Override
public void originMethod1() {
}
@Override
public void originMethod2() {
}
}
@Mix(value = 2)
private static class SuperClass
extends OriginClass implements SuperInterface2, SuperInterface1 {
@Method(value = 2)
@Override
public void originMethod2() {
}
@Method(value = 2)
@Override
public void superMethod1() {
}
@Override
public void superMethod2() {
}
}
@Type(value = 1)
private static class TestClass
extends SuperClass implements TestInterface2, TestInterface1 {
@Method(value = 1)
@Mix(value = 1)
@Override
public void originMethod1() {
}
@Method(value = 1)
@Mix(value = 1)
@Override
public void superMethod2() {
}
@Method(value = 1)
@Mix(value = 1)
@Override
public void testMethod1() {
}
@Override
public void testMethod2() {
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
private @interface Method {
public int value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
private @interface Mix {
public int value();
}
@Type(value = 9)
private interface OriginInterface1 {
@Method(value = 9)
@Mix(value = 9)
public void originMethod1();
}
@Mix(value = 8)
private interface OriginInterface2 extends OriginInterface1 {
@Method(value = 8)
public void originMethod2();
}
@Mix(value = 7)
private interface SuperInterface1 extends OriginInterface1 {
@Method(value = 7)
void superMethod1();
}
@Type(value = 6)
private interface SuperInterface2 extends SuperInterface1 {
@Override
@Method(value = 6)
@Mix(value = 6)
void originMethod1();
@Method(value = 6)
@Mix(value = 6)
void superMethod2();
}
@Type(value = 4)
private interface TestInterface1 {
@Method(value = 4)
@Mix(value = 4)
public void testMethod1();
}
@Mix(value = 3)
private interface TestInterface2 extends TestInterface1, SuperInterface2 {
@Method(value = 3)
@Override
public void superMethod1();
@Method(value = 3)
public void testMethod2();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
private @interface Type {
public int value();
}
}