/** * 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); }
/** * 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)); }
@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)); }
/** * 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); }
@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()); }
@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); } }
/** * 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())); }
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)); } }
/** * 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; }
/** * 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); }
/** * 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())); }
/** * 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); } }
@Test public void listenerWithSubTypeSeveralGenerics() { Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleString", String.class); supportsEventType(true, method, ResolvableType.forClass(PayloadTestEvent.class)); }
@Test public void listenerWithPayloadTypeErasure() { // Always accept such event when the type is unknown Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleString", String.class); supportsEventType(true, method, ResolvableType.forClass(PayloadApplicationEvent.class)); }
/** * Return a {@code BodyExtractor} that reads into a Reactor {@link Flux}. * * @param elementClass the class of element in the {@code Flux} * @param <T> the element type * @return a {@code BodyExtractor} that reads a mono */ public static <T> BodyExtractor<Flux<T>> toFlux(Class<? extends T> elementClass) { Assert.notNull(elementClass, "'elementClass' must not be null"); return toFlux(ResolvableType.forClass(elementClass)); }