package com.github.dreamhead.moco.parser.model;
import com.github.dreamhead.moco.CookieAttribute;
import com.github.dreamhead.moco.Moco;
import com.github.dreamhead.moco.RequestExtractor;
import com.github.dreamhead.moco.ResponseHandler;
import com.github.dreamhead.moco.parser.ResponseHandlerFactory;
import com.github.dreamhead.moco.resource.Resource;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.Map;
import static com.github.dreamhead.moco.Moco.attachment;
import static com.github.dreamhead.moco.Moco.status;
import static com.github.dreamhead.moco.Moco.template;
import static com.github.dreamhead.moco.Moco.text;
import static com.github.dreamhead.moco.Moco.toJson;
import static com.github.dreamhead.moco.Moco.var;
import static com.github.dreamhead.moco.Moco.version;
import static com.github.dreamhead.moco.Moco.with;
import static com.github.dreamhead.moco.handler.AndResponseHandler.and;
import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.ImmutableMap.copyOf;
import static com.google.common.collect.ImmutableSet.of;
import static java.lang.String.format;
public class DynamicResponseHandlerFactory extends Dynamics implements ResponseHandlerFactory {
private static final ImmutableSet<String> RESOURCES = of("text", "file", "pathResource", "version");
private static final ImmutableMap<String, String> COMPOSITES = ImmutableMap.<String, String>builder()
.put("headers", "header")
.put("cookies", "cookie")
.build();
@Override
public ResponseHandler createResponseHandler(final ResponseSetting responseSetting) {
FluentIterable<ResponseHandler> handlers = from(getFields(responseSetting.getClass()))
.filter(isValidField(responseSetting))
.transform(fieldToResponseHandler(responseSetting));
return getResponseHandler(handlers);
}
private ResponseHandler getResponseHandler(final FluentIterable<ResponseHandler> handlers) {
if (handlers.size() == 1) {
return handlers.get(0);
}
return and(handlers);
}
private boolean isResource(final String name) {
return RESOURCES.contains(name);
}
private Function<Field, ResponseHandler> fieldToResponseHandler(final ResponseSetting response) {
return new Function<Field, ResponseHandler>() {
@Override
public ResponseHandler apply(final Field field) {
try {
Object value = field.get(response);
return createResponseHandler(field.getName(), value);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
};
}
private ResponseHandler createResponseHandler(final String name, final Object value) {
if ("json".equalsIgnoreCase(name)) {
return toJson(value);
}
if (isResource(name) && TextContainer.class.isInstance(value)) {
TextContainer container = TextContainer.class.cast(value);
return with(resourceFrom(name, container));
}
if (Map.class.isInstance(value)) {
return createCompositeHandler(name, castToMap(value));
}
if ("status".equalsIgnoreCase(name)) {
return status(Integer.parseInt(value.toString()));
}
if ("latency".equalsIgnoreCase(name)) {
LatencyContainer container = LatencyContainer.class.cast(value);
return with(container.asProcedure());
}
if (ProxyContainer.class.isInstance(value)) {
return ((ProxyContainer) value).asResponseHandler();
}
if ("attachment".equalsIgnoreCase(name)) {
AttachmentSetting attachment = (AttachmentSetting) value;
return attachment(attachment.getFilename(), resourceFrom(attachment));
}
throw new IllegalArgumentException(format("unknown field [%s]", name));
}
private Field getField(final Class<?> clazz, final String name) throws NoSuchFieldException {
try {
return clazz.getDeclaredField(name);
} catch (NoSuchFieldException e) {
Class<?> superclass = clazz.getSuperclass();
if (superclass != null) {
return getField(superclass, name);
}
throw e;
}
}
private Resource resourceFrom(final BaseResourceSetting resourceSetting) {
for (String resource : RESOURCES) {
try {
Field field = getField(resourceSetting.getClass(), resource);
return resourceFrom(resource, (TextContainer) field.get(resourceSetting));
} catch (Exception ignored) {
}
}
throw new IllegalArgumentException("resourceSetting is expected");
}
private ResponseHandler createCompositeHandler(final String name, final Map<String, Container> map) {
FluentIterable<ResponseHandler> handlers = from(map.entrySet()).transform(toTargetHandler(name));
return getResponseHandler(handlers);
}
private Function<Map.Entry<String, Container>, ResponseHandler> toTargetHandler(final String name) {
return new Function<Map.Entry<String, Container>, ResponseHandler>() {
@Override
public ResponseHandler apply(final Map.Entry<String, Container> pair) {
String result = COMPOSITES.get(name);
if (result == null) {
throw new IllegalArgumentException("unknown composite handler name [" + name + "]");
}
return createResponseHandler(pair, result);
}
};
}
private Resource getResource(final TextContainer container) {
if (container.isForTemplate()) {
return template(container.getText());
}
return text(container.getText());
}
private Resource getResource(final CookieContainer container) {
if (container.isForTemplate()) {
return template(container.getTemplate());
}
return text(container.getValue());
}
private ResponseHandler createResponseHandler(final Map.Entry<String, Container> pair,
final String targetMethodName) {
Container container = pair.getValue();
String key = pair.getKey();
if (container instanceof TextContainer) {
return createResponseHandler(targetMethodName, key, (TextContainer) container);
}
if (container instanceof CookieContainer) {
return createResponseHandler(targetMethodName, key, (CookieContainer) container);
}
throw new IllegalArgumentException();
}
private ResponseHandler createResponseHandler(final String target, final String key,
final TextContainer textContainer) {
try {
Method method = Moco.class.getMethod(target, String.class, Resource.class);
return (ResponseHandler) method.invoke(null, key, getResource(textContainer));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private ResponseHandler createResponseHandler(final String target, final String key,
final CookieContainer cookieContainer) {
try {
Method method = Moco.class.getMethod(target, String.class, Resource.class, CookieAttribute[].class);
return (ResponseHandler) method.invoke(null, key,
getResource(cookieContainer), cookieContainer.getOptions());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
private Map<String, Container> castToMap(final Object value) {
return Map.class.cast(value);
}
private Resource resourceFrom(final String name, final TextContainer container) {
if (container.isFileContainer()) {
return fileResource(name, FileContainer.class.cast(container));
}
return textResource(name, container);
}
private Resource textResource(final String name, final TextContainer container) {
if (container.isRawText()) {
return invokeTarget(name, container.getText(), Resource.class);
}
if (container.isForTemplate()) {
if ("version".equalsIgnoreCase(name)) {
return version(container.asTemplateResource());
}
return container.asTemplateResource(name);
}
throw new IllegalArgumentException(format("unknown text container:[%s]", container));
}
private Resource fileResource(final String name, final FileContainer fileContainer) {
if (fileContainer.isForTemplate()) {
if ("version".equalsIgnoreCase(name)) {
return version(fileContainer.asTemplateResource());
}
return fileContainer.asTemplateResource(name);
}
TextContainer filename = fileContainer.getName();
if (filename.isRawText()) {
return asResource(name, fileContainer);
}
if (filename.isForTemplate()) {
Optional<Charset> charset = fileContainer.getCharset();
Resource resource = filename.asTemplateResource();
return asResource(name, resource, charset);
}
throw new IllegalArgumentException(format("unknown file container:[%s]", fileContainer));
}
private Resource asResource(final String name, final Resource resource, final Optional<Charset> charset) {
if (charset.isPresent()) {
return invokeTarget(name, resource, charset.get(),
Resource.class, Resource.class, Charset.class);
}
return invokeTarget(name, resource, Resource.class, Resource.class);
}
private Resource asResource(final String name, final FileContainer fileContainer) {
Optional<Charset> charset = fileContainer.getCharset();
String text = fileContainer.getName().getText();
return asResource(name, text(text), charset);
}
public static ImmutableMap<String, RequestExtractor<?>> toVariables(final Map<String, TextContainer> props) {
return copyOf(Maps.transformEntries(props, toVariable()));
}
private static Maps.EntryTransformer<String, TextContainer, RequestExtractor<?>> toVariable() {
return new Maps.EntryTransformer<String, TextContainer, RequestExtractor<?>>() {
@Override
public RequestExtractor<?> transformEntry(final String key, final TextContainer value) {
if (value.isRawText()) {
return var(value.getText());
}
return createRequestExtractor(getExtractorMethod(value.getOperation()), value.getText());
}
};
}
}