1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.lang3.event;
18
19 import static org.apache.commons.lang3.LangAssertions.assertIllegalArgumentException;
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertFalse;
22 import static org.junit.jupiter.api.Assertions.assertNotNull;
23 import static org.junit.jupiter.api.Assertions.assertThrows;
24 import static org.junit.jupiter.api.Assertions.assertTrue;
25
26 import java.beans.PropertyChangeEvent;
27 import java.beans.PropertyChangeListener;
28 import java.beans.VetoableChangeListener;
29 import java.lang.reflect.Constructor;
30 import java.lang.reflect.InvocationHandler;
31 import java.lang.reflect.Method;
32 import java.lang.reflect.Modifier;
33 import java.lang.reflect.Proxy;
34 import java.util.Date;
35 import java.util.Map;
36 import java.util.TreeMap;
37
38 import javax.naming.event.ObjectChangeListener;
39
40 import org.apache.commons.lang3.AbstractLangTest;
41 import org.junit.jupiter.api.Test;
42
43
44
45 class EventUtilsTest extends AbstractLangTest {
46 public static class EventCounter {
47 private int count;
48
49 public void eventOccurred() {
50 count++;
51 }
52
53 public int getCount() {
54 return count;
55 }
56 }
57
58 public static class EventCounterWithEvent {
59 private int count;
60
61 public void eventOccurred(final PropertyChangeEvent e) {
62 count++;
63 }
64
65 public int getCount() {
66 return count;
67 }
68 }
69
70 private static final class EventCountingInvocationHandler implements InvocationHandler {
71
72 private final Map<String, Integer> eventCounts = new TreeMap<>();
73
74 public <L> L createListener(final Class<L> listenerType) {
75 return listenerType.cast(Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] { listenerType }, this));
76 }
77
78 public int getEventCount(final String eventName) {
79 final Integer count = eventCounts.get(eventName);
80 return count == null ? 0 : count.intValue();
81 }
82
83 @Override
84 public Object invoke(final Object proxy, final Method method, final Object[] args) {
85 final Integer count = eventCounts.get(method.getName());
86 if (count == null) {
87 eventCounts.put(method.getName(), Integer.valueOf(1));
88 } else {
89 eventCounts.put(method.getName(), Integer.valueOf(count.intValue() + 1));
90 }
91 return null;
92 }
93 }
94
95 public static class ExceptionEventSource {
96 public void addPropertyChangeListener(final PropertyChangeListener listener) {
97 throw new RuntimeException();
98 }
99 }
100
101 public static class ListenerCapturingSource {
102 PropertyChangeListener listener;
103
104 public void addPropertyChangeListener(final PropertyChangeListener listener) {
105 this.listener = listener;
106 }
107 }
108
109 public interface MultipleEventListener {
110 void event1(PropertyChangeEvent e);
111
112 void event2(PropertyChangeEvent e);
113 }
114
115 public static class MultipleEventSource {
116 private final EventListenerSupport<MultipleEventListener> listeners = EventListenerSupport.create(MultipleEventListener.class);
117
118 public void addMultipleEventListener(final MultipleEventListener listener) {
119 listeners.addListener(listener);
120 }
121 }
122
123 public static class PropertyChangeSource {
124 private final EventListenerSupport<PropertyChangeListener> listeners = EventListenerSupport.create(PropertyChangeListener.class);
125
126 private String property;
127
128 public void addPropertyChangeListener(final PropertyChangeListener listener) {
129 listeners.addListener(listener);
130 }
131
132 protected void addVetoableChangeListener(final VetoableChangeListener listener) {
133
134 }
135
136 public void removePropertyChangeListener(final PropertyChangeListener listener) {
137 listeners.removeListener(listener);
138 }
139
140 public void setProperty(final String property) {
141 final String oldValue = this.property;
142 this.property = property;
143 listeners.fire().propertyChange(new PropertyChangeEvent(this, "property", oldValue, property));
144 }
145 }
146
147 @Test
148 void testAddEventListener() {
149 final PropertyChangeSource src = new PropertyChangeSource();
150 final EventCountingInvocationHandler handler = new EventCountingInvocationHandler();
151 final PropertyChangeListener listener = handler.createListener(PropertyChangeListener.class);
152 assertEquals(0, handler.getEventCount("propertyChange"));
153 EventUtils.addEventListener(src, PropertyChangeListener.class, listener);
154 assertEquals(0, handler.getEventCount("propertyChange"));
155 src.setProperty("newValue");
156 assertEquals(1, handler.getEventCount("propertyChange"));
157 }
158
159 @Test
160 void testAddEventListenerThrowsException() {
161 final ExceptionEventSource src = new ExceptionEventSource();
162 assertThrows(RuntimeException.class, () -> EventUtils.addEventListener(src, PropertyChangeListener.class, e -> {
163
164 }));
165 }
166
167 @Test
168 void testAddEventListenerWithNoAddMethod() {
169 final PropertyChangeSource src = new PropertyChangeSource();
170 final EventCountingInvocationHandler handler = new EventCountingInvocationHandler();
171 final ObjectChangeListener listener = handler.createListener(ObjectChangeListener.class);
172 final IllegalArgumentException e = assertIllegalArgumentException(() -> EventUtils.addEventListener(src, ObjectChangeListener.class, listener));
173 assertEquals("Unable to add listener for class " + src.getClass().getName() + " and public add" + ObjectChangeListener.class.getSimpleName()
174 + " method which takes a parameter of type " + ObjectChangeListener.class.getName() + ".", e.getMessage());
175 }
176
177 @Test
178 void testAddEventListenerWithPrivateAddMethod() {
179 final PropertyChangeSource src = new PropertyChangeSource();
180 final EventCountingInvocationHandler handler = new EventCountingInvocationHandler();
181 final VetoableChangeListener listener = handler.createListener(VetoableChangeListener.class);
182 final IllegalArgumentException e = assertIllegalArgumentException(() -> EventUtils.addEventListener(src, VetoableChangeListener.class, listener));
183 assertEquals("Unable to add listener for class " + src.getClass().getName() + " and public add" + VetoableChangeListener.class.getSimpleName()
184 + " method which takes a parameter of type " + VetoableChangeListener.class.getName() + ".", e.getMessage());
185 }
186
187 @Test
188 void testBindEventsToMethod() {
189 final PropertyChangeSource src = new PropertyChangeSource();
190 final EventCounter counter = new EventCounter();
191 EventUtils.bindEventsToMethod(counter, "eventOccurred", src, PropertyChangeListener.class);
192 assertEquals(0, counter.getCount());
193 src.setProperty("newValue");
194 assertEquals(1, counter.getCount());
195 }
196
197 @Test
198 void testBindEventsToMethodObjectMethodsNotDispatched() {
199
200
201 final ListenerCapturingSource src = new ListenerCapturingSource();
202 final EventCounter counter = new EventCounter();
203 EventUtils.bindEventsToMethod(counter, "eventOccurred", src, PropertyChangeListener.class);
204 final PropertyChangeListener listener = src.listener;
205 assertEquals(listener.hashCode(), listener.hashCode());
206 assertTrue(listener.equals(listener));
207 assertFalse(listener.equals(counter));
208 assertNotNull(listener.toString());
209 assertEquals(0, counter.getCount(), "Object methods must not invoke the bound target method");
210
211
212
213 final ListenerCapturingSource src2 = new ListenerCapturingSource();
214 final EventCounter counter2 = new EventCounter();
215 EventUtils.bindEventsToMethod(counter2, "eventOccurred", src2, PropertyChangeListener.class, "propertyChange");
216 final PropertyChangeListener listener2 = src2.listener;
217 assertEquals(listener2.hashCode(), listener2.hashCode());
218 assertTrue(listener2.equals(listener2));
219 assertNotNull(listener2.toString());
220 assertEquals(0, counter2.getCount());
221 }
222
223 @Test
224 void testBindEventsToMethodWithEvent() {
225 final PropertyChangeSource src = new PropertyChangeSource();
226 final EventCounterWithEvent counter = new EventCounterWithEvent();
227 EventUtils.bindEventsToMethod(counter, "eventOccurred", src, PropertyChangeListener.class);
228 assertEquals(0, counter.getCount());
229 src.setProperty("newValue");
230 assertEquals(1, counter.getCount());
231 }
232
233 @Test
234 void testBindFilteredEventsToMethod() {
235 final MultipleEventSource src = new MultipleEventSource();
236 final EventCounter counter = new EventCounter();
237 EventUtils.bindEventsToMethod(counter, "eventOccurred", src, MultipleEventListener.class, "event1");
238 assertEquals(0, counter.getCount());
239 src.listeners.fire().event1(new PropertyChangeEvent(new Date(), "Day", Integer.valueOf(0), Integer.valueOf(1)));
240 assertEquals(1, counter.getCount());
241 src.listeners.fire().event2(new PropertyChangeEvent(new Date(), "Day", Integer.valueOf(1), Integer.valueOf(2)));
242 assertEquals(1, counter.getCount());
243 }
244
245 @Test
246 void testConstructor() {
247 assertNotNull(new EventUtils());
248 final Constructor<?>[] cons = EventUtils.class.getDeclaredConstructors();
249 assertEquals(1, cons.length);
250 assertTrue(Modifier.isPublic(cons[0].getModifiers()));
251 assertTrue(Modifier.isPublic(EventUtils.class.getModifiers()));
252 assertFalse(Modifier.isFinal(EventUtils.class.getModifiers()));
253 }
254 }