/**
  * Return the Jackson {@link JavaType} for the specified type and context class.
  *
  * <p>The default implementation returns {@code typeFactory.constructType(type, contextClass)},
  * but this can be overridden in subclasses, to allow for custom generic collection handling. For
  * instance:
  *
  * <pre class="code">
  * protected JavaType getJavaType(Type type) {
  *   if (type instanceof Class && List.class.isAssignableFrom((Class)type)) {
  *     return TypeFactory.collectionType(ArrayList.class, MyBean.class);
  *   } else {
  *     return super.getJavaType(type);
  *   }
  * }
  * </pre>
  *
  * @param type the generic type to return the Jackson JavaType for
  * @param contextClass a context class for the target type, for example a class in which the
  *     target type appears in a method signature (can be {@code null})
  * @return the Jackson JavaType
  */
 protected JavaType getJavaType(Type type, Class<?> contextClass) {
   TypeFactory typeFactory = this.objectMapper.getTypeFactory();
   if (type instanceof TypeVariable && contextClass != null) {
     ResolvableType resolvedType =
         resolveVariable((TypeVariable<?>) type, ResolvableType.forClass(contextClass));
     if (resolvedType != ResolvableType.NONE) {
       return typeFactory.constructType(resolvedType.resolve());
     }
   }
   return typeFactory.constructType(type);
 }
  private Flux<Object> decodeInternal(
      JsonObjectDecoder objectDecoder,
      Publisher<DataBuffer> inputStream,
      ResolvableType elementType,
      MimeType mimeType,
      Object[] hints) {

    Assert.notNull(inputStream, "'inputStream' must not be null");
    Assert.notNull(elementType, "'elementType' must not be null");

    TypeFactory typeFactory = this.mapper.getTypeFactory();
    JavaType javaType = typeFactory.constructType(elementType.getType());

    ObjectReader reader = this.mapper.readerFor(javaType);

    return objectDecoder
        .decode(inputStream, elementType, mimeType, hints)
        .map(
            dataBuffer -> {
              try {
                Object value = reader.readValue(dataBuffer.asInputStream());
                DataBufferUtils.release(dataBuffer);
                return value;
              } catch (IOException e) {
                return Flux.error(new CodecException("Error while reading the data", e));
              }
            });
  }
  /**
   * Invokes all {@link ResourceProcessor} instances registered for the type of the given value and
   * reference type.
   *
   * @param value must not be {@literal null}.
   * @param referenceType must not be {@literal null}.
   * @return
   */
  @SuppressWarnings("unchecked")
  public <T extends ResourceSupport> T invokeProcessorsFor(T value, ResolvableType referenceType) {

    Assert.notNull(value, "Value must not be null!");
    Assert.notNull(referenceType, "Reference type must not be null!");

    // For Resources implementations, process elements first
    if (ResourceProcessorHandlerMethodReturnValueHandler.RESOURCES_TYPE.isAssignableFrom(
        referenceType)) {

      Resources<?> resources = (Resources<?>) value;
      ResolvableType elementTargetType =
          ResolvableType.forClass(Resources.class, referenceType.getRawClass()).getGeneric(0);
      List<Object> result = new ArrayList<Object>(resources.getContent().size());

      for (Object element : resources) {

        ResolvableType elementType = ResolvableType.forClass(element.getClass());

        if (!getRawType(elementTargetType).equals(elementType.getRawClass())) {
          elementTargetType = elementType;
        }

        result.add(invokeProcessorsFor(element, elementTargetType));
      }

      ReflectionUtils.setField(
          ResourceProcessorHandlerMethodReturnValueHandler.CONTENT_FIELD, resources, result);
    }

    return (T) invokeProcessorsFor((Object) value, referenceType);
  }
 @Test
 public void canDecode() {
   assertTrue(
       this.decoder.canDecode(
           ResolvableType.forClass(ByteBuffer.class), MimeTypeUtils.TEXT_PLAIN));
   assertFalse(
       this.decoder.canDecode(ResolvableType.forClass(Integer.class), MimeTypeUtils.TEXT_PLAIN));
   assertTrue(
       this.decoder.canDecode(
           ResolvableType.forClass(ByteBuffer.class), MimeTypeUtils.APPLICATION_JSON));
 }
 /**
  * Resolve the method arguments to use for the specified {@link ApplicationEvent}.
  *
  * <p>These arguments will be used to invoke the method handled by this instance. Can return
  * {@code null} to indicate that no suitable arguments could be resolved and therefore the method
  * should not be invoked at all for the specified event.
  */
 protected Object[] resolveArguments(ApplicationEvent event) {
   ResolvableType declaredEventType = getResolvableType(event);
   if (declaredEventType == null) {
     return null;
   }
   if (this.method.getParameterCount() == 0) {
     return new Object[0];
   }
   if (!ApplicationEvent.class.isAssignableFrom(declaredEventType.getRawClass())
       && event instanceof PayloadApplicationEvent) {
     return new Object[] {((PayloadApplicationEvent) event).getPayload()};
   } else {
     return new Object[] {event};
   }
 }
  protected void publishEvent(Object event, ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    if (logger.isTraceEnabled()) {
      logger.trace("Publishing event in " + getDisplayName() + ": " + event);
    }

    // Decorate event as an ApplicationEvent if necessary
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
      applicationEvent = (ApplicationEvent) event;
    } else {
      applicationEvent = new PayloadApplicationEvent<Object>(this, event);
      if (eventType == null) {
        eventType =
            ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class, event.getClass());
      }
    }

    // Multicast right now if possible - or lazily once the multicaster is initialized
    if (this.earlyApplicationEvents != null) {
      this.earlyApplicationEvents.add(applicationEvent);
    } else {
      getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    }

    // Publish event via parent context as well...
    if (this.parent != null) {
      if (this.parent instanceof AbstractApplicationContext) {
        ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
      } else {
        this.parent.publishEvent(event);
      }
    }
  }
    /**
     * Returns whether the given {@link Resource} matches the given target {@link ResolvableType}.
     * We inspect the {@link Resource}'s value to determine the match.
     *
     * @param resource
     * @param target must not be {@literal null}.
     * @return whether the given {@link Resource} can be assigned to the given target {@link
     *     ResolvableType}
     */
    private static boolean isValueTypeMatch(Resource<?> resource, ResolvableType target) {

      if (resource == null || !isRawTypeAssignable(target, resource.getClass())) {
        return false;
      }

      Object content = resource.getContent();

      if (content == null) {
        return false;
      }

      ResolvableType type = findGenericType(target, Resource.class);
      return type != null
          && type.getGeneric(0).isAssignableFrom(ResolvableType.forClass(content.getClass()));
    }
 @Test
 public void canWrite() {
   assertTrue(
       this.encoder.canEncode(ResolvableType.forClass(String.class), MimeTypeUtils.TEXT_PLAIN));
   assertTrue(
       this.encoder.canEncode(
           ResolvableType.forClass(StringBuilder.class), MimeTypeUtils.TEXT_PLAIN));
   assertTrue(
       this.encoder.canEncode(
           ResolvableType.forClass(StringBuffer.class), MimeTypeUtils.TEXT_PLAIN));
   assertFalse(
       this.encoder.canEncode(ResolvableType.forClass(Integer.class), MimeTypeUtils.TEXT_PLAIN));
   assertFalse(
       this.encoder.canEncode(
           ResolvableType.forClass(String.class), MimeTypeUtils.APPLICATION_JSON));
 }
 protected ResolvableType getGenericApplicationEventType(String fieldName) {
   try {
     return ResolvableType.forField(TestEvents.class.getField(fieldName));
   } catch (NoSuchFieldException e) {
     throw new IllegalStateException("No such field on Events '" + fieldName + "'");
   }
 }
  @Override
  public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {

    ResolvableType returnType = result.getReturnType();
    MethodParameter bodyType;

    Mono<?> returnValueMono;
    Optional<Object> optionalValue = result.getReturnValue();

    Class<?> rawClass = returnType.getRawClass();
    ReactiveAdapter adapter = getAdapterRegistry().getAdapterFrom(rawClass, optionalValue);

    if (adapter != null) {
      returnValueMono = adapter.toMono(optionalValue);
      bodyType = new MethodParameter(result.getReturnTypeSource());
      bodyType.increaseNestingLevel();
      bodyType.increaseNestingLevel();
    } else {
      returnValueMono = Mono.justOrEmpty(optionalValue);
      bodyType = new MethodParameter(result.getReturnTypeSource());
      bodyType.increaseNestingLevel();
    }

    return returnValueMono.then(
        returnValue -> {
          Assert.isInstanceOf(HttpEntity.class, returnValue);
          HttpEntity<?> httpEntity = (HttpEntity<?>) returnValue;

          if (httpEntity instanceof ResponseEntity) {
            ResponseEntity<?> responseEntity = (ResponseEntity<?>) httpEntity;
            exchange.getResponse().setStatusCode(responseEntity.getStatusCode());
          }

          HttpHeaders entityHeaders = httpEntity.getHeaders();
          HttpHeaders responseHeaders = exchange.getResponse().getHeaders();

          if (!entityHeaders.isEmpty()) {
            entityHeaders
                .entrySet()
                .stream()
                .filter(entry -> !responseHeaders.containsKey(entry.getKey()))
                .forEach(entry -> responseHeaders.put(entry.getKey(), entry.getValue()));
          }

          return writeBody(httpEntity.getBody(), bodyType, exchange);
        });
  }
    /**
     * Creates a new {@link DefaultProcessorWrapper} with the given {@link ResourceProcessor}.
     *
     * @param processor must not be {@literal null}.
     */
    public DefaultProcessorWrapper(ResourceProcessor<?> processor) {

      Assert.notNull(processor);

      this.processor = processor;
      this.targetType =
          ResolvableType.forClass(ResourceProcessor.class, processor.getClass()).getGeneric(0);
    }
 @Override
 public boolean supportsEventType(ResolvableType resolvableType) {
   Class<?> type = resolvableType.getRawClass();
   if (type == null) {
     return false;
   }
   return ApplicationStartedEvent.class.isAssignableFrom(type)
       || ApplicationFailedEvent.class.isAssignableFrom(type);
 }
  @Override
  public boolean supports(HandlerResult result) {
    Class<?> returnType = result.getReturnType().getRawClass();
    if (isSupportedType(returnType)) {
      return true;
    } else {
      ReactiveAdapter adapter =
          getAdapterRegistry().getAdapterFrom(returnType, result.getReturnValue());
      if (adapter != null
          && !adapter.getDescriptor().isMultiValue()
          && !adapter.getDescriptor().isNoValue()) {

        ResolvableType genericType = result.getReturnType().getGeneric(0);
        return isSupportedType(genericType.getRawClass());
      }
    }
    return false;
  }
 @Override
 public boolean supportsReturnType(MethodParameter returnType) {
   if (ResponseBodyEmitter.class.isAssignableFrom(returnType.getParameterType())) {
     return true;
   } else if (ResponseEntity.class.isAssignableFrom(returnType.getParameterType())) {
     Class<?> bodyType = ResolvableType.forMethodParameter(returnType).getGeneric(0).resolve();
     return (bodyType != null && ResponseBodyEmitter.class.isAssignableFrom(bodyType));
   }
   return false;
 }
 private ResolvableType getResolvableType(ApplicationEvent event) {
   ResolvableType payloadType = null;
   if (event instanceof PayloadApplicationEvent) {
     PayloadApplicationEvent<?> payloadEvent = (PayloadApplicationEvent<?>) event;
     payloadType =
         payloadEvent.getResolvableType().as(PayloadApplicationEvent.class).getGeneric(0);
   }
   for (ResolvableType declaredEventType : this.declaredEventTypes) {
     if (!ApplicationEvent.class.isAssignableFrom(declaredEventType.getRawClass())
         && payloadType != null) {
       if (declaredEventType.isAssignableFrom(payloadType)) {
         return declaredEventType;
       }
     }
     if (declaredEventType.getRawClass().isAssignableFrom(event.getClass())) {
       return declaredEventType;
     }
   }
   return null;
 }
  private Object createHttpEntity(
      Object body, ResolvableType entityType, ServerWebExchange exchange) {

    ServerHttpRequest request = exchange.getRequest();
    HttpHeaders headers = request.getHeaders();
    if (RequestEntity.class == entityType.getRawClass()) {
      return new RequestEntity<>(body, headers, request.getMethod(), request.getURI());
    } else {
      return new HttpEntity<>(body, headers);
    }
  }
 @Test
 public void decode() throws InterruptedException {
   Stream<ByteBuffer> source =
       Streams.just(Buffer.wrap("{\"foo\": \"foofoo\", \"bar\": \"barbar\"}").byteBuffer());
   List<Object> results =
       Streams.wrap(decoder.decode(source, ResolvableType.forClass(Pojo.class), null))
           .toList()
           .await();
   assertEquals(1, results.size());
   assertEquals("foofoo", ((Pojo) results.get(0)).getFoo());
 }
 private List<ResolvableType> resolveDeclaredEventTypes() {
   int count = this.method.getParameterCount();
   if (count > 1) {
     throw new IllegalStateException(
         "Maximum one parameter is allowed for event listener method: " + this.method);
   }
   EventListener ann = getEventListener();
   if (ann != null && ann.classes().length > 0) {
     List<ResolvableType> types = new ArrayList<>();
     for (Class<?> eventType : ann.classes()) {
       types.add(ResolvableType.forClass(eventType));
     }
     return types;
   } else {
     if (count == 0) {
       throw new IllegalStateException(
           "Event parameter is mandatory for event listener method: " + this.method);
     }
     return Collections.singletonList(ResolvableType.forMethodParameter(this.method, 0));
   }
 }
  @Override
  public Mono<Object> resolveArgument(
      MethodParameter param, BindingContext bindingContext, ServerWebExchange exchange) {

    ResolvableType entityType = ResolvableType.forMethodParameter(param);
    MethodParameter bodyParameter = new MethodParameter(param);
    bodyParameter.increaseNestingLevel();

    return readBody(bodyParameter, false, bindingContext, exchange)
        .map(body -> createHttpEntity(body, entityType, exchange))
        .defaultIfEmpty(createHttpEntity(null, entityType, exchange));
  }
    /**
     * Returns whether the given {@link Resources} instance matches the given {@link
     * ResolvableType}. We predict this by inspecting the first element of the content of the {@link
     * Resources}.
     *
     * @param resources the {@link Resources} to inspect.
     * @param target that target {@link ResolvableType}.
     * @return
     */
    static boolean isValueTypeMatch(Resources<?> resources, ResolvableType target) {

      if (resources == null) {
        return false;
      }

      Collection<?> content = resources.getContent();

      if (content.isEmpty()) {
        return false;
      }

      ResolvableType superType = null;

      for (Class<?> resourcesType :
          Arrays.<Class<?>>asList(resources.getClass(), Resources.class)) {

        superType = ResolvableType.forClass(resourcesType, getRawType(target));

        if (superType != null) {
          break;
        }
      }

      if (superType == null) {
        return false;
      }

      Object element = content.iterator().next();
      ResolvableType resourceType = superType.getGeneric(0);

      if (element instanceof Resource) {
        return ResourceProcessorWrapper.isValueTypeMatch((Resource<?>) element, resourceType);
      } else if (element instanceof EmbeddedWrapper) {
        return isRawTypeAssignable(resourceType, ((EmbeddedWrapper) element).getRelTargetType());
      }

      return false;
    }
 @SuppressWarnings("unchecked")
 protected H createHealthIndicator(S source) {
   Class<?>[] generics =
       ResolvableType.forClass(CompositeHealthIndicatorConfiguration.class, getClass())
           .resolveGenerics();
   Class<H> indicatorClass = (Class<H>) generics[0];
   Class<S> sourceClass = (Class<S>) generics[1];
   try {
     return indicatorClass.getConstructor(sourceClass).newInstance(source);
   } catch (Exception ex) {
     throw new IllegalStateException(
         "Unable to create indicator " + indicatorClass + " for source " + sourceClass, ex);
   }
 }
  /**
   * Creates a new {@link ResourceProcessorInvoker} to consider the given {@link ResourceProcessor}
   * to post-process the controller methods return value to before invoking the delegate.
   *
   * @param processors the {@link ResourceProcessor}s to be considered, must not be {@literal null}.
   */
  public ResourceProcessorInvoker(Collection<ResourceProcessor<?>> processors) {

    Assert.notNull(processors, "ResourceProcessors must not be null!");

    this.processors = new ArrayList<ProcessorWrapper>();

    for (ResourceProcessor<?> processor : processors) {

      ResolvableType processorType =
          ResolvableType.forClass(ResourceProcessor.class, processor.getClass());
      Class<?> rawType = processorType.getGeneric(0).resolve();

      if (Resource.class.isAssignableFrom(rawType)) {
        this.processors.add(new ResourceProcessorWrapper(processor));
      } else if (Resources.class.isAssignableFrom(rawType)) {
        this.processors.add(new ResourcesProcessorWrapper(processor));
      } else {
        this.processors.add(new DefaultProcessorWrapper(processor));
      }
    }

    Collections.sort(this.processors, AnnotationAwareOrderComparator.INSTANCE);
  }
    private static ResolvableType findGenericType(ResolvableType source, Class<?> type) {

      Class<?> rawType = getRawType(source);

      if (Object.class.equals(rawType)) {
        return null;
      }

      if (rawType.equals(type)) {
        return source;
      }

      return findGenericType(source.getSuperType(), type);
    }
  @Test
  public void decode() {
    DataBuffer fooBuffer = stringBuffer("foo");
    DataBuffer barBuffer = stringBuffer("bar");
    Flux<DataBuffer> source = Flux.just(fooBuffer, barBuffer);
    Flux<ByteBuffer> output =
        this.decoder.decode(
            source,
            ResolvableType.forClassWithGenerics(Publisher.class, ByteBuffer.class),
            null,
            Collections.emptyMap());

    StepVerifier.create(output)
        .expectNext(ByteBuffer.wrap("foo".getBytes()), ByteBuffer.wrap("bar".getBytes()))
        .expectComplete()
        .verify();
  }
 private ResolvableType resolveVariable(TypeVariable<?> typeVariable, ResolvableType contextType) {
   ResolvableType resolvedType;
   if (contextType.hasGenerics()) {
     resolvedType = ResolvableType.forType(typeVariable, contextType);
     if (resolvedType.resolve() != null) {
       return resolvedType;
     }
   }
   resolvedType = resolveVariable(typeVariable, contextType.getSuperType());
   if (resolvedType.resolve() != null) {
     return resolvedType;
   }
   for (ResolvableType ifc : contextType.getInterfaces()) {
     resolvedType = resolveVariable(typeVariable, ifc);
     if (resolvedType.resolve() != null) {
       return resolvedType;
     }
   }
   return ResolvableType.NONE;
 }
 @Override
 public boolean supportsEventType(ResolvableType eventType) {
   for (ResolvableType declaredEventType : this.declaredEventTypes) {
     if (declaredEventType.isAssignableFrom(eventType)) {
       return true;
     } else if (PayloadApplicationEvent.class.isAssignableFrom(eventType.getRawClass())) {
       ResolvableType payloadType = eventType.as(PayloadApplicationEvent.class).getGeneric();
       if (declaredEventType.isAssignableFrom(payloadType)) {
         return true;
       }
     }
   }
   return eventType.hasUnresolvableGenerics();
 }
 public SmartGenericTestEvent(Object source, T payload) {
   super(source, payload);
   this.resolvableType = ResolvableType.forClassWithGenerics(getClass(), payload.getClass());
 }
  /**
   * Invokes all {@link ResourceProcessor} instances registered for the type of the given value.
   *
   * @param value must not be {@literal null}.
   * @return
   */
  public <T extends ResourceSupport> T invokeProcessorsFor(T value) {

    Assert.notNull(value, "Value must not be null!");

    return invokeProcessorsFor(value, ResolvableType.forClass(value.getClass()));
  }
  private static Class<?> getRawType(ResolvableType type) {

    Class<?> rawType = type.getRawClass();
    return rawType == null ? Object.class : rawType;
  }
