/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.module.extension.internal.runtime.resolver;
import static org.mule.runtime.core.util.ClassUtils.isInstance;
import org.mule.runtime.api.exception.MuleException;
import org.mule.runtime.api.lifecycle.Initialisable;
import org.mule.runtime.api.lifecycle.InitialisationException;
import org.mule.runtime.api.metadata.DataType;
import org.mule.runtime.core.api.TransformationService;
import org.mule.runtime.core.api.transformer.Transformer;
import javax.inject.Inject;
/**
* {@link ValueResolver} wrapper implementation which wraps another {@link ValueResolver} and ensures that the output is always of
* a certain type.
* <p>
* If the returned value of the {@link this#valueResolverDelegate} is not of the desired type, it tries to locate a
* {@link Transformer} which can do the transformation from the obtained type to the expected one.
*
* @param <T>
* @since 4.0
*/
public class TypeSafeValueResolverWrapper<T> implements ValueResolver<T>, Initialisable {
private final Class<T> expectedType;
private ValueResolver valueResolverDelegate;
private Resolver<T> resolver;
@Inject
private TransformationService transformationService;
public TypeSafeValueResolverWrapper(ValueResolver valueResolverDelegate, Class<T> expectedType) {
this.expectedType = expectedType;
this.valueResolverDelegate = valueResolverDelegate;
}
@Override
public T resolve(ValueResolvingContext context) throws MuleException {
return resolver.resolve(context);
}
@Override
public boolean isDynamic() {
return valueResolverDelegate.isDynamic();
}
@Override
public void initialise() throws InitialisationException {
TypeSafeTransformer typeSafeTransformer = new TypeSafeTransformer(transformationService);
resolver = context -> {
Object resolvedValue = valueResolverDelegate.resolve(context);
return isInstance(expectedType, resolvedValue)
? (T) resolvedValue
: (T) typeSafeTransformer.transform(resolvedValue, DataType.fromObject(resolvedValue), DataType.fromType(expectedType));
};
if (!valueResolverDelegate.isDynamic()) {
resolver = new CachedResolver(resolver);
}
}
public void setTransformationService(TransformationService transformationService) {
this.transformationService = transformationService;
}
@FunctionalInterface
private interface Resolver<T> {
T resolve(ValueResolvingContext context) throws MuleException;
}
/**
* {@link Resolver} implementation which caches the value of the resolution of the given {@param resolver} so the resolution
* logic is executed once.
*/
private class CachedResolver implements Resolver<T> {
private Resolver<T> resolver;
private CachedResolver(Resolver<T> resolver) {
this.resolver = context -> {
T resolvedValue = resolver.resolve(context);
this.resolver = c -> resolvedValue;
return resolvedValue;
};
}
@Override
public T resolve(ValueResolvingContext context) throws MuleException {
return resolver.resolve(context);
}
}
}