gt:                     List<Parameter> parameters = determineParameters(method);
context: /**
   * This file is part of Graylog.
   *
   * Graylog is free software: you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * Graylog is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with Graylog.  If not, see <http://www.gnu.org/licenses/>.
   */
  package org.graylog2.shared.rest.documentation.generator;
  
  import com.fasterxml.jackson.annotation.JsonAutoDetect;
  import com.fasterxml.jackson.annotation.JsonIgnore;
  import com.fasterxml.jackson.annotation.JsonProperty;
  import com.fasterxml.jackson.databind.JsonMappingException;
  import com.fasterxml.jackson.databind.ObjectMapper;
  import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
  import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper;
  import com.google.common.collect.ComparisonChain;
  import com.google.common.collect.ImmutableMap;
  import com.google.common.collect.Lists;
  import com.google.common.collect.Maps;
  import com.google.common.collect.Sets;
  import com.google.common.primitives.Primitives;
  import com.wordnik.swagger.annotations.Api;
  import com.wordnik.swagger.annotations.ApiOperation;
  import com.wordnik.swagger.annotations.ApiParam;
  import com.wordnik.swagger.annotations.ApiResponse;
  import com.wordnik.swagger.annotations.ApiResponses;
  import org.graylog2.shared.ServerVersion;
  import org.reflections.Reflections;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import javax.ws.rs.DELETE;
  import javax.ws.rs.FormParam;
  import javax.ws.rs.GET;
  import javax.ws.rs.HEAD;
  import javax.ws.rs.HeaderParam;
  import javax.ws.rs.OPTIONS;
  import javax.ws.rs.POST;
  import javax.ws.rs.PUT;
  import javax.ws.rs.Path;
  import javax.ws.rs.PathParam;
  import javax.ws.rs.Produces;
  import javax.ws.rs.QueryParam;
  import javax.ws.rs.core.Response;
  import java.lang.annotation.Annotation;
  import java.lang.reflect.Method;
  import java.lang.reflect.ParameterizedType;
  import java.lang.reflect.Type;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  /**
   * This is generating API information in Swagger format.
   * <p/>
   * http://swagger.wordnik.com/
   * <p/>
   * We decided to write this ourselves and not to use the Swagger JAXRS/Jersey integration
   * because it was not compatible to Jersey2 at that point and just way too complicated
   * and too big for what we want to do with it.
   */
  public class Generator {
  
      private static final Logger LOG = LoggerFactory.getLogger(Generator.class);
  
      public static final String EMULATED_SWAGGER_VERSION = "1.2";
  
      private static Map<String, Object> overviewResult = Maps.newHashMap();
      private static Reflections reflections;
  
      private final ObjectMapper mapper;
  
      public Generator(String[] packageNames, ObjectMapper mapper) {
          this.mapper = mapper;
  
          if (reflections == null) {
              reflections = new Reflections((Object[]) packageNames);
          }
      }
  
      public Generator(String packageName, ObjectMapper mapper) {
          this(new String[]{packageName}, mapper);
      }
  
      public synchronized Map<String, Object> generateOverview() {
          if (!overviewResult.isEmpty()) {
              return overviewResult;
          }
  
          final List<Map<String, Object>> apis = Lists.newArrayList();
          for (Class<?> clazz : getAnnotatedClasses()) {
              Api info = clazz.getAnnotation(Api.class);
              Path path = clazz.getAnnotation(Path.class);
  
              if (info == null || path == null) {
                  LOG.debug("Skipping REST resource with no Api or Path annotation: <{}>", clazz.getCanonicalName());
                  continue;
              }
  
              final Map<String, Object> apiDescription = Maps.newHashMap();
              apiDescription.put("name", info.value());
              apiDescription.put("path", path.value());
              apiDescription.put("description", info.description());
  
              apis.add(apiDescription);
          }
          Collections.sort(apis, new Comparator<Map<String, Object>>() {
              @Override
              public int compare(Map<String, Object> o1, Map<String, Object> o2) {
                  return ComparisonChain.start().compare(o1.get("name").toString(), o2.get("name").toString()).result();
              }
          });
          Map<String, String> info = Maps.newHashMap();
          info.put("title", "Graylog REST API");
  
          overviewResult.put("apiVersion", ServerVersion.VERSION.toString());
          overviewResult.put("swaggerVersion", EMULATED_SWAGGER_VERSION);
          overviewResult.put("apis", apis);
  
          return overviewResult;
      }
  
      public Set<Class<?>> getAnnotatedClasses() {
          return reflections.getTypesAnnotatedWith(Api.class);
      }
  
      public Map<String, Object> generateForRoute(String route, String basePath) {
          Map<String, Object> result = Maps.newHashMap();
          Set<Class<?>> modelTypes = Sets.newHashSet();
          List<Map<String, Object>> apis = Lists.newArrayList();
  
          for (Class<?> clazz : getAnnotatedClasses()) {
              Path path = clazz.getAnnotation(Path.class);
              if (path == null) {
                  LOG.debug("Skipping REST resource with no Api or Path annotation: <{}>", clazz.getCanonicalName());
                  continue;
              }
  
              if (cleanRoute(route).equals(cleanRoute(path.value()))) {
                  // This is the class representing the given route. Get all methods.
                  LOG.debug("Found corresponding REST resource class: <{}>", clazz.getCanonicalName());
  
                  Method[] methods = clazz.getDeclaredMethods();
                  if (methods == null || methods.length == 0) {
                      LOG.debug("REST resource <{}> has no methods. Skipping.", clazz.getCanonicalName());
                      break;
                  }
  
                  for (Method method : methods) {
                      if (!method.isAnnotationPresent(ApiOperation.class)) {
                          LOG.debug("Method <{}> has no ApiOperation annotation. Skipping.", method.toGenericString());
                          continue;
                      }
  
                      ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
  
                      Map<String, Object> api = Maps.newHashMap();
                      List<Map<String, Object>> operations = Lists.newArrayList();
  
                      String methodPath;
                      if (method.isAnnotationPresent(Path.class)) {
                          // Method has annotated Path.
                          methodPath = cleanRoute(method.getAnnotation(Path.class).value());
  
                          if (clazz.isAnnotationPresent(Path.class)) {
                              // The class has a Path, too. Prepend.
                              String classPath = cleanRoute(clazz.getAnnotation(Path.class).value());
                              methodPath = classPath + methodPath;
                          }
                      } else {
                          // Method has no annotated Path. We read from it's class.
                          if (clazz.isAnnotationPresent(Path.class)) {
                              methodPath = cleanRoute(clazz.getAnnotation(Path.class).value());
                          } else {
                              LOG.debug("Method <{}> has no Path annotation. Skipping.", method.toGenericString());
                              continue;
                          }
                      }
  
                      Produces produces = null;
                      if (clazz.isAnnotationPresent(Produces.class) || method.isAnnotationPresent(Produces.class)) {
                          produces = clazz.getAnnotation(Produces.class);
                          if (method.isAnnotationPresent(Produces.class)) {
                              produces = method.getAnnotation(Produces.class);
                          }
                      }
                      api.put("path", methodPath);
  
                      Map<String, Object> operation = Maps.newHashMap();
                      operation.put("method", determineHttpMethod(method));
                      operation.put("summary", apiOperation.value());
                      operation.put("notes", apiOperation.notes());
                      operation.put("nickname", method.getName());
                      if (produces != null) {
                          operation.put("produces", produces.value());
                      }
                      // skip Response.class because we can't reliably infer any schema information from its payload anyway.
                      if (!method.getReturnType().isAssignableFrom(Response.class)) {
                          operation.put("type", method.getReturnType().getSimpleName());
                          modelTypes.add(method.getReturnType());
                      }
  
  