package org.gridkit.jvmtool.event;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TypedEventWriterProxy {
public static <T extends TypedEventWriter> T createWriter(Class<T> facade, TypedEventWriterProvider provider) {
return facade.cast(Proxy.newProxyInstance(facade.getClassLoader(), new Class<?>[]{facade}, new Handler(facade, provider)));
}
public static WriterBuilder decorate(UniversalEventWriter writer) {
return new WriterBuilder(writer);
}
private static class Handler implements InvocationHandler {
private final Class<?> facade;
private final TypedEventWriterProvider provider;
private final Map<Method, UniversalEventWriter> methodMap = new HashMap<Method, UniversalEventWriter>();
public Handler(Class<?> facade, TypedEventWriterProvider provider) {
this.facade = facade;
this.provider = provider;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args); // delegate
}
else if ("close".equals(method.getName()) && method.getParameterTypes().length == 0) {
provider.close();
}
else {
UniversalEventWriter writer = writerFor(method);
writer.store((Event) args[0]);
}
return null;
}
private UniversalEventWriter writerFor(Method method) {
if (methodMap.containsKey(method)) {
return methodMap.get(method);
}
else {
if (method.getParameterTypes().length == 1
&& Event.class.isAssignableFrom(method.getParameterTypes()[0])
&& method.getParameterTypes()[0].isInterface()
&& Event.class.isAssignableFrom(method.getParameterTypes()[0])
&& method.getReturnType() == void.class) {
Class<?> type = method.getParameterTypes()[0];
@SuppressWarnings({ "unchecked", "rawtypes" })
UniversalEventWriter writer = provider.getWriterFor((Class)type);
methodMap.put(method, writer);
return writer;
}
else {
throw new IllegalArgumentException("Invalid event write method " + method.getName());
}
}
}
@Override
public String toString() {
return facade.getSimpleName() + "(" + provider + ")";
}
}
private static class GenericEventWriterProvider implements TypedEventWriterProvider {
private Class<?>[] eventTypes = new Class<?>[0];
private UniversalEventWriter[] writers = new UniversalEventWriter[0];
@Override
public <T extends Event> UniversalEventWriter getWriterFor(Class<T> eventInterface) {
int n = 0;
for(Class<?> c: eventTypes) {
if (c.isAssignableFrom(eventInterface)) {
return writers[n];
}
++n;
}
throw new IllegalArgumentException("Event type " + eventInterface.getSimpleName() + " is not supported");
}
@Override
public void close() {
for(UniversalEventWriter writer: writers) {
try {
writer.close();
}
catch(Exception e) {
}
}
}
public void add(Class<?> facade, UniversalEventWriter writer) {
int n = eventTypes.length;
eventTypes = Arrays.copyOf(eventTypes, n + 1);
writers = Arrays.copyOf(writers, n + 1);
eventTypes[n] = facade;
writers[n] = writer;
}
}
private static class TransformingWriter implements UniversalEventWriter {
private final UniversalEventWriter nested;
EventTransformer<Event, Event> transformer;
public TransformingWriter(UniversalEventWriter nested, EventTransformer<Event, Event> transformer) {
this.nested = nested;
this.transformer = transformer;
}
@Override
public void store(Event event) throws IOException {
event = transformer == null ? event : transformer.transform(event);
if (event != null) {
nested.store(event);
}
}
@Override
public void close() throws IOException {
nested.close();
}
}
private static class ChainTransformer implements EventTransformer<Event, Event> {
private final EventTransformer<Event, Event>[] tchain;
public ChainTransformer(EventTransformer<Event, Event>[] tchain) {
this.tchain = tchain;
}
@Override
public Event transform(Event source) {
Event e = source;
for(int i = tchain.length - 1; i >= 0; --i) {
e = tchain[i].transform(e);
if (e == null) {
return null;
}
}
return e;
}
}
public static class WriterBuilder {
private final List<EventTransformer<Event, Event>> tailTransformers = new ArrayList<EventTransformer<Event, Event>>();
private final TransformingWriter root;
private GenericEventWriterProvider provider;
public WriterBuilder(UniversalEventWriter writer) {
root = new TransformingWriter(writer, null);
provider = new GenericEventWriterProvider();
}
public WriterBuilder morth(EventTransformer<Event, Event> transformer) {
tailTransformers.add(transformer);
return this;
}
public <T extends Event> WriterBuilder pass(Class<T> facade) {
provider.add(facade, root);
return this;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T extends Event> WriterBuilder pass(Class<T> facade, EventTransformer<T, Event> transformer) {
provider.add(facade, new TransformingWriter(root, (EventTransformer)transformer));
return this;
}
@SuppressWarnings("unchecked")
public <T extends Event> WriterBuilder ignore(Class<T> facade) {
return pass(facade, new VoidTransformer());
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T extends TypedEventWriter> T facade(Class<T> facade) {
if (!tailTransformers.isEmpty()) {
EventTransformer[] chain = tailTransformers.toArray(new EventTransformer[0]);
ChainTransformer tchain = new ChainTransformer(chain);
root.transformer = tchain;
}
T writer = createWriter(facade, provider);
provider = null;
return writer;
}
}
@SuppressWarnings("rawtypes")
private static class VoidTransformer implements EventTransformer {
@Override
public Event transform(Event source) {
return null;
}
}
}