gt:                             for (Parameter param : operation.getParameters()) {
context: /*******************************************************************************
   * Copyright (c) 2017, 2018 IBM Corporation and others.
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   *
   * Contributors:
   *     IBM Corporation - initial API and implementation
   *******************************************************************************/
  
  package com.ibm.ws.microprofile.openapi;
  
  import java.lang.annotation.Annotation;
  import java.lang.reflect.Method;
  import java.lang.reflect.Type;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.LinkedHashMap;
  import java.util.LinkedHashSet;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Optional;
  import java.util.Set;
  import java.util.TreeSet;
  import java.util.stream.Collectors;
  import java.util.stream.Stream;
  
  import javax.ws.rs.Consumes;
  import javax.ws.rs.Produces;
  
  import org.apache.commons.lang3.ArrayUtils;
  import org.apache.commons.lang3.StringUtils;
  import org.eclipse.microprofile.openapi.annotations.ExternalDocumentation;
  import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition;
  import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
  import org.eclipse.microprofile.openapi.annotations.media.ExampleObject;
  import org.eclipse.microprofile.openapi.annotations.parameters.Parameters;
  import org.eclipse.microprofile.openapi.annotations.servers.Server;
  import org.eclipse.microprofile.openapi.models.Components;
  import org.eclipse.microprofile.openapi.models.OpenAPI;
  import org.eclipse.microprofile.openapi.models.Operation;
  import org.eclipse.microprofile.openapi.models.PathItem;
  import org.eclipse.microprofile.openapi.models.Paths;
  import org.eclipse.microprofile.openapi.models.callbacks.Callback;
  import org.eclipse.microprofile.openapi.models.examples.Example;
  import org.eclipse.microprofile.openapi.models.links.Link;
  import org.eclipse.microprofile.openapi.models.media.Content;
  import org.eclipse.microprofile.openapi.models.media.MediaType;
  import org.eclipse.microprofile.openapi.models.media.Schema;
  import org.eclipse.microprofile.openapi.models.parameters.Parameter;
  import org.eclipse.microprofile.openapi.models.parameters.RequestBody;
  import org.eclipse.microprofile.openapi.models.responses.APIResponse;
  import org.eclipse.microprofile.openapi.models.responses.APIResponses;
  import org.eclipse.microprofile.openapi.models.security.SecurityRequirement;
  import org.eclipse.microprofile.openapi.models.security.SecurityScheme;
  import org.eclipse.microprofile.openapi.models.tags.Tag;
  
  import com.fasterxml.jackson.databind.BeanDescription;
  import com.fasterxml.jackson.databind.JavaType;
  import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
  import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
  import com.fasterxml.jackson.databind.type.TypeFactory;
  import com.ibm.ws.microprofile.openapi.impl.core.converter.ModelConverters;
  import com.ibm.ws.microprofile.openapi.impl.core.converter.ResolvedSchema;
  import com.ibm.ws.microprofile.openapi.impl.core.util.AnnotationsUtils;
  import com.ibm.ws.microprofile.openapi.impl.core.util.BaseReaderUtils;
  import com.ibm.ws.microprofile.openapi.impl.core.util.Json;
  import com.ibm.ws.microprofile.openapi.impl.core.util.ParameterProcessor;
  import com.ibm.ws.microprofile.openapi.impl.core.util.PathUtils;
  import com.ibm.ws.microprofile.openapi.impl.core.util.ReflectionUtils;
  import com.ibm.ws.microprofile.openapi.impl.jaxrs2.OperationParser;
  import com.ibm.ws.microprofile.openapi.impl.jaxrs2.ResolvedParameter;
  import com.ibm.ws.microprofile.openapi.impl.jaxrs2.SecurityParser;
  import com.ibm.ws.microprofile.openapi.impl.jaxrs2.ext.OpenAPIExtension;
  import com.ibm.ws.microprofile.openapi.impl.jaxrs2.ext.OpenAPIExtensions;
  import com.ibm.ws.microprofile.openapi.impl.jaxrs2.util.ReaderUtils;
  import com.ibm.ws.microprofile.openapi.impl.model.ComponentsImpl;
  import com.ibm.ws.microprofile.openapi.impl.model.OpenAPIImpl;
  import com.ibm.ws.microprofile.openapi.impl.model.OperationImpl;
  import com.ibm.ws.microprofile.openapi.impl.model.PathItemImpl;
  import com.ibm.ws.microprofile.openapi.impl.model.PathsImpl;
  import com.ibm.ws.microprofile.openapi.impl.model.callbacks.CallbackImpl;
  import com.ibm.ws.microprofile.openapi.impl.model.media.ContentImpl;
  import com.ibm.ws.microprofile.openapi.impl.model.media.MediaTypeImpl;
  import com.ibm.ws.microprofile.openapi.impl.model.media.SchemaImpl;
  import com.ibm.ws.microprofile.openapi.impl.model.parameters.ParameterImpl;
  import com.ibm.ws.microprofile.openapi.impl.model.parameters.RequestBodyImpl;
  import com.ibm.ws.microprofile.openapi.impl.model.responses.APIResponseImpl;
  import com.ibm.ws.microprofile.openapi.impl.model.responses.APIResponsesImpl;
  import com.ibm.ws.microprofile.openapi.impl.model.security.SecuritySchemeImpl;
  
  public class Reader {
      //private static final Logger LOGGER = LoggerFactory.getLogger(Reader.class);
  
      public static final String DEFAULT_MEDIA_TYPE_VALUE = "*/*";
      public static final String DEFAULT_DESCRIPTION = "default response";
  
      private final OpenAPI openAPI;
      private final Components components;
      private final Paths paths;
      private final Set<Tag> openApiTags;
      private String applicationPath;
  
      private static final String GET_METHOD = "get";
      private static final String POST_METHOD = "post";
      private static final String PUT_METHOD = "put";
      private static final String DELETE_METHOD = "delete";
      private static final String PATCH_METHOD = "patch";
      private static final String TRACE_METHOD = "trace";
      private static final String HEAD_METHOD = "head";
      private static final String OPTIONS_METHOD = "options";
  
      public Reader() {
          this.openAPI = new OpenAPIImpl();
          paths = new PathsImpl();
          openApiTags = new LinkedHashSet<>();
          components = new ComponentsImpl();
  
      }
  
      public Reader(OpenAPI openAPI) {
          if (openAPI != null) {
              this.openAPI = openAPI;
          } else {
              this.openAPI = new OpenAPIImpl();
              openAPI = this.openAPI;
          }
          if (openAPI.getPaths() != null) {
              this.paths = openAPI.getPaths();
          } else {
              this.paths = new PathsImpl();
          }
          openApiTags = new LinkedHashSet<>();
          if (openAPI.getTags() != null)
              openApiTags.addAll(openAPI.getTags());
          if (openAPI.getComponents() != null) {
              this.components = openAPI.getComponents();
          } else {
              this.components = new ComponentsImpl();
          }
      }
  
      public OpenAPI getOpenAPI() {
          return openAPI;
      }
  
      /**
       * Scans a single class for Swagger annotations - does not invoke ReaderListeners
       */
      public OpenAPI read(Class<?> cls) {
          return read(cls, this.applicationPath != null ? this.applicationPath : "");
      }
  
      /**
       * Scans a set of classes for both ReaderListeners and OpenAPI annotations. All found listeners will
       * be instantiated before any of the classes are scanned for OpenAPI annotations - so they can be invoked
       * accordingly.
       *
       * @param classes a set of classes to scan
       * @return the generated OpenAPI definition
       */
      public OpenAPI read(Set<Class<?>> classes) {
          Set<Class<?>> sortedClasses = new TreeSet<>(new Comparator<Class<?>>() {
              @Override
              public int compare(Class<?> class1, Class<?> class2) {
                  if (class1.equals(class2)) {
                      return 0;
                  } else if (class1.isAssignableFrom(class2)) {
                      return -1;
                  } else if (class2.isAssignableFrom(class1)) {
                      return 1;
                  }
                  return class1.getName().compareTo(class2.getName());
              }
          });
          sortedClasses.addAll(classes);
  
          for (Class<?> cls : sortedClasses) {
              read(cls, this.applicationPath != null ? applicationPath : "");
          }
          return openAPI;
      }
  
      public OpenAPI read(Set<Class<?>> classes, Map<String, Object> resources) {
          return read(classes);
      }
  
      public OpenAPI read(Class<?> cls, String parentPath) {
  
          List<org.eclipse.microprofile.openapi.annotations.security.SecurityScheme> apiSecurityScheme = ReflectionUtils.getRepeatableAnnotations(cls,
                                                                                                                                                  org.eclipse.microprofile.openapi.annotations.security.SecurityScheme.class);
          List<org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement> apiSecurityRequirements = ReflectionUtils.getRepeatableAnnotations(cls,
                                                                                                                                                             org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement.class);
          org.eclipse.microprofile.openapi.annotations.security.SecurityRequirementsSet apiSecurityRequirementSet = ReflectionUtils.getAnnotation(cls,
                                                                                                                                                  org.eclipse.microprofile.openapi.annotations.security.SecurityRequirementsSet.class);
          List<org.eclipse.microprofile.openapi.annotations.servers.Server> apiServers = ReflectionUtils.getRepeatableAnnotations(cls,
                                                                                                                                  org.eclipse.microprofile.openapi.annotations.servers.Server.class);
          List<org.eclipse.microprofile.openapi.annotations.callbacks.Callback> apiCallbacks = ReflectionUtils.getRepeatableAnnotations(cls,
                                                                                                                                        org.eclipse.microprofile.openapi.annotations.callbacks.Callback.class);
  
          List<Extension> classExtensions = ReflectionUtils.getRepeatableAnnotations(cls, Extension.class);
  
          if (classExtensions != null && classExtensions.size() > 0) {
              Map<String, Object> ext = BaseReaderUtils.parseExtensions(classExtensions.toArray(new org.eclipse.microprofile.openapi.annotations.extensions.Extension[classExtensions.size()]));
              if (ext != null && ext.size() > 0) {
                  ext.forEach((k, v) -> {
                      ((OpenAPIImpl) openAPI).addExtension_compat(k, v);
                  });
              }
          }
  
          ExternalDocumentation apiExternalDocs = ReflectionUtils.getAnnotation(cls, ExternalDocumentation.class);
          org.eclipse.microprofile.openapi.annotations.tags.Tag[] apiTags = ReflectionUtils.getRepeatableAnnotationsArray(cls,
                                                                                                                          org.eclipse.microprofile.openapi.annotations.tags.Tag.class);
          org.eclipse.microprofile.openapi.annotations.tags.Tags tagsAnnotation = ReflectionUtils.getAnnotation(cls,
                                                                                                                org.eclipse.microprofile.openapi.annotations.tags.Tags.class);
  
          javax.ws.rs.Consumes classConsumes = ReflectionUtils.getAnnotation(cls, javax.ws.rs.Consumes.class);
          javax.ws.rs.Produces classProduces = ReflectionUtils.getAnnotation(cls, javax.ws.rs.Produces.class);
  
          // class security schemes
          if (apiSecurityScheme != null) {
              for (org.eclipse.microprofile.openapi.annotations.security.SecurityScheme securitySchemeAnnotation : apiSecurityScheme) {
                  Optional<SecurityScheme> securityScheme = SecurityParser.getSecurityScheme(securitySchemeAnnotation);
                  if (securityScheme.isPresent()) {
                      Map<String, SecurityScheme> securitySchemeMap = new HashMap<>();
                      if (StringUtils.isNotBlank(((SecuritySchemeImpl) securityScheme.get()).getSchemeName())) {
                          securitySchemeMap.put(((SecuritySchemeImpl) securityScheme.get()).getSchemeName(), securityScheme.get());
                          if (components.getSecuritySchemes() != null && components.getSecuritySchemes().size() != 0) {
                              components.getSecuritySchemes().putAll(securitySchemeMap);
                          } else {
                              components.setSecuritySchemes(securitySchemeMap);
                          }
                      }
                  }
              }
          }
  
          // class security requirements
          List<SecurityRequirement> classSecurityRequirements = new ArrayList<>();
          if (apiSecurityRequirements != null) {
              Optional<List<SecurityRequirement>> requirementsObject = SecurityParser.getSecurityRequirements(
                                                                                                              apiSecurityRequirements.toArray(new org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement[apiSecurityRequirements.size()]));
              if (requirementsObject.isPresent()) {
                  classSecurityRequirements = requirementsObject.get();
              }
          }
          if (apiSecurityRequirementSet != null) {
              Optional<SecurityRequirement> requirementsObject = SecurityParser.getSecurityRequirementFromSet(apiSecurityRequirementSet);
              if (requirementsObject.isPresent()) {
                  classSecurityRequirements.add(requirementsObject.get());
              }
          }
  
          List<org.eclipse.microprofile.openapi.models.servers.Server> classServers = new ArrayList<>();
          if (apiServers != null) {
              Optional<List<org.eclipse.microprofile.openapi.models.servers.Server>> serversObject = AnnotationsUtils.getServers(
                                                                                                                                 apiServers.toArray(new org.eclipse.microprofile.openapi.annotations.servers.Server[apiServers.size()]));
              if (serversObject.isPresent()) {
                  classServers = serversObject.get();
              }
          }
  
          Map<String, Callback> classCallbacks = null;
          if (apiCallbacks != null) {
              Map<String, Callback> callbacks = new LinkedHashMap<>();
              for (org.eclipse.microprofile.openapi.annotations.callbacks.Callback classCallback : apiCallbacks) {
                  Map<String, Callback> currentCallbacks = getCallbacks(classCallback);
                  callbacks.putAll(currentCallbacks);
              }
              if (callbacks.size() > 0) {
                  classCallbacks = callbacks;
              }
          }
  
          // class tags, consider only name to add to class operations
          final Set<String> classTags = new LinkedHashSet<>();
          if (apiTags != null) {
              AnnotationsUtils.getTags(apiTags, false).ifPresent(tags -> tags.stream().map(t -> t.getName()).forEach(t -> classTags.add(t)));
          }
          if (tagsAnnotation != null && tagsAnnotation.refs() != null) {
              classTags.addAll(Stream.of(tagsAnnotation.refs()).filter(StringUtils::isNotBlank).collect(Collectors.toList()));
          }
  
          // class external docs
          Optional<org.eclipse.microprofile.openapi.models.ExternalDocumentation> classExternalDocumentation = AnnotationsUtils.getExternalDocumentation(apiExternalDocs);
  
          // class path
          final javax.ws.rs.Path apiPath = ReflectionUtils.getAnnotation(cls, javax.ws.rs.Path.class);
  
          JavaType classType = TypeFactory.defaultInstance().constructType(cls);
          BeanDescription bd = Json.mapper().getSerializationConfig().introspect(classType);
  
          final List<Parameter> globalParameters = new ArrayList<>();
  
          // look for constructor-level annotated properties
          globalParameters.addAll(ReaderUtils.collectConstructorParameters(cls, components, classConsumes));
  
          // look for field-level annotated properties
          globalParameters.addAll(ReaderUtils.collectFieldParameters(cls, components, classConsumes));
  
          // iterate class methods
          Method methods[] = cls.getMethods();
          for (Method method : methods) {
              if (isOperationHidden(method)) {
                  continue;
              }
              AnnotatedMethod annotatedMethod = bd.findMethod(method.getName(), method.getParameterTypes());
              javax.ws.rs.Produces methodProduces = ReflectionUtils.getAnnotation(method, javax.ws.rs.Produces.class);
              javax.ws.rs.Consumes methodConsumes = ReflectionUtils.getAnnotation(method, javax.ws.rs.Consumes.class);
  
              if (ReflectionUtils.isOverriddenMethod(method, cls)) {
                  continue;
              }
              javax.ws.rs.Path methodPath = ReflectionUtils.getAnnotation(method, javax.ws.rs.Path.class);
  
              String operationPath = ReaderUtils.getPath(apiPath, methodPath, parentPath);
  
              // skip if path is the same as parent, e.g. for @ApplicationPath annotated application
              // extending resource config.
              if (ignoreOperationPath(operationPath, parentPath)) {
                  continue;
              }
  
              Map<String, String> regexMap = new LinkedHashMap<>();
              operationPath = PathUtils.parsePath(operationPath, regexMap);
              if (operationPath != null) {
  
                  Operation operation = parseMethod(
                                                    method,
                                                    globalParameters,
                                                    methodProduces,
                                                    classProduces,
                                                    methodConsumes,
                                                    classConsumes,
                                                    classSecurityRequirements,
                                                    classExternalDocumentation,
                                                    classTags,
                                                    classServers,
                                                    classCallbacks);
                  if (operation != null) {
                      PathItem pathItemObject;
                      if (openAPI.getPaths() != null && openAPI.getPaths().get(operationPath) != null) {
                          pathItemObject = openAPI.getPaths().get(operationPath);
                      } else {
                          pathItemObject = new PathItemImpl();
                      }
  
                      String httpMethod = ReaderUtils.extractOperationMethod(operation, method, OpenAPIExtensions.chain());
                      if (StringUtils.isBlank(httpMethod)) {
                          continue;
                      }
                      setPathItemOperation(pathItemObject, httpMethod, operation);
                      org.eclipse.microprofile.openapi.annotations.parameters.RequestBody methodRequestBody = method.getAnnotation(org.eclipse.microprofile.openapi.annotations.parameters.RequestBody.class);
                      List<Parameter> operationParameters = new ArrayList<>();
                      Annotation[][] paramAnnotations = ReflectionUtils.getParameterAnnotations(method);
                      if (annotatedMethod == null) { // annotatedMethod not null only when method with 0-2 parameters
                          Type[] genericParameterTypes = method.getGenericParameterTypes();
                          for (int i = 0; i < genericParameterTypes.length; i++) {
                              final Type type = TypeFactory.defaultInstance().constructType(genericParameterTypes[i], cls);
                              ResolvedParameter resolvedParameter = getParameters(type, Arrays.asList(paramAnnotations[i]), operation, classConsumes, methodConsumes);
                              for (Parameter p : resolvedParameter.parameters) {
                                  operationParameters.add(p);
                              }
                              if (resolvedParameter.requestBody != null) {
                                  processRequestBody(
                                                     resolvedParameter.requestBody,
                                                     operation,
                                                     methodConsumes,
                                                     classConsumes,
                                                     paramAnnotations[i],
                                                     methodRequestBody);
                              }
                          }
                      } else {
                          for (int i = 0; i < annotatedMethod.getParameterCount(); i++) {
                              AnnotatedParameter param = annotatedMethod.getParameter(i);
                              final Type type = TypeFactory.defaultInstance().constructType(param.getParameterType(), cls);
                              ResolvedParameter resolvedParameter = getParameters(type, Arrays.asList(paramAnnotations[i]), operation, classConsumes, methodConsumes);
                              for (Parameter p : resolvedParameter.parameters) {
                                  operationParameters.add(p);
                              }
                              if (resolvedParameter.requestBody != null) {
                                  processRequestBody(
                                                     resolvedParameter.requestBody,
                                                     operation,
                                                     methodConsumes,
                                                     classConsumes,
                                                     paramAnnotations[i],
                                                     methodRequestBody);
                              }
                          }
                      }
  
                      if (operation.getRequestBody() == null) {
                          processRequestBody(new ParameterImpl(), operation, methodConsumes, classConsumes, null, methodRequestBody);
                      }
  
                      if (operationParameters.size() > 0) {
                          Map<String, Parameter> params = new HashMap<>();
  
                          if (operation.getParameters() != null) {
  