All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.webcohesion.enunciate.modules.spring_web.model.RequestParameterFactory Maven / Gradle / Ivy

There is a newer version: 2.7.0
Show newest version
/**
 * Copyright © 2006-2016 Web Cohesion ([email protected])
 *
 * 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.webcohesion.enunciate.modules.spring_web.model;

import com.webcohesion.enunciate.javac.decorations.DecoratedProcessingEnvironment;
import com.webcohesion.enunciate.javac.decorations.ElementDecorator;
import com.webcohesion.enunciate.javac.decorations.element.DecoratedTypeElement;
import com.webcohesion.enunciate.javac.decorations.element.DecoratedVariableElement;
import com.webcohesion.enunciate.javac.decorations.element.PropertyElement;
import com.webcohesion.enunciate.javac.decorations.type.DecoratedTypeMirror;
import com.webcohesion.enunciate.javac.decorations.type.TypeVariableContext;
import com.webcohesion.enunciate.metadata.Ignore;
import org.springframework.web.bind.annotation.*;

import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.util.ElementFilter;
import java.util.*;

/**
 * @author Ryan Heaton
 */
public class RequestParameterFactory {

  public static final Set KNOWN_SYSTEM_MANAGED_PARAMETER_TYPES = new TreeSet(Arrays.asList(
    //list of valid request mapping argument types that are supplied by the system, and not by the user.
    //see http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-arguments
    "javax.servlet.ServletContext", "javax.servlet.ServletRequest", "javax.servlet.ServletResponse", "javax.servlet.http.HttpSession",
    "org.springframework.web.context.request.WebRequest", "java.util.Locale", "java.util.TimeZone", "java.time.ZoneId",
    "java.io.Writer", "java.io.OutputStream", "org.springframework.http.HttpMethod", "java.security.Principal", "org.springframework.ui.Model",
    "org.springframework.ui.ModelMap", "java.util.Map", "org.springframework.web.servlet.mvc.support.RedirectAttributes",
    "org.springframework.validation.Errors", "org.springframework.validation.BindingResult", "org.springframework.web.bind.support.SessionStatus",
    "org.springframework.web.util.UriComponentsBuilder"
  ));

  public static List getRequestParameters(ExecutableElement mapping, VariableElement candidate, RequestMapping context) {
    ArrayList parameters = new ArrayList();

    if (!gatherAnnotatedRequestParameters(mapping, candidate, parameters, context) && !isSystemManagedParameter(candidate)) {
      gatherFormObjectParameters(candidate.asType(), parameters, context);
    }

    return parameters;
  }

  private static boolean isSystemManagedParameter(VariableElement candidate) {
    if (candidate.getAnnotation(ModelAttribute.class) != null) {
      //model attribute parameters are system-managed.
      //see http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-ann-modelattrib-methods
      return true;
    }

    TypeMirror parameterType = candidate.asType();
    while (parameterType instanceof DeclaredType) {
      Element element = ((DeclaredType) parameterType).asElement();
      if (element instanceof TypeElement) {
        String fqn = ((TypeElement) element).getQualifiedName().toString();
        if (KNOWN_SYSTEM_MANAGED_PARAMETER_TYPES.contains(fqn)) {
          //if it's a system parameter, it's not a request parameter.
          return true;
        }

        parameterType = ((TypeElement) element).getSuperclass();
      }
      else {
        parameterType = null;
      }
    }

    return false;
  }

