package org.embulk.config; import java.lang.reflect.Proxy; import java.lang.reflect.Method; import java.lang.reflect.InvocationHandler; import java.util.Set; import java.util.Map; import java.util.HashMap; import com.google.common.collect.ImmutableMap; class TaskInvocationHandler implements InvocationHandler { private final ModelManager model; private final Class<?> iface; private final Map<String, Object> objects; private final Set<String> injectedFields; public TaskInvocationHandler(ModelManager model, Class<?> iface, Map<String, Object> objects, Set<String> injectedFields) { this.model = model; this.iface = iface; this.objects = objects; this.injectedFields = injectedFields; } /** * fieldName = Method of the getter */ public static Map<String, Method> fieldGetters(Class<?> iface) { ImmutableMap.Builder<String, Method> builder = ImmutableMap.builder(); for (Method method : iface.getMethods()) { String methodName = method.getName(); String fieldName = getterFieldNameOrNull(methodName); if (fieldName != null && hasExpectedArgumentLength(method, 0)) { builder.put(fieldName, method); } } return builder.build(); } // visible for ModelManager.AccessorSerializer Map<String, Object> getObjects() { return objects; } // visible for ModelManager.AccessorSerializer Set<String> getInjectedFields() { return injectedFields; } protected Object invokeGetter(Method method, String fieldName) { return objects.get(fieldName); } protected void invokeSetter(Method method, String fieldName, Object value) { if (value == null) { objects.remove(fieldName); } else { objects.put(fieldName, value); } } private Map<String, Object> getSerializableFields() { Map<String, Object> data = new HashMap<String, Object>(objects); for (String injected : injectedFields) { data.remove(injected); } return data; } protected TaskSource invokeDump() { return new DataSourceImpl(model, model.writeObjectAsObjectNode(getSerializableFields())); } protected String invokeToString() { StringBuilder sb = new StringBuilder(); sb.append(iface.getName()); sb.append(getSerializableFields()); return sb.toString(); } protected int invokeHashCode() { return objects.hashCode(); } protected boolean invokeEquals(Object other) { return (other instanceof TaskInvocationHandler) && objects.equals(((TaskInvocationHandler) other).objects); } public Object invoke(Object proxy, Method method, Object[] args) { String methodName = method.getName(); switch(methodName) { case "validate": checkArgumentLength(method, 0, methodName); model.validate(proxy); return proxy; case "dump": checkArgumentLength(method, 0, methodName); return invokeDump(); case "toString": checkArgumentLength(method, 0, methodName); return invokeToString(); case "hashCode": checkArgumentLength(method, 0, methodName); return invokeHashCode(); case "equals": checkArgumentLength(method, 1, methodName); if (args[0] instanceof Proxy) { Object otherHandler = Proxy.getInvocationHandler(args[0]); return invokeEquals(otherHandler); } return false; default: { String fieldName; fieldName = getterFieldNameOrNull(methodName); if (fieldName != null) { checkArgumentLength(method, 0, methodName); return invokeGetter(method, fieldName); } fieldName = setterFieldNameOrNull(methodName); if (fieldName != null) { checkArgumentLength(method, 1, methodName); invokeSetter(method, fieldName, args[0]); return this; } } } throw new IllegalArgumentException(String.format("Undefined method '%s'", methodName)); } private static String getterFieldNameOrNull(String methodName) { if (methodName.startsWith("get")) { return methodName.substring(3); } return null; } private static String setterFieldNameOrNull(String methodName) { if (methodName.startsWith("set")) { return methodName.substring(3); } return null; } protected static boolean hasExpectedArgumentLength(Method method, int expected) { return method.getParameterTypes().length == expected; } protected static void checkArgumentLength(Method method, int expected, String methodName) { if (!hasExpectedArgumentLength(method, expected)) { throw new IllegalArgumentException( String.format("Method '%s' expected %d argument but got %d arguments", methodName, expected, method.getParameterTypes().length)); } } }