class PluginAnnotation<T extends Annotation> {
    private static AgentLogger LOGGER = AgentLogger.getLogger(PluginAnnotation.class);

    // the main plugin class
    Class<?> pluginClass;

    // target plugin object
    Object plugin;

    // the annotation to process
    T annotation;

    // annotation is on a field (and method property is empty)
    Field field;

    // annotation is on a method (and field property is empty)
    Method method;

    // plugin matcher
    final PluginMatcher pluginMatcher;

    // Method matcher
    final MethodMatcher methodMatcher;

    // plugin group (elresolver etc..)
    final String group;

    // Falback plugin - plugin is used if no other plugin in the group version matches
    final boolean fallback;

    public PluginAnnotation(Class<?> pluginClass, Object plugin, T annotation, Method method) {
        this.pluginClass = pluginClass;
        this.plugin = plugin;
        this.annotation = annotation;
        this.method = method;

        Plugin pluginAnnotation = pluginClass.getAnnotation(Plugin.class);
        this.group = (pluginAnnotation.group() != null && !pluginAnnotation.group().isEmpty()) ? pluginAnnotation.group() : null;
        this.fallback = pluginAnnotation.fallback();

        if(method != null && (Modifier.isStatic(method.getModifiers()))) {
            this.pluginMatcher = new PluginMatcher(pluginClass);
            this.methodMatcher= new MethodMatcher(method);
        } else {
            this.pluginMatcher = null;
            this.methodMatcher = null;
        }
    }

    public PluginAnnotation(Class<?> pluginClass, Object plugin, T annotation, Field field) {

        this.pluginClass = pluginClass;
        this.plugin = plugin;
        this.annotation = annotation;
        this.field = field;
        this.pluginMatcher = null;
        this.methodMatcher = null;
        this.fallback  = false;
        this.group = null;
    }

    /**
     * Return plugin class (the plugin to which this annotation belongs - not necessarily declaring class.
     */
    public Class<?> getPluginClass() {
        return pluginClass;
    }

    public Object getPlugin() {
        return plugin;
    }

    public T getAnnotation() {
        return annotation;
    }

    public Method getMethod() {
        return method;
    }

    public Field getField() {
        return field;
    }

    public boolean shouldCheckVersion() {
        return //
        (this.plugin == null)//
                && //
                (//
                        (pluginMatcher != null && pluginMatcher.isApply()) //
                        || //
                        (methodMatcher != null && methodMatcher.isApply())//
                );//
    }

    /**
     * @return true, if plugin is fallback
     */
    public boolean isFallBack() {
       return fallback;
    }

    /**
     * @return the plugin group
     */
    public String getGroup() {
        return group;
    }

    /**
     * Matches.
     *
     * @param deploymentInfo the deployment info
     * @return true, if successful
     */
    public boolean matches(DeploymentInfo deploymentInfo){
        if(deploymentInfo == null || (pluginMatcher == null && methodMatcher == null)) {
            LOGGER.debug("No matchers, apply");
            return true;
        }

        if(pluginMatcher != null && pluginMatcher.isApply()) {
            if(VersionMatchResult.REJECTED.equals(pluginMatcher.matches(deploymentInfo))){
                LOGGER.debug("Plugin matcher rejected");
                return false;
            }
        }
        if(methodMatcher != null && methodMatcher.isApply()) {
            if(VersionMatchResult.REJECTED.equals(methodMatcher.matches(deploymentInfo))){
                LOGGER.debug("Method matcher rejected");
                return false;
            }
        }
        return true;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        PluginAnnotation<?> that = (PluginAnnotation<?>) o;

        if (!annotation.equals(that.annotation)) return false;
        if (field != null ? !field.equals(that.field) : that.field != null) return false;
        if (method != null ? !method.equals(that.method) : that.method != null) return false;
        if (!plugin.equals(that.plugin)) return false;

        return true;
    }

    @Override
    public int hashCode() {<FILL_FUNCTION_BODY>}

    @Override
    public String toString() {
        return "PluginAnnotation{" +
                "plugin=" + plugin +
                ", annotation=" + annotation +
                ", field=" + field +
                ", method=" + method +
                '}';
    }
}

        int result = plugin.hashCode();
        result = 31 * result + annotation.hashCode();
        result = 31 * result + (field != null ? field.hashCode() : 0);
        result = 31 * result + (method != null ? method.hashCode() : 0);
        return result;
    
<no_super_class>