Updated comments.
[aprog.git] / Aprog / test / net / sourceforge / aprog / events / ObservableTest.java
blob1b6361b50b551d340f7dab27b90f4a117ba9a66e
1 /*
2 * The MIT License
3 *
4 * Copyright 2010 Codist Monk.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
25 package net.sourceforge.aprog.events;
27 import static org.junit.Assert.*;
29 import java.lang.reflect.Array;
30 import java.lang.reflect.InvocationHandler;
31 import java.lang.reflect.Method;
32 import java.lang.reflect.Proxy;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.List;
36 import net.sourceforge.aprog.events.Observable.Event;
37 import net.sourceforge.aprog.tools.Tools;
38 import org.junit.Test;
40 /**
42 * @author codistmonk (creation 2010-06-23)
44 public final class ObservableTest {
46 @Test
47 public final <R extends EventRecorder & DummyObservable.Listener> void testFireEvent() {
48 final DummyObservable observable = new DummyObservable();
49 @SuppressWarnings("unchecked")
50 final R recorder1 = (R) newEventRecorder(DummyObservable.Listener.class);
51 @SuppressWarnings("unchecked")
52 final R recorder2 = (R) newEventRecorder(DummyObservable.Listener.class);
54 observable.addListener(recorder1);
55 observable.addListener(recorder2);
56 observable.fireNewEvent();
57 observable.removeListener(recorder2);
58 observable.fireNewEvent();
60 assertTrue(recorder1.getEvent(0) instanceof DummyObservable.EventFiredEvent);
61 assertTrue(recorder1.getEvent(1) instanceof DummyObservable.EventFiredEvent);
62 assertSame(recorder1.getEvent(0), recorder2.getEvent(0));
63 assertEquals(2, recorder1.getEvents().size());
64 assertEquals(1, recorder2.getEvents().size());
67 /**
69 * @param <R> the (multi)listener recorder proxy type
70 * @param listenerTypes
71 * <br>Not null
72 * @return
73 * <br>Not null
74 * <br>New
76 @SuppressWarnings("unchecked")
77 public static final <R extends EventRecorder> R newEventRecorder(
78 final Class<?>... listenerTypes) {
79 return (R) Proxy.newProxyInstance(
80 Tools.getCallerClass().getClassLoader(),
81 add(listenerTypes, EventRecorder.class),
82 new RecorderInvocationHandler());
85 /**
87 * @param <T>
88 * @param array
89 * <br>Not null
90 * @param moreElements
91 * <br>Not null
92 * @return
93 * <br>Not null
94 * <br>New
96 private static final <T> T[] add(final T[] array, final T... moreElements) {
97 @SuppressWarnings("unchecked")
98 final T[] result = (T[]) Array.newInstance(
99 array.getClass().getComponentType(), array.length + moreElements.length);
101 System.arraycopy(array, 0, result, 0, array.length);
102 System.arraycopy(moreElements, 0, result, array.length, moreElements.length);
104 return result;
109 * @author codistmonk (creation 2010-06-18)
111 public static interface EventRecorder {
115 * @return
116 * <br>Not null
117 * <br>Not shared
119 public abstract List<Event<?>> getEvents();
123 * @param <E> the expected event type
124 * @param index
125 * <br>Range: {@code [0 .. this.getEvents().size() - 1]}
126 * @return
127 * <br>Not null
128 * <br>Shared
129 * @throws IndexOutOfBoundsException if {@code index} is out of range
131 public abstract <E extends Event<?>> E getEvent(int index);
137 * @author codistmonk (creation 2010-06-18)
139 private static class RecorderInvocationHandler implements InvocationHandler, EventRecorder {
141 private final List<Event<?>> events;
143 RecorderInvocationHandler() {
144 this.events = new ArrayList<Event<?>>();
147 @Override
148 public final Object invoke(final Object proxy, final Method method, final Object[] arguments)
149 throws Throwable {
150 if (method.getDeclaringClass().isAssignableFrom(EventRecorder.class)) {
151 return method.invoke(this, arguments);
154 if (arguments.length == 1 && arguments[0] instanceof Event<?>) {
155 this.events.add((Event<?>) arguments[0]);
158 return null;
161 @Override
162 public final List<Event<?>> getEvents() {
163 return Collections.unmodifiableList(this.events);
166 @Override
167 @SuppressWarnings("unchecked")
168 public final <E extends Event<?>> E getEvent(final int index) {
169 return (E) this.getEvents().get(index);
172 @Override
173 public final boolean equals(final Object object) {
174 return this == object ||
175 object != null &&
176 Proxy.isProxyClass(object.getClass()) &&
177 this == Proxy.getInvocationHandler(object);
180 @Override
181 public final int hashCode() {
182 return super.hashCode();
189 * @author codistmonk (creation 2010-06-23)
191 private static final class DummyObservable extends AbstractObservable<DummyObservable.Listener> {
193 public final void fireNewEvent() {
194 new EventFiredEvent().fire();
199 * @author codistmonk (creation 2010-06-23)
201 public static interface Listener {
205 * @param event
206 * <br>Not null
208 public abstract void eventFired(final EventFiredEvent event);
214 * @author codistmonk (creation 2010-06-23)
216 public final class EventFiredEvent extends AbstractEvent<DummyObservable, Listener> {
218 @Override
219 protected final void notifyListener(final Listener listener) {
220 listener.eventFired(this);