content: 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;
      }
  }
  