class AnnotationInvocationHandler implements InvocationHandler {

        /** The annotation class. */
        private final Class<? extends Annotation> annotationClass;

        /** The {@link AnnotationInfo} object for this annotation. */
        private final AnnotationInfo annotationInfo;

        /** The annotation parameter values instantiated. */
        private final Map<String, Object> annotationParameterValuesInstantiated = new HashMap<>();

        /**
         * Constructor.
         *
         * @param annotationClass
         *            the annotation class
         * @param annotationInfo
         *            the annotation info
         */
        AnnotationInvocationHandler(final Class<? extends Annotation> annotationClass,
                final AnnotationInfo annotationInfo) {
            this.annotationClass = annotationClass;
            this.annotationInfo = annotationInfo;

            // Instantiate the annotation parameter values (this loads and gets references for class literals,
            // enum constants, etc.)
            for (final AnnotationParameterValue apv : annotationInfo.getParameterValues()) {
                final Object instantiatedValue = apv.instantiate(annotationInfo.getClassInfo());
                if (instantiatedValue == null) {
                    // Annotations cannot contain null values
                    throw new IllegalArgumentException("Got null value for annotation parameter " + apv.getName()
                            + " of annotation " + annotationInfo.name);
                }
                this.annotationParameterValuesInstantiated.put(apv.getName(), instantiatedValue);
            }
        }

        /* (non-Javadoc)
         * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method,
         * java.lang.Object[])
         */
        @Override
        public Object invoke(final Object proxy, final Method method, final Object[] args) {<FILL_FUNCTION_BODY>}
    }

            final String methodName = method.getName();
            final Class<?>[] paramTypes = method.getParameterTypes();
            if ((args == null ? 0 : args.length) != paramTypes.length) {
                throw new IllegalArgumentException(
                        "Wrong number of arguments for " + annotationClass.getName() + "." + methodName + ": got "
                                + (args == null ? 0 : args.length) + ", expected " + paramTypes.length);
            }
            if (args != null && paramTypes.length == 1) {
                if ("equals".equals(methodName) && paramTypes[0] == Object.class) {
                    // equals() needs to function the same as the JDK implementation 
                    // (see src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java in the JDK)
                    if (this == args[0]) {
                        return true;
                    } else if (!annotationClass.isInstance(args[0])) {
                        return false;
                    }
                    final ReflectionUtils reflectionUtils = annotationInfo.scanResult == null
                            ? new ReflectionUtils()
                            : annotationInfo.scanResult.reflectionUtils;
                    for (final Entry<String, Object> ent : annotationParameterValuesInstantiated.entrySet()) {
                        final String paramName = ent.getKey();
                        final Object paramVal = ent.getValue();
                        final Object otherParamVal = reflectionUtils.invokeMethod(/* throwException = */ false,
                                args[0], paramName);
                        if ((paramVal == null) != (otherParamVal == null)) {
                            // Annotation values should never be null, but just to be safe
                            return false;
                        } else if (paramVal == null && otherParamVal == null) {
                            return true;
                        } else if (paramVal == null || !paramVal.equals(otherParamVal)) {
                            return false;
                        }
                    }
                    return true;
                } else {
                    // .equals(Object) is the only method of an enum that can take one parameter
                    throw new IllegalArgumentException();
                }
            } else if (paramTypes.length == 0) {
                // Handle .toString(), .hashCode(), .annotationType()
                switch (methodName) {
                case "toString":
                    return annotationInfo.toString();
                case "hashCode": {
                    // hashCode() needs to function the same as the JDK implementation
                    // (see src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java in the JDK)
                    int result = 0;
                    for (final Entry<String, Object> ent : annotationParameterValuesInstantiated.entrySet()) {
                        final String paramName = ent.getKey();
                        final Object paramVal = ent.getValue();
                        int paramValHashCode;
                        if (paramVal == null) {
                            // Annotation values should never be null, but just to be safe
                            paramValHashCode = 0;
                        } else {
                            final Class<?> type = paramVal.getClass();
                            if (!type.isArray()) {
                                paramValHashCode = paramVal.hashCode();
                            } else if (type == byte[].class) {
                                paramValHashCode = Arrays.hashCode((byte[]) paramVal);
                            } else if (type == char[].class) {
                                paramValHashCode = Arrays.hashCode((char[]) paramVal);
                            } else if (type == double[].class) {
                                paramValHashCode = Arrays.hashCode((double[]) paramVal);
                            } else if (type == float[].class) {
                                paramValHashCode = Arrays.hashCode((float[]) paramVal);
                            } else if (type == int[].class) {
                                paramValHashCode = Arrays.hashCode((int[]) paramVal);
                            } else if (type == long[].class) {
                                paramValHashCode = Arrays.hashCode((long[]) paramVal);
                            } else if (type == short[].class) {
                                paramValHashCode = Arrays.hashCode((short[]) paramVal);
                            } else if (type == boolean[].class) {
                                paramValHashCode = Arrays.hashCode((boolean[]) paramVal);
                            } else {
                                paramValHashCode = Arrays.hashCode((Object[]) paramVal);
                            }
                        }
                        result += (127 * paramName.hashCode()) ^ paramValHashCode;
                    }
                    return result;
                }
                case "annotationType":
                    return annotationClass;
                default:
                    // Fall through (other method names are used for returning annotation parameter values)
                    break;
                }
            } else {
                // Throw exception for 2 or more params
                throw new IllegalArgumentException();
            }

            // Instantiate the annotation parameter value (this loads and gets references for class literals,
            // enum constants, etc.)
            final Object annotationParameterValue = annotationParameterValuesInstantiated.get(methodName);
            if (annotationParameterValue == null) {
                // Undefined enum constant (enum values cannot be null)
                throw new IncompleteAnnotationException(annotationClass, methodName);
            }

            // Clone any array-typed annotation parameter values, in keeping with the Java Annotation API
            final Class<?> annotationParameterValueClass = annotationParameterValue.getClass();
            if (annotationParameterValueClass.isArray()) {
                // Handle array types
                if (annotationParameterValueClass == String[].class) {
                    return ((String[]) annotationParameterValue).clone();
                } else if (annotationParameterValueClass == byte[].class) {
                    return ((byte[]) annotationParameterValue).clone();
                } else if (annotationParameterValueClass == char[].class) {
                    return ((char[]) annotationParameterValue).clone();
                } else if (annotationParameterValueClass == double[].class) {
                    return ((double[]) annotationParameterValue).clone();
                } else if (annotationParameterValueClass == float[].class) {
                    return ((float[]) annotationParameterValue).clone();
                } else if (annotationParameterValueClass == int[].class) {
                    return ((int[]) annotationParameterValue).clone();
                } else if (annotationParameterValueClass == long[].class) {
                    return ((long[]) annotationParameterValue).clone();
                } else if (annotationParameterValueClass == short[].class) {
                    return ((short[]) annotationParameterValue).clone();
                } else if (annotationParameterValueClass == boolean[].class) {
                    return ((boolean[]) annotationParameterValue).clone();
                } else {
                    // Handle arrays of nested annotation types
                    final Object[] arr = (Object[]) annotationParameterValue;
                    return arr.clone();
                }
            }
            return annotationParameterValue;
        
<methods>public java.lang.String toString() ,public java.lang.String toStringWithSimpleNames() <variables>private transient io.github.classgraph.ClassInfo classInfo,protected transient Class<?> classRef,protected transient io.github.classgraph.ScanResult scanResult