1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.doxia.sink.impl;
20
21 import java.lang.reflect.InvocationHandler;
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.lang.reflect.Proxy;
25 import java.util.LinkedList;
26 import java.util.Queue;
27
28 import org.apache.maven.doxia.sink.Sink;
29
30
31
32
33 public class BufferingSinkProxyFactory implements SinkWrapperFactory {
34
35 private static final class MethodWithArguments {
36 private final Method method;
37 private final Object[] args;
38
39 MethodWithArguments(Method method, Object[] args) {
40 super();
41 this.method = method;
42 this.args = args;
43 }
44
45 void invoke(Object object) {
46 try {
47 method.invoke(object, args);
48 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
49 throw new IllegalStateException("Could not call buffered method " + method, e);
50 }
51 }
52 }
53
54 public interface BufferingSink extends Sink {
55
56 Sink getBufferedSink();
57 }
58
59 private static final class BufferingSinkProxy implements InvocationHandler {
60 private final Queue<MethodWithArguments> bufferedInvocations;
61 private final Sink delegate;
62 private static final Method FLUSH_METHOD;
63 private static final Method GET_BUFFERED_SINK_METHOD;
64 private static final Method GET_DOCUMENT_LOCATOR_METHOD;
65
66 static {
67 try {
68 FLUSH_METHOD = Sink.class.getMethod("flush");
69 GET_BUFFERED_SINK_METHOD = BufferingSink.class.getMethod("getBufferedSink");
70 GET_DOCUMENT_LOCATOR_METHOD = BufferingSink.class.getMethod("getDocumentLocator");
71 } catch (NoSuchMethodException | SecurityException e) {
72 throw new IllegalStateException("Could not find flush method in Sink!", e);
73 }
74 }
75
76 BufferingSinkProxy(Sink delegate) {
77 bufferedInvocations = new LinkedList<>();
78 this.delegate = delegate;
79 }
80
81 @Override
82 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
83 if (method.equals(FLUSH_METHOD)) {
84 bufferedInvocations.forEach(i -> i.invoke(delegate));
85 bufferedInvocations.clear();
86 } else if (method.equals(GET_BUFFERED_SINK_METHOD)) {
87 return delegate;
88 } else if (method.equals(GET_DOCUMENT_LOCATOR_METHOD)) {
89 return delegate.getDocumentLocator();
90 } else {
91 bufferedInvocations.add(new MethodWithArguments(method, args));
92 }
93 if (method.getReturnType() != Void.TYPE) {
94 throw new IllegalStateException(
95 "BufferingSinkProxy only works for methods returning void, but given method " + method
96 + " requires another return type");
97 }
98 return null;
99 }
100 }
101
102 @Override
103 public Sink createWrapper(Sink delegate) {
104 BufferingSinkProxy proxy = new BufferingSinkProxy(delegate);
105 return (Sink) Proxy.newProxyInstance(
106 delegate.getClass().getClassLoader(), new Class<?>[] {BufferingSink.class}, proxy);
107 }
108
109 public static BufferingSink castAsBufferingSink(Sink sink) {
110 if (sink instanceof BufferingSink) {
111 return (BufferingSink) sink;
112 } else {
113 throw new IllegalArgumentException("The given sink is not a BufferingSink but a " + sink.getClass());
114 }
115 }
116
117 @Override
118 public int getPriority() {
119 return 0;
120 }
121 }