package ru.yandex.qatools.allure.data.plugins; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Base class for all plugins with resources. * * @author Dmitry Baev charlie@yandex-team.ru * Date: 17.04.15 * @see ProcessPlugin */ public abstract class AbstractPlugin implements WithResources, WithData, WithPriority { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractPlugin.class); private String name = getClass().isAnnotationPresent(Plugin.Name.class) ? getClass().getAnnotation(Plugin.Name.class).value() : null; private int priority = getClass().isAnnotationPresent(Plugin.Priority.class) ? getClass().getAnnotation(Plugin.Priority.class).value() : 0; /** * Get name of plugin. Name should be specified in {@link Plugin.Name} * annotation. */ @Override public String getName() { return name; } /** * Get plugin priority. Plugins with higher priority will be processed firstly. */ @Override public int getPriority() { return priority; } /** * This method creates a {@link PluginData} for each field with * {@link Plugin.Data} annotation. * * @see #getFileName(Field) * @see #getFieldValue(Field) */ @Override public List<PluginData> getPluginData() { List<PluginData> results = new ArrayList<>(); for (Field field : getClass().getDeclaredFields()) { if (field.isAnnotationPresent(Plugin.Data.class)) { String fileName = getFileName(field); results.add(new PluginData(fileName, getFieldValue(field))); } } return results; } /** * Try to get field value. Field with {@link Plugin.Data} annotation should * be accessible. */ private Object getFieldValue(Field field) { try { field.setAccessible(true); return field.get(this); } catch (IllegalArgumentException | IllegalAccessException e) { LOGGER.error("Can't access to field value", e); return null; } } /** * Get file name for field with {@link Plugin.Data} annotation. If name * is default then use {@link #name} as file name and <code>json</code> * as file extension. * * @see #getName() */ private String getFileName(Field field) { String fileName = field.getAnnotation(Plugin.Data.class).value(); if ("##default".equals(fileName)) { fileName = getName() + ".json"; } return fileName; } /** * Verify plugin class. Plugin with resources plugin should not be private, abstract or interface. * Also class should be annotated with {@link Plugin.Name} * annotation. Returns true if given class is valid plugin, false otherwise. * Plugin class should not be null. */ public static boolean isValid(Class<? extends AbstractPlugin> pluginClass) { return pluginClass != null && pluginClass.isAnnotationPresent(Plugin.Name.class) && checkModifiers(pluginClass) && checkFieldsWithDataAnnotation(pluginClass); } /** * Check given class modifiers. Plugin with resources plugin should not be private or abstract * or interface. */ private static boolean checkModifiers(Class<? extends AbstractPlugin> pluginClass) { int modifiers = pluginClass.getModifiers(); return !Modifier.isAbstract(modifiers) && !Modifier.isInterface(modifiers) && !Modifier.isPrivate(modifiers); } /** * Check fields with {@link Plugin.Data} annotation. Firstly filter all declared fields * and then check it using {@link #shouldHasUniqueValues(List)} */ private static boolean checkFieldsWithDataAnnotation(Class<? extends AbstractPlugin> pluginClass) { List<Field> dataFields = new ArrayList<>(); for (Field field : pluginClass.getDeclaredFields()) { if (field.isAnnotationPresent(Plugin.Data.class)) { dataFields.add(field); } } return shouldHasUniqueValues(dataFields); } /** * Check that each given field has unique value in {@link Plugin.Data} annotation. */ private static boolean shouldHasUniqueValues(List<Field> dataFields) { Set<String> dataValues = new HashSet<>(); for (Field field : dataFields) { String value = field.getAnnotation(Plugin.Data.class).value(); if (dataValues.contains(value)) { return false; } dataValues.add(value); } return true; } }