<commit_before>/*
 * Copyright 2015 Agapsys Tecnologia Ltda-ME.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.agapsys.rcf;

import com.agapsys.rcf.exceptions.ClientException;
import com.agapsys.rcf.exceptions.ForbiddenException;
import com.agapsys.rcf.exceptions.UnauthorizedException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Servlet responsible by mapping methods to actions
 */
public class Controller extends ActionServlet {

    // <editor-fold desc="STATIC SCOPE">
    // ========================================================================

    // <editor-fold desc="Private static members" defaultstate="collapsed">
    // -------------------------------------------------------------------------
    private static final Set<String> EMPTY_ROLE_SET = Collections.unmodifiableSet(new LinkedHashSet<String>());
    private static final Object[]    EMPTY_OBJ_ARRAY = new Object[] {};

    // Validates a candidate method to be interpreted as an action
    private static class MethodActionValidator {

        private static enum ArgGoal {
            INVALID (new Class[] {} ),
            REQUEST (new Class[] { ActionRequest.class, HttpServletRequest.class } ),
            RESPONSE(new Class[] { ActionResponse.class, HttpServletResponse.class} );

            private static ArgGoal __lookup(Class tested) {
                for (ArgGoal goal : ArgGoal.values()) {

                    for (Class c : goal.supportedClasses) {
                        if (c.isAssignableFrom(tested))
                            return goal;
                    }
                }

                return INVALID;
            }

            private static ArgGoal __lookup(String testedClassName) {
                try {
                    return __lookup(Class.forName(testedClassName));
                } catch (ClassNotFoundException ex) {
                    return INVALID;
                }
            }

            private final Class[] supportedClasses;

            private ArgGoal(Class[] supportedClasses) {
                this.supportedClasses = supportedClasses;
            }

        }

        /**
         * Checks if an annotated method signature matches with required one.
         *
         * @param method annotated method.
         * @return boolean indicating if method signature is valid.
         */
        private static boolean __matchSignature(Method method) {
            String signature = method.toGenericString();
            String[] tokens = signature.split(Pattern.quote(" "));

            if (!tokens[0].equals("public")) {
                return false;
            }

            int indexOfOpenParenthesis = signature.indexOf("(");
            int indexOfCloseParenthesis = signature.indexOf(")");

            String argString = signature.substring(indexOfOpenParenthesis + 1, indexOfCloseParenthesis).trim();
            String[] args = argString.isEmpty() ? new String[0] : argString.split(Pattern.quote(","));

            switch (args.length) {
                case 0:
                    return true;

                case 1:
                    return ArgGoal.__lookup(args[0]) != ArgGoal.INVALID;

                case 2: {
                    ArgGoal arg0Goal = ArgGoal.__lookup(args[0]);
                    ArgGoal arg1Goal = ArgGoal.__lookup(args[1]);

                    return !(arg0Goal == ArgGoal.INVALID || arg1Goal == ArgGoal.INVALID || (arg0Goal == arg1Goal));
                }

                default:
                    return false;
            }
        }

        private static Object[] __getCallParams(Method method, ActionRequest request, ActionResponse response) {
            if (method.getParameterCount() == 0) return EMPTY_OBJ_ARRAY;

            List argList = new LinkedList();

            for (Class<?> type : method.getParameterTypes()) {
                
                if (JsonRequest.class.isAssignableFrom(type)) {
                    argList.add(new JsonRequest(request));
                    continue;
                }
                
                if (JsonResponse.class.isAssignableFrom(type)) {
                    argList.add(new JsonResponse(response));
                    continue;
                }
                

                if (ActionRequest.class.isAssignableFrom(type)) {
                    argList.add(request);
                    continue;
                }

                if (ActionResponse.class.isAssignableFrom(type)) {
                    argList.add(response);
                    continue;
                }

                if (HttpServletRequest.class.isAssignableFrom(type)) {
                    argList.add(request.getServletRequest());
                    continue;
                }

                if (HttpServletResponse.class.isAssignableFrom(type)) {
                    argList.add(response.getServletResponse());
                    continue;
                }

                throw new UnsupportedOperationException(String.format("Unsupported param type: %s", type.getName()));
            }

            return argList.toArray();
        }
    }
    // -------------------------------------------------------------------------
    // </editor-fold>

    /** Defines a Data Transfer Object */
    public static interface Dto {

        /**
         * Returns a transfer object associated with this instance.
         *
         * @return a transfer object associated with this instance.
         */
        public Object getDto();
    }

    /** Name of the session attribute used to store current user. */
    public static final String SESSION_ATTR_USER = Controller.class.getName() + ".SESSION_ATTR_USER";

    /** Name of the default session attribute used to store CSRF token. */
    public static final String SESSION_ATTR_CSRF_TOKEN = Controller.class.getName() + ".SESSION_ATTR_CSRF_TOKEN";

    /** Name of the header used to send/retrieve a CSRF token. */
    public static final String CSRF_HEADER  = "X-Csrf-Token";