/**
 * Tests for {@link MockDefinition}.
 *
 * @author Phillip Webb
 */
public class MockDefinitionTests {

  private static final ResolvableType EXAMPLE_SERVICE_TYPE =
      ResolvableType.forClass(ExampleService.class);

  @Rule public ExpectedException thrown = ExpectedException.none();

  @Test
  public void classToMockMustNotBeNull() throws Exception {
    this.thrown.expect(IllegalArgumentException.class);
    this.thrown.expectMessage("TypeToMock must not be null");
    new MockDefinition(null, null, null, null, false, null, null);
  }

  @Test
  public void createWithDefaults() throws Exception {
    MockDefinition definition =
        new MockDefinition(null, EXAMPLE_SERVICE_TYPE, null, null, false, null, null);
    assertThat(definition.getName()).isNull();
    assertThat(definition.getTypeToMock()).isEqualTo(EXAMPLE_SERVICE_TYPE);
    assertThat(definition.getExtraInterfaces()).isEmpty();
    assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_DEFAULTS);
    assertThat(definition.isSerializable()).isFalse();
    assertThat(definition.getReset()).isEqualTo(MockReset.AFTER);
    assertThat(definition.getQualifier()).isNull();
  }

  @Test
  public void createExplicit() throws Exception {
    QualifierDefinition qualifier = mock(QualifierDefinition.class);
    MockDefinition definition =
        new MockDefinition(
            "name",
            EXAMPLE_SERVICE_TYPE,
            new Class<?>[] {ExampleExtraInterface.class},
            Answers.RETURNS_SMART_NULLS,
            true,
            MockReset.BEFORE,
            qualifier);
    assertThat(definition.getName()).isEqualTo("name");
    assertThat(definition.getTypeToMock()).isEqualTo(EXAMPLE_SERVICE_TYPE);
    assertThat(definition.getExtraInterfaces()).containsExactly(ExampleExtraInterface.class);
    assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_SMART_NULLS);
    assertThat(definition.isSerializable()).isTrue();
    assertThat(definition.getReset()).isEqualTo(MockReset.BEFORE);
    assertThat(definition.isProxyTargetAware()).isFalse();
    assertThat(definition.getQualifier()).isEqualTo(qualifier);
  }

  @Test
  public void createMock() throws Exception {
    MockDefinition definition =
        new MockDefinition(
            "name",
            EXAMPLE_SERVICE_TYPE,
            new Class<?>[] {ExampleExtraInterface.class},
            Answers.RETURNS_SMART_NULLS,
            true,
            MockReset.BEFORE,
            null);
    ExampleService mock = definition.createMock();
    MockCreationSettings<?> settings = SpringBootMockUtil.getMockSettings(mock);
    assertThat(mock).isInstanceOf(ExampleService.class);
    assertThat(mock).isInstanceOf(ExampleExtraInterface.class);
    assertThat(settings.getMockName().toString()).isEqualTo("name");
    assertThat(settings.getDefaultAnswer()).isEqualTo(Answers.RETURNS_SMART_NULLS.get());
    assertThat(settings.isSerializable()).isTrue();
    assertThat(MockReset.get(mock)).isEqualTo(MockReset.BEFORE);
  }
}