<commit_before>package org.kohsuke.stapler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Abstracts the difference between normal instance methods and
 * static duck-typed methods.
 *
 * @author <NAME>
 */
abstract class Function {
    /**
     * Gets the method name.
     */
    abstract String getName();

    /**
     * Gets the human readable name of this function. Used to assist debugging.
     */
    abstract String getDisplayName();

    /**
     * Gets "className.methodName"
     */
    abstract String getQualifiedName();

    /**
     * Gets the type of parameters in a single array.
     */
    abstract Class[] getParameterTypes();

    /**
     * Gets the annotations on parameters.
     */
    abstract Annotation[][] getParameterAnnotations();

    /**
     * Gets the list of parameter names.
     */
    abstract String[] getParameterNames();

    /**
     * Calls {@link #bindAndInvoke(Object, StaplerRequest, StaplerResponse, Object...)} and then
     * optionally serve the response object.
     */
    void bindAndInvokeAndServeResponse(Object node, StaplerRequest req, StaplerResponse rsp, Object... headArgs) throws IllegalAccessException, InvocationTargetException, ServletException, IOException {
        try {
            Object ret = bindAndInvoke(node, req, rsp, headArgs);
            if (ret instanceof HttpResponse) {
                // let the result render the response
                HttpResponse response = (HttpResponse) ret;
                response.generateResponse(req,rsp,node);
            }
        } catch (InvocationTargetException e) {
            // exception as an HttpResponse
            Throwable te = e.getTargetException();
            if(te instanceof HttpResponse)
                ((HttpResponse)te).generateResponse(req,rsp,node);
            else
                throw e;
        }
    }

    /**
     * Use the given arguments as the first N arguments,
     * then figure out the rest of the arguments by looking at parameter annotations,
     * then finally call {@link #invoke}.
     */
    Object bindAndInvoke(Object o, StaplerRequest req, StaplerResponse rsp, Object... headArgs) throws IllegalAccessException, InvocationTargetException, ServletException {
        Class[] types = getParameterTypes();
        Annotation[][] annotations = getParameterAnnotations();
        String[] parameterNames = getParameterNames();

        Object[] arguments = new Object[types.length];

        // fill in the first N arguments
        System.arraycopy(headArgs,0,arguments,0,headArgs.length);

        try {
            // find the rest of the arguments. either known types, or with annotations
            for( int i=headArgs.length; i<types.length; i++ ) {
                Class t = types[i];
                if(t==StaplerRequest.class || t==HttpServletRequest.class) {
                    arguments[i] = req;
                    continue;
                }
                if(t==StaplerResponse.class || t==HttpServletResponse.class) {
                    arguments[i] = rsp;
                    continue;
                }
                arguments[i] = AnnotationHandler.handle(req,annotations[i],
                    i<parameterNames.length ? parameterNames[i] : null,
                    t);
            }
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to invoke "+getDisplayName(),e);
        }

        return invoke(req,o,arguments);
    }

    /**
     * Invokes the method.
     */
    abstract Object invoke(HttpServletRequest req, Object o, Object... args) throws IllegalAccessException, InvocationTargetException;

    final Function protectBy(Method m) {
        try {
            LimitedTo a = m.getAnnotation(LimitedTo.class);
            if(a==null)
                return this;    // not protected
            else
                return new ProtectedFunction(this,a.value());
        } catch (LinkageError e) {
            // running in JDK 1.4
            return this;
        }
    }

    public abstract <A extends Annotation> A getAnnotation(Class<A> annotation);

    private abstract static class MethodFunction extends Function {
        protected final Method m;
        private volatile String[] names;
<commit_msg>might as well extend AbstractList, not just Iterable.<commit_after>package org.kohsuke.stapler;

import com.google.common.collect.MapMaker;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * Abstracts the difference between normal instance methods and
 * static duck-typed methods.
 *
 * @author <NAME>
 */
abstract class Function {
    /**
     * Gets the method name.
     */
    abstract String getName();

    /**
     * Gets the human readable name of this function. Used to assist debugging.
     */
    abstract String getDisplayName();

    /**
     * Gets "className.methodName"
     */
    abstract String getQualifiedName();

    /**
     * Gets the type of parameters in a single array.
     */
    abstract Class[] getParameterTypes();

    /**
     * Gets the annotations on parameters.
     */
    abstract Annotation[][] getParameterAnnotations();

    /**
     * Gets the list of parameter names.
     */
    abstract String[] getParameterNames();

