/** * Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org> * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.seedstack.seed.rest.internal; import org.seedstack.seed.rest.Rel; import org.seedstack.shed.reflect.Classes; import javax.ws.rs.BeanParam; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import static org.seedstack.seed.rest.internal.UriBuilder.uri; /** * Provides a set of functions which scan HTTP methods to find various information * like rel, path, query parameters, etc. */ class RESTReflect { private RESTReflect() { } /** * Finds a the rel of a method. The rel can be found on the declaring class, * but the rel on the method will have the precedence. * * @param method the method to scan * @return the rel annotation */ static Rel findRel(Method method) { Rel rootRel = method.getDeclaringClass().getAnnotation(Rel.class); Rel rel = method.getAnnotation(Rel.class); if (rel != null) { return rel; } else if (rootRel != null) { return rootRel; } return null; } /** * Finds the path of a method. If the declaring class and the method are both * annotated with {@literal @}Path, the paths will be concatenated. * <p> * Leading or ending slashes in paths are not taken in account. The method * will always add a leading slash and strip the ending slash. * </p> * <p> * If JAX-RS regex are present in the path, they will be replaced by * UriTemplate expressions. * </p> * For example: * <pre> * {@literal @}Path("widgets/{widgetName: [a-zA-Z][a-zA-Z_0-9]}/") --> /widgets/{widgetName} * </pre> * * @param method the method to scan * @return the resource's path */ static String findPath(Method method) { Path pathFromClass = method.getDeclaringClass().getAnnotation(Path.class); Path pathFromMethod = method.getAnnotation(Path.class); String path = concatenatePathValues(pathFromClass, pathFromMethod); if (path == null) { return null; } else { path = addLeadingSlash(path); return UriBuilder.stripJaxRsRegex(path); } } private static String concatenatePathValues(Path pathFromClass, Path pathFromMethod) { boolean hasPathOnClass = pathFromClass != null; boolean hasPathOnMethod = pathFromMethod != null; String path = null; if (hasPathOnMethod && hasPathOnClass) { path = uri(pathFromClass.value(), pathFromMethod.value()); } else if (hasPathOnClass) { path = uri(pathFromClass.value()); } else if (hasPathOnMethod) { path = uri(pathFromMethod.value()); } return path; } private static String addLeadingSlash(String path) { if (!path.startsWith("/")) { path = "/" + path; } return path; } /** * Finds path parameters based on {@link javax.ws.rs.PathParam} annotation. * * @param baseParam the base parameter URI * @param method the method to scan * @return the map of parameter URI by parameter name */ static Map<String, String> findPathParams(String baseParam, Method method) { Map<String, String> hrefVars = new HashMap<>(); for (Annotation[] paramAnnotations : method.getParameterAnnotations()) { for (Annotation paramAnnotation : paramAnnotations) { if (paramAnnotation.annotationType().equals(PathParam.class)) { String varName = ((PathParam) paramAnnotation).value(); addHrefVar(baseParam, hrefVars, varName); } } } return hrefVars; } /** * Finds query parameters based on {@link javax.ws.rs.QueryParam} annotation. * * @param baseParam the base parameter URI * @param method the method to scan * @return the map of parameter URI by parameter name */ static Map<String, String> findQueryParams(String baseParam, Method method) { Map<String, String> hrefVars = new HashMap<>(); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); for (int i = 0; i < parameterAnnotations.length; i++) { Annotation[] paramAnnotations = parameterAnnotations[i]; for (Annotation paramAnnotation : paramAnnotations) { Class<?> parameterClass = method.getParameterTypes()[i]; hrefVars.putAll(findQueryParamOnParameter(baseParam, parameterClass, paramAnnotation)); } } return hrefVars; } private static Map<String, String> findQueryParamOnParameter(String baseParam, Class<?> parameterClass, Annotation paramAnnotation) { Map<String, String> hrefVars = new HashMap<>(); if (paramAnnotation.annotationType().equals(QueryParam.class)) { String varName = ((QueryParam) paramAnnotation).value(); addHrefVar(baseParam, hrefVars, varName); } else if (Classes.optional("javax.ws.rs.BeanParam").isPresent() && paramAnnotation.annotationType().equals(BeanParam.class)) { hrefVars.putAll(findQueryParamOnFields(baseParam, parameterClass)); } return hrefVars; } private static String addHrefVar(String baseParam, Map<String, String> hrefVars, String varName) { return hrefVars.put(varName, uri(baseParam, varName)); } private static Map<String, String> findQueryParamOnFields(String baseParam, Class<?> aClass) { Map<String, String> hrefVars = new HashMap<>(); for (Field field : aClass.getDeclaredFields()) { if (field.getAnnotation(QueryParam.class) != null) { addHrefVar(baseParam, hrefVars, field.getName()); } } return hrefVars; } }