    // Default size of CSRF token
    private static final int CSRF_TOKEN_LENGTH = 128;<commit_msg>Added generics to DTO<commit_after>/*
 * Copyright 2015 Agapsys Tecnologia Ltda-ME.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.agapsys.rcf;

import com.agapsys.rcf.exceptions.ClientException;
import com.agapsys.rcf.exceptions.ForbiddenException;
import com.agapsys.rcf.exceptions.UnauthorizedException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Servlet responsible by mapping methods to actions
 */
public class Controller extends ActionServlet {

    // <editor-fold desc="STATIC SCOPE">
    // ========================================================================

    // <editor-fold desc="Private static members" defaultstate="collapsed">
    // -------------------------------------------------------------------------
    private static final Set<String> EMPTY_ROLE_SET = Collections.unmodifiableSet(new LinkedHashSet<String>());
    private static final Object[]    EMPTY_OBJ_ARRAY = new Object[] {};

    // Validates a candidate method to be interpreted as an action
    private static class MethodActionValidator {

        private static enum ArgGoal {
            INVALID (new Class[] {} ),
            REQUEST (new Class[] { ActionRequest.class, HttpServletRequest.class } ),
            RESPONSE(new Class[] { ActionResponse.class, HttpServletResponse.class} );

            private static ArgGoal __lookup(Class tested) {
                for (ArgGoal goal : ArgGoal.values()) {

                    for (Class c : goal.supportedClasses) {
                        if (c.isAssignableFrom(tested))
                            return goal;
                    }
                }

                return INVALID;
            }

            private static ArgGoal __lookup(String testedClassName) {
                try {
                    return __lookup(Class.forName(testedClassName));
                } catch (ClassNotFoundException ex) {
                    return INVALID;
                }
            }

            private final Class[] supportedClasses;

            private ArgGoal(Class[] supportedClasses) {
                this.supportedClasses = supportedClasses;
            }

        }

        /**
         * Checks if an annotated method signature matches with required one.
         *
         * @param method annotated method.
         * @return boolean indicating if method signature is valid.
         */
        private static boolean __matchSignature(Method method) {
            String signature = method.toGenericString();
            String[] tokens = signature.split(Pattern.quote(" "));

            if (!tokens[0].equals("public")) {
                return false;
            }

            int indexOfOpenParenthesis = signature.indexOf("(");
            int indexOfCloseParenthesis = signature.indexOf(")");

            String argString = signature.substring(indexOfOpenParenthesis + 1, indexOfCloseParenthesis).trim();
            String[] args = argString.isEmpty() ? new String[0] : argString.split(Pattern.quote(","));

            switch (args.length) {
                case 0:
                    return true;

                case 1:
                    return ArgGoal.__lookup(args[0]) != ArgGoal.INVALID;

                case 2: {
                    ArgGoal arg0Goal = ArgGoal.__lookup(args[0]);
                    ArgGoal arg1Goal = ArgGoal.__lookup(args[1]);

                    return !(arg0Goal == ArgGoal.INVALID || arg1Goal == ArgGoal.INVALID || (arg0Goal == arg1Goal));
                }

                default:
                    return false;
            }
        }

        private static Object[] __getCallParams(Method method, ActionRequest request, ActionResponse response) {
            if (method.getParameterCount() == 0) return EMPTY_OBJ_ARRAY;

            List argList = new LinkedList();

            for (Class<?> type : method.getParameterTypes()) {
                
                if (JsonRequest.class.isAssignableFrom(type)) {
                    argList.add(new JsonRequest(request));
                    continue;
                }
                
                if (JsonResponse.class.isAssignableFrom(type)) {
                    argList.add(new JsonResponse(response));
                    continue;
                }
                

                if (ActionRequest.class.isAssignableFrom(type)) {
                    argList.add(request);
                    continue;
                }

                if (ActionResponse.class.isAssignableFrom(type)) {
                    argList.add(response);
                    continue;
                }

                if (HttpServletRequest.class.isAssignableFrom(type)) {
                    argList.add(request.getServletRequest());
                    continue;
                }

                if (HttpServletResponse.class.isAssignableFrom(type)) {
                    argList.add(response.getServletResponse());
                    continue;
                }

                throw new UnsupportedOperationException(String.format("Unsupported param type: %s", type.getName()));
            }

            return argList.toArray();
        }
    }
    // -------------------------------------------------------------------------
    // </editor-fold>

    /** Defines a Data Transfer Object */
    public static interface Dto<T> {

        /**
         * Returns a transfer object associated with this instance.
         *
         * @return a transfer object associated with this instance.
         */
        public T getDto();
    }

    /** Name of the session attribute used to store current user. */
    public static final String SESSION_ATTR_USER = Controller.class.getName() + ".SESSION_ATTR_USER";

    /** Name of the default session attribute used to store CSRF token. */
    public static final String SESSION_ATTR_CSRF_TOKEN = Controller.class.getName() + ".SESSION_ATTR_CSRF_TOKEN";

    /** Name of the header used to send/retrieve a CSRF token. */
    public static final String CSRF_HEADER  = "X-Csrf-Token";

    // Default size of CSRF token
    private static final int CSRF_TOKEN_LENGTH = 128;
{'fraction_non_alphanumeric': 0.1177709152161707, 'fraction_numerical': 0.003790005614823133, 'mean_word_length': 1.876261606782398, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '6951496', 'n_tokens_mistral': 3553, 'n_tokens_neox': 3345, 'n_words': 926}