    /**
     * Calls {@link #bindAndInvoke(Object, StaplerRequest, StaplerResponse, Object...)} and then
     * optionally serve the response object.
     */
    void bindAndInvokeAndServeResponse(Object node, StaplerRequest req, StaplerResponse rsp, Object... headArgs) throws IllegalAccessException, InvocationTargetException, ServletException, IOException {
        try {
            Object ret = bindAndInvoke(node, req, rsp, headArgs);
            if (ret instanceof HttpResponse) {
                // let the result render the response
                HttpResponse response = (HttpResponse) ret;
                response.generateResponse(req,rsp,node);
            }
        } catch (InvocationTargetException e) {
            // exception as an HttpResponse
            Throwable te = e.getTargetException();
            if(te instanceof HttpResponse)
                ((HttpResponse)te).generateResponse(req,rsp,node);
            else
                throw e;
        }
    }

    /**
     * Use the given arguments as the first N arguments,
     * then figure out the rest of the arguments by looking at parameter annotations,
     * then finally call {@link #invoke}.
     */
    Object bindAndInvoke(Object o, StaplerRequest req, StaplerResponse rsp, Object... headArgs) throws IllegalAccessException, InvocationTargetException, ServletException {
        Class[] types = getParameterTypes();
        Annotation[][] annotations = getParameterAnnotations();
        String[] parameterNames = getParameterNames();

        Object[] arguments = new Object[types.length];

        // fill in the first N arguments
        System.arraycopy(headArgs,0,arguments,0,headArgs.length);

        try {
            // find the rest of the arguments. either known types, or with annotations
            for( int i=headArgs.length; i<types.length; i++ ) {
                Class t = types[i];
                if(t==StaplerRequest.class || t==HttpServletRequest.class) {
                    arguments[i] = req;
                    continue;
                }
                if(t==StaplerResponse.class || t==HttpServletResponse.class) {
                    arguments[i] = rsp;
                    continue;
                }

                // if the databinding method is provided, call that
                Function binder = PARSE_METHODS.get(t);
                if (binder!=RETURN_NULL) {
                    arguments[i] = binder.bindAndInvoke(null,req,rsp);
                    continue;
                }
                
                arguments[i] = AnnotationHandler.handle(req,annotations[i],
                    i<parameterNames.length ? parameterNames[i] : null,
                    t);
            }
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to invoke "+getDisplayName(),e);
        }

        return invoke(req,o,arguments);
    }

    /**
     * Computing map that discovers the static 'fromStapler' method from a class.
     * The discovered method will be returned as a Function so that the invocation can do parameter injections.
     */
    private static final Map<Class,Function> PARSE_METHODS;
    private static final Function RETURN_NULL;

    static {
        try {
            RETURN_NULL = new StaticFunction(Function.class.getMethod("returnNull"));
        } catch (NoSuchMethodException e) {
            throw new AssertionError(e);    // impossible
        }

        PARSE_METHODS = new MapMaker().weakKeys().makeComputingMap(new com.google.common.base.Function<Class,Function>() {
            public Function apply(Class from) {
                // MethdFunction for invoking a static method as a static method
                FunctionList methods = new ClassDescriptor(from).methods.name("fromStapler");
                switch (methods.size()) {
                case 0: return RETURN_NULL;
                default:
                    throw new IllegalArgumentException("Too many 'fromStapler' methods on "+from);
                case 1:
                    Method m = ((MethodFunction)methods.get(0)).m;
                    return new MethodFunction(m) {
                        @Override
                        Class[] getParameterTypes() {
                            return m.getParameterTypes();
                        }

                        @Override
                        Annotation[][] getParameterAnnotations() {
                            return m.getParameterAnnotations();
                        }

                        @Override
                        Object invoke(HttpServletRequest req, Object o, Object... args) throws IllegalAccessException, InvocationTargetException {
                            return m.invoke(null,args);
                        }
                    };
                }
            }
        });
    }

    public static Object returnNull() { return null; }

    /**
     * Invokes the method.
     */
    abstract Object invoke(HttpServletRequest req, Object o, Object... args) throws IllegalAccessException, InvocationTargetException;

    final Function protectBy(Method m) {
        try {
            LimitedTo a = m.getAnnotation(LimitedTo.class);
            if(a==null)
                return this;    // not protected
            else
                return new ProtectedFunction(this,a.value());
        } catch (LinkageError e) {
            // running in JDK 1.4
            return this;
        }
    }

    public abstract <A extends Annotation> A getAnnotation(Class<A> annotation);

    private abstract static class MethodFunction extends Function {
        protected final Method m;
        private volatile String[] names;

{'fraction_non_alphanumeric': 0.0898984593837535, 'fraction_numerical': 0.0009628851540616247, 'mean_word_length': 2.027291997880233, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5446010', 'n_tokens_mistral': 2838, 'n_tokens_neox': 2695, 'n_words': 801}