  private static boolean gatherAnnotatedRequestParameters(ExecutableElement mapping, VariableElement candidate, List parameters, PathContext context) {
    List annotations = candidate.getAnnotationMirrors();

    boolean success = false;
    Boolean isMapStringString = null;
    for (AnnotationMirror annotation : annotations) {
      TypeElement declaration = (TypeElement) annotation.getAnnotationType().asElement();
      if (declaration != null) {
        String fqn = declaration.getQualifiedName().toString();
        if (Ignore.class.getName().equals(fqn)) {
          parameters.clear(); //we're told to ignore this, so clear it.
          return true;//and indicate successful gathering.
        }

        if (PathVariable.class.getName().equals(fqn)) {
          if ((isMapStringString == null && (isMapStringString = isMapStringString(candidate.asType(), new TypeVariableContext(), context.getContext().getContext().getProcessingEnvironment()))) || isMapStringString) {
            for (PathSegment segment : context.getPathSegments()) {
              if (segment.getVariable() != null) {
                parameters.add(new ExplicitRequestParameter(mapping, null, segment.getVariable(), ResourceParameterType.PATH, false, segment.getRegex() == null ? new ResourceParameterConstraints.UnboundString() : new ResourceParameterConstraints.Regex(segment.getRegex()), context.getContext()));
              }
            }
          }
          else {
            parameters.add(new SimpleRequestParameter(candidate, context));
          }
          success = true;
        }
        else if (MatrixVariable.class.getName().equals(fqn)
          || RequestParam.class.getName().equals(fqn)
          || RequestHeader.class.getName().equals(fqn)) {
          if ((isMapStringString == null && (!(isMapStringString = isMapStringString(candidate.asType(), new TypeVariableContext(), context.getContext().getContext().getProcessingEnvironment())))) || !isMapStringString) {
            //only add it if it's _not_ a map-string-string because otherwise we can't name the request parameters.
            parameters.add(new SimpleRequestParameter(candidate, context));
          }
          success = true;
        }
        else if (CookieValue.class.getName().equals(fqn)
          || RequestPart.class.getName().equals(fqn)) {
          parameters.add(new SimpleRequestParameter(candidate, context));
          success = true;
        }
      }
    }

    return success;
  }

  private static boolean isMapStringString(TypeMirror candidate, TypeVariableContext variableContext, DecoratedProcessingEnvironment env) {
    Element el = candidate instanceof DeclaredType ? ((DeclaredType)candidate).asElement() :
      candidate instanceof TypeVariable ? ((TypeVariable)candidate).asElement()
      : null;

    if (el instanceof TypeElement) {
      TypeElement element = (TypeElement) el;
      String fqn = element.getQualifiedName().toString();
      if (Object.class.getName().equals(fqn)) {
        return false;
      }
      else if ("org.springframework.util.MultiValueMap".equals(fqn) || Map.class.getName().equalsIgnoreCase(fqn)) {
        TypeMirror resolvedType = variableContext.resolveTypeVariables(candidate, env);
        if (resolvedType instanceof DeclaredType) {
          List typeArgs = ((DeclaredType) resolvedType).getTypeArguments();
          if (typeArgs.size() == 2 && ((DecoratedTypeMirror) typeArgs.get(0)).isInstanceOf(String.class) && ((DecoratedTypeMirror) typeArgs.get(1)).isInstanceOf(String.class)) {
            return true;
          }
        }
      }
      else {
        TypeMirror superclass = element.getSuperclass();
        if (superclass != null && superclass.getKind() != TypeKind.NONE) {
          return isMapStringString(superclass, variableContext.push(element.getTypeParameters(), candidate instanceof DeclaredType ? ((DeclaredType) candidate).getTypeArguments() : new ArrayList()), env);
        }
      }
    }

    return false;
  }

  private static void gatherFormObjectParameters(TypeMirror type, ArrayList params, RequestMapping context) {
    if (type instanceof DeclaredType) {
      Set methods = context.getHttpMethods();
      ResourceParameterType defaultType = methods.contains("POST") ? ResourceParameterType.FORM : ResourceParameterType.QUERY;
      DecoratedTypeElement typeDeclaration = (DecoratedTypeElement) ElementDecorator.decorate(((DeclaredType) type).asElement(), context.getContext().getContext().getProcessingEnvironment());
      for (VariableElement field : ElementFilter.fieldsIn(typeDeclaration.getEnclosedElements())) {
        DecoratedVariableElement decorated = (DecoratedVariableElement) field;
        if (!decorated.isFinal() && !decorated.isTransient() && decorated.isPublic()) {
          params.add(new SimpleRequestParameter(decorated, context, defaultType));
        }
      }

      for (PropertyElement property : typeDeclaration.getProperties()) {
        if (property.getSetter() != null) {
          params.add(new SimpleRequestParameter(property, context, defaultType));
        }
      }

      if (typeDeclaration.getKind() == ElementKind.CLASS) {
        gatherFormObjectParameters(typeDeclaration.getSuperclass(), params, context);
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy