diff --git a/openbis-common/source/java/ch/systemsx/cisd/openbis/common/api/server/json/util/ClassReferences.java b/openbis-common/source/java/ch/systemsx/cisd/openbis/common/api/server/json/util/ClassReferences.java index a6cbb073b1656149e4f2e25bd1b420778bb7081d..d68c44af19b6e0faa4d66afe71c7867e5ffdecba 100644 --- a/openbis-common/source/java/ch/systemsx/cisd/openbis/common/api/server/json/util/ClassReferences.java +++ b/openbis-common/source/java/ch/systemsx/cisd/openbis/common/api/server/json/util/ClassReferences.java @@ -28,42 +28,51 @@ import java.util.Set; import org.reflections.Reflections; +import com.google.common.base.Predicate; + /** - * A utility class that searches for classes and interfaces that are potentially converted in - * JSON form. + * A utility class that searches for classes and interfaces that are potentially converted in JSON + * form. * * @author anttil */ public class ClassReferences { - private static Reflections ref = new Reflections(""); + public static Reflections ref = new Reflections("ch"); /** * Returns all the classes and interfaces that are referenced by the public methods declared by - * the given class. - * - * A class considered to be referenced by a method if either of the following holds - * 1) The class or its superclass or an interface it implements is mentioned in the signature of - * the method as an argument or as a return type either directly or as a type of an array - * or a collection. - * 2) The class or its superclass or an interface it implements is used as a return type of a - * getter method in a class referenced (note recursion here) by a method. - * - * However, the following kind of classes never considered to be referenced: - * 1) Anonymous classes - * 2) Classes that are not defined within package "ch." + * the given class. A class considered to be referenced by a method if either of the following + * holds 1) The class or its superclass or an interface it implements is mentioned in the + * signature of the method as an argument or as a return type either directly or as a type of an + * array or a collection. 2) The class or its superclass or an interface it implements is used + * as a return type of a getter method in a class referenced (note recursion here) by a method. + * However, the following kind of classes never considered to be referenced: 1) Anonymous + * classes 2) Classes that are not defined within package "ch." * * @param clazz class whose references should be searched. * @returns referenced classes */ - public static Collection<Class<?>> search(Class<?> clazz) + public static Collection<Class<?>> search(Class<?> clazz, Predicate<Class<?>> filter) { Set<Class<?>> results = new HashSet<Class<?>>(); for (Method method : clazz.getDeclaredMethods()) { searchIfMethodIsPublic(method, results); } + if (filter != null) + { + Set<Class<?>> filteredResults = new HashSet<Class<?>>(); + for (Class<?> result : results) + { + if (filter.apply(result)) + { + filteredResults.add(result); + } + } + results = filteredResults; + } return results; } diff --git a/openbis-common/sourceTest/java/ch/systemsx/cisd/openbis/common/api/server/json/ClassReferenceSearchTest.java b/openbis-common/sourceTest/java/ch/systemsx/cisd/openbis/common/api/server/json/ClassReferenceSearchTest.java index 455567ba8dca9d83b9e473512d1b8d3c30662d43..06319cd425d6f3047284848509db64e45c6b853f 100644 --- a/openbis-common/sourceTest/java/ch/systemsx/cisd/openbis/common/api/server/json/ClassReferenceSearchTest.java +++ b/openbis-common/sourceTest/java/ch/systemsx/cisd/openbis/common/api/server/json/ClassReferenceSearchTest.java @@ -25,102 +25,126 @@ import java.util.List; import org.testng.annotations.Test; +import com.google.common.base.Predicate; + import ch.systemsx.cisd.openbis.common.api.server.json.util.ClassReferences; public class ClassReferenceSearchTest { - public class BaseClass + private static Predicate<Class<?>> noFilter = new Predicate<Class<?>>() + { + @Override + public boolean apply(Class<?> arg0) + { + return true; + } + }; + + public class BaseClass { } - public interface BaseInterface + + public interface BaseInterface { } - public class SimpleClass extends BaseClass + + public class SimpleClass extends BaseClass { } - public class AnotherSimpleClass implements BaseInterface + + public class AnotherSimpleClass implements BaseInterface { } - public class NotExtendedSimpleClass + + public class NotExtendedSimpleClass { } - - public interface SimpleInterface + public interface SimpleInterface { public AnotherSimpleClass getSimple(SimpleClass argument); } + @Test - public void findsDirectSimpleReferences() + public void findsDirectSimpleReferences() { - Class<?>[] expected = {SimpleClass.class, AnotherSimpleClass.class}; - Collection<Class<?>> result = ClassReferences.search(SimpleInterface.class); + Class<?>[] expected = + { SimpleClass.class, AnotherSimpleClass.class }; + Collection<Class<?>> result = ClassReferences.search(SimpleInterface.class, noFilter); assertThat(result, hasItems(expected)); assertThat(result.size(), is(expected.length)); } - - - public interface CollectionInterface + + public interface CollectionInterface { Collection<SimpleClass> getCollection(List<AnotherSimpleClass> argument); } + @Test - public void findsReferencesInCollections() + public void findsReferencesInCollections() { - Class<?>[] expected = {SimpleClass.class, AnotherSimpleClass.class}; - Collection<Class<?>> result = ClassReferences.search(CollectionInterface.class); + Class<?>[] expected = + { SimpleClass.class, AnotherSimpleClass.class }; + Collection<Class<?>> result = ClassReferences.search(CollectionInterface.class, noFilter); assertThat(result, hasItems(expected)); assertThat(result.size(), is(expected.length)); } - - public interface ArrayInterface + public interface ArrayInterface { SimpleClass[] getArray(AnotherSimpleClass[] argument); } + @Test - public void findsReferencesInArrays() + public void findsReferencesInArrays() { - Class<?>[] expected = {SimpleClass.class, AnotherSimpleClass.class}; - Collection<Class<?>> result = ClassReferences.search(ArrayInterface.class); + Class<?>[] expected = + { SimpleClass.class, AnotherSimpleClass.class }; + Collection<Class<?>> result = ClassReferences.search(ArrayInterface.class, noFilter); assertThat(result, hasItems(expected)); assertThat(result.size(), is(expected.length)); } - - public interface SubTypeInterface + + public interface SubTypeInterface { - public BaseClass getBaseclass(BaseInterface argument); + public BaseClass getBaseclass(BaseInterface argument); } + @Test - public void findsSubTypesOfReferencedClassesAndInterfaces() - { - Class<?>[] expected = {SimpleClass.class, - AnotherSimpleClass.class, - BaseClass.class, - BaseInterface.class}; - Collection<Class<?>> result = ClassReferences.search(SubTypeInterface.class); + public void findsSubTypesOfReferencedClassesAndInterfaces() + { + Class<?>[] expected = + { SimpleClass.class, + AnotherSimpleClass.class, + BaseClass.class, + BaseInterface.class }; + Collection<Class<?>> result = ClassReferences.search(SubTypeInterface.class, noFilter); assertThat(result, hasItems(expected)); assertThat(result.size(), is(expected.length)); } - - - public interface GetterInterface + + public interface GetterInterface { public void method(InterfaceWithOneGetter argument); } - public interface InterfaceWithOneGetter + + public interface InterfaceWithOneGetter { public BaseClass doSomething(); + public BaseInterface getCalculatedValue(BaseInterface argument); + public NotExtendedSimpleClass getSimpleClass(); } + @Test - public void searchesOnlyGettersOfReferencedClasses() + public void searchesOnlyGettersOfReferencedClasses() { - Class<?>[] expected = {InterfaceWithOneGetter.class, - NotExtendedSimpleClass.class}; - Collection<Class<?>> result = ClassReferences.search(GetterInterface.class); + Class<?>[] expected = + { InterfaceWithOneGetter.class, + NotExtendedSimpleClass.class }; + Collection<Class<?>> result = ClassReferences.search(GetterInterface.class, noFilter); assertThat(result, hasItems(expected)); assertThat(result.size(), is(expected.length)); } diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/screening/systemtests/JsonAnnotationTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/screening/systemtests/JsonAnnotationTest.java index 2db9fa9cbf011e546ee9d71df873b21c18184e8f..3b940da0cc2ec46d3ba915ff2d20d1abd9510325 100644 --- a/screening/sourceTest/java/ch/systemsx/cisd/openbis/screening/systemtests/JsonAnnotationTest.java +++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/screening/systemtests/JsonAnnotationTest.java @@ -25,12 +25,10 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; -import org.reflections.Reflections; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.google.common.base.Predicate; import ch.systemsx.cisd.base.annotation.JsonObject; import ch.systemsx.cisd.openbis.common.api.server.json.JsonUniqueCheckIgnore; @@ -53,26 +51,33 @@ public class JsonAnnotationTest { private Collection<Class<?>> allJsonClasses = new HashSet<Class<?>>(); - private Reflections reflections = new Reflections(""); - private Collection<Class<?>> empty = Collections.emptySet(); private Map<String, Collection<Class<?>>> emptyMap = new HashMap<String, Collection<Class<?>>>(); // Used by TestNG + @SuppressWarnings("unused") @BeforeClass private void findAllClassesUsedByJsonRpcApi() { Class<?>[] jsonRpcInterfaces = - { IDssServiceRpcGeneric.class, IScreeningApiServer.class, - IGeneralInformationChangingService.class, - IGeneralInformationService.class, IWebInformationService.class, - IQueryApiServer.class, IDssServiceRpcScreening.class }; + { IDssServiceRpcGeneric.class, IScreeningApiServer.class, + IGeneralInformationChangingService.class, + IGeneralInformationService.class, IWebInformationService.class, + IQueryApiServer.class, IDssServiceRpcScreening.class }; for (Class<?> jsonClass : jsonRpcInterfaces) { - allJsonClasses.addAll(ClassReferences.search(jsonClass)); + allJsonClasses.addAll(ClassReferences.search(jsonClass, new Predicate<Class<?>>() + { + @Override + public boolean apply(Class<?> clazz) + { + return (clazz.getPackage().getName().startsWith( + "ch.systemsx.sybit.imageviewer") == false); + } + })); } } @@ -87,7 +92,7 @@ public class JsonAnnotationTest public void jsonTypeNamesAreUnique() { Map<String, Collection<Class<?>>> names = new HashMap<String, Collection<Class<?>>>(); - for (Class<?> clazz : reflections.getTypesAnnotatedWith(JsonObject.class)) + for (Class<?> clazz : ClassReferences.ref.getTypesAnnotatedWith(JsonObject.class)) { if (clazz.getAnnotation(JsonUniqueCheckIgnore.class) != null) { @@ -101,50 +106,11 @@ public class JsonAnnotationTest assertThat(duplicatedValuesIn(names), is(emptyMap)); } - @Test(enabled = false) - public void jsonClassesWithSubClassesAreAnnotatedWithJsonSubTypes() - { - Collection<Class<?>> classesWithoutAnnotation = new HashSet<Class<?>>(); - for (Class<?> clazz : allJsonClasses) - { - if (clazz.isEnum() == false && reflections.getSubTypesOf(clazz).isEmpty() == false) - { - if (clazz.getAnnotation(JsonSubTypes.class) == null) - { - classesWithoutAnnotation.add(clazz); - } - } - } - assertThat(classesWithoutAnnotation, is(empty)); - } - - @Test(enabled = false) - public void jsonSubTypesAnnotationsContainAllDirectSubClasses() - { - Map<String, Collection<Class<?>>> missingSubtypeAnnotations = - new PrettyPrintingCollectionMap<String, Collection<Class<?>>>(); - for (Class<?> main : reflections.getTypesAnnotatedWith(JsonSubTypes.class)) - { - Collection<Class<?>> annotatedSubtypes = getAnnotatedSubTypes(main); - - for (Class<?> subtype : reflections.getSubTypesOf(main)) - { - if (subtypeIsMissing(main, subtype, annotatedSubtypes)) - { - addValueToCollectionMap(missingSubtypeAnnotations, main.getCanonicalName(), - subtype); - } - } - } - - assertThat(missingSubtypeAnnotations, is(emptyMap)); - } - private static class PrettyPrintingCollectionMap<K, V extends Collection<?>> extends HashMap<K, V> { - private static final long serialVersionUID = 2615134692782526120L; + private static final long serialVersionUID = 1L; @Override public String toString() @@ -163,40 +129,6 @@ public class JsonAnnotationTest } } - private static boolean subtypeIsMissing(Class<?> main, Class<?> subtype, - Collection<Class<?>> classes) - { - - if (subtype.isAnonymousClass()) - { - return false; - } - - if (classes.contains(subtype)) - { - return false; - } - - if (subtype.isInterface()) - { - return true; - } else - { - return subtype.getSuperclass().equals(main); - } - } - - private static Collection<Class<?>> getAnnotatedSubTypes(Class<?> clazz) - { - Collection<Class<?>> annotated = new HashSet<Class<?>>(); - JsonSubTypes types = clazz.getAnnotation(JsonSubTypes.class); - for (Type type : types.value()) - { - annotated.add(type.value()); - } - return annotated; - } - private Collection<Class<?>> getAllJsonRpcClassesWithoutJsonObject() { Collection<Class<?>> classesWithoutJsonObject = new HashSet<Class<?>>();