/* * JBoss, Home of Professional Open Source * Copyright 2010-2016, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This 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 software 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. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.richfaces.tests.metamer.ftest.extension.attributes.coverage.collect; import static java.text.MessageFormat.format; import java.lang.reflect.Method; import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.richfaces.tests.metamer.ftest.attributes.AttributeEnum; import org.richfaces.tests.metamer.ftest.extension.attributes.coverage.annotations.CoversAttributes; import org.richfaces.tests.metamer.ftest.extension.attributes.coverage.annotations.CoversAttributes.DetectFromPackage; import org.richfaces.tests.metamer.ftest.extension.attributes.coverage.annotations.MultipleCoversAttributes; import org.richfaces.tests.metamer.ftest.extension.finder.ClassFinder; import org.richfaces.tests.metamer.ftest.extension.finder.MethodFinder; import org.richfaces.tests.metamer.ftest.extension.finder.SimpleClassFinder; import org.richfaces.tests.metamer.ftest.extension.finder.SimpleMethodFinder; import org.testng.annotations.Test; import com.google.common.collect.Maps; public class SimpleAttributesCoverageCollector implements AttributesCoverageCollector { public static final String A4J = "a4j"; public static final String A4J_COMPONENT_TEST_PACKAGE = "org.richfaces.tests.metamer.ftest.a4j"; public static final String ATTRIBUTES = "Attributes"; public static final String EMPTY_STRING = ""; public static final String RICH = "rich"; public static final String RICH_COMPONENT_TEST_PACKAGE = "org.richfaces.tests.metamer.ftest.rich"; public static final String SRC_TEST_JAVA = "src/test/java"; private final Set<Method> checkedMethods = new HashSet<Method>(1000); private final ClassFinder classesFinder = new SimpleClassFinder(); private final Map<Class<? extends Enum>, EnumSet> map = new HashMap<Class<? extends Enum>, EnumSet>(100); private final MethodFinder methodsFinder = new SimpleMethodFinder(); private final Map<Package, Class<? extends Enum>> pkgClassMap = Maps.newHashMap(); private static String getEnumFQNFromPackageName(String packageName) { return packageName + packageName.substring(packageName.lastIndexOf('.')) .replaceAll(A4J, EMPTY_STRING) .replaceAll(RICH, EMPTY_STRING) + ATTRIBUTES; } @Override public Map<Class<? extends Enum>, EnumSet> collect(String pkg) { collectAll( methodsFinder.findAllMethodsFrom(classesFinder.findAllClassesFrom(SRC_TEST_JAVA).fromPackageStartingWith(pkg).scan() ).withAnnotation(Test.class).scan() ); return map; } private void collect(Method original) { collect(original, original); } private void collect(Method original, Method parentMethod) { if (!checkedMethods.contains(original)) {// already resolved same method? CoversAttributes coversAnnotation = parentMethod.getAnnotation(CoversAttributes.class); MultipleCoversAttributes coversMultipleAnnotations = parentMethod.getAnnotation(MultipleCoversAttributes.class); if (coversAnnotation != null) { checkedMethods.add(original); handleCoversAttribute(coversAnnotation, original); } else if (coversMultipleAnnotations != null) { checkedMethods.add(original); handleMultipleCoversAttribute(coversMultipleAnnotations, original); } else { // try to scan for annotations in parent classes for methods with same name as this @Test method for (Method methodInParent : parentMethod.getDeclaringClass().getSuperclass().getDeclaredMethods()) { if (methodInParent.getName().equals(original.getName())) { collect(original, methodInParent); return; } } } } } private void collectAll(Collection<Method> methods) { for (Method m : methods) { collect(m); } } @SuppressWarnings("unchecked") private Class<? extends Enum> getEnumClassFromAnnotationOrMethodPackage(CoversAttributes annotation, Method m) { // check if the annotation contains attributes enum class if (annotation != null) { Class<? extends AttributeEnum> attributeClass = annotation.attributeEnumClass(); if (!attributeClass.equals(DetectFromPackage.class)) { return (Class<? extends Enum>) attributeClass; } } Package aPackage = m.getDeclaringClass().getPackage(); String packageName = aPackage.getName(); // already in cache? if (pkgClassMap.containsKey(aPackage)) { return pkgClassMap.get(aPackage); } String expectedFQN = getEnumFQNFromPackageName(packageName); Class<? extends Enum> result; // scan only in a4j.* or rich.* subpackages, do not scan for enum in abstract test superclass of multiple components, e.g. AbstractValidationMessagesTest if (expectedFQN.startsWith(A4J_COMPONENT_TEST_PACKAGE) || expectedFQN.startsWith(RICH_COMPONENT_TEST_PACKAGE)) { try { result = (Class<? extends Enum>) Class.forName(expectedFQN); } catch (ClassNotFoundException ex) { throw new RuntimeException("Class " + expectedFQN + " was not found", ex); } } else { result = null; } pkgClassMap.put(aPackage, result); return result; } /** * Adds covered attribute(s) to the inner map */ private void handleCoversAttribute(CoversAttributes annotation, Method original) { Class<? extends Enum> attributeClass = getEnumClassFromAnnotationOrMethodPackage(annotation, original); if (attributeClass != null) { EnumSet set; if (map.containsKey(attributeClass)) { set = map.get(attributeClass); } else { set = EnumSet.noneOf(attributeClass); } for (String attributeName : annotation.value()) { try { set.add(Enum.valueOf(attributeClass, attributeName)); } catch (IllegalArgumentException ex) { System.err.println( format("No such enum constant <{0}> in class <{1}>. Detected in method <{2}>", attributeName, attributeClass.getName(), original)); } } map.put(attributeClass, set); } } private void handleMultipleCoversAttribute(MultipleCoversAttributes annotation, Method original) { for (CoversAttributes value : annotation.value()) { handleCoversAttribute(value, original); } } }