[Aprog]
[aprog.git] / Aprog / src / net / sourceforge / aprog / events / Observable.java
blob039aea96a12c3f9b3b15b92326d6eebe5b7ee791
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 java.util.logging.Level;
28 import java.util.logging.Logger;
30 /**
31 * This interface defines the common behavior to observable objects,
32 * namely managing a collection of listeners.
33 * <br>A classic event interface is provided ({@link Event}),
34 * as well as an abstract implementation ({@link AbstractEvent})
35 * that takes care of dispatching itself using a visitor pattern.
36 * <br>The use of the {@link Event} interface is recommended but not mandatory;
37 * you can define your own event hierarchy and dispatching mechanism.
39 * @param <L> the event listener type
40 * @author codistmonk (creation 2010-06-18)
42 public interface Observable<L> {
44 /**
46 * @param listener
47 * <br>Not null
48 * <br>Shared
50 public abstract void addListener(L listener);
52 /**
54 * @param listener
55 * <br>Maybe null
56 * <br>Shared
58 public abstract void removeListener(L listener);
60 /**
62 * @return
63 * <br>Not null
64 * <br>New
66 public abstract Iterable<L> getListeners();
68 /**
69 * This interface defines a classic event interface that you can use to notify listeners.
71 * @param <S> the event source type
72 * @author codistmonk (creation 2010-06-18)
74 public static interface Event<S extends Observable<?>> {
76 /**
78 * @return
79 * <br>Not null
80 * <br>Shared
82 public abstract S getSource();
84 /**
85 * Time in milliseconds.
87 * @return
88 * <br>Range: {@code [0 .. Long.MAX_VALUE]}
90 public abstract long getTime();
92 /**
94 * @return {@code true} if and only if the event should not be dispatched to the remaining listeners
96 public abstract boolean isInterrupted();
98 /**
100 * @param interrupted Boolean indicating whether or not the event should be dispatched to the remaining listeners
102 public abstract void setInterrupted(boolean interrupted);
108 * @param <S> the event source type
109 * @param <L> the event listener type
110 * @author codistmonk (creation 2010-06-15)
112 public static abstract class AbstractEvent<S extends Observable<L>, L> implements Event<S> {
114 private final S source;
116 private final long time;
118 private boolean interrupted;
120 private boolean alreadyFired;
124 * @param source
125 * <br>Not null
126 * <br>Shared
127 * @param time in milliseconds
128 * <br>Range: {@code [0 .. Long.MAX_VALUE]}
130 protected AbstractEvent(final S source, final long time) {
131 this.time = time;
132 this.source = source;
137 * @param source
138 * <br>Not null
139 * <br>Shared
141 protected AbstractEvent(final S source) {
142 this(source, System.currentTimeMillis());
145 @Override
146 public final S getSource() {
147 return this.source;
150 @Override
151 public final long getTime() {
152 return this.time;
155 @Override
156 public final boolean isInterrupted() {
157 return this.interrupted;
160 @Override
161 public final void setInterrupted(final boolean interrupted) {
162 this.interrupted = interrupted;
166 * Dispatches this event to its source listeners.
167 * <br>If a listener throws an exception (subclass of {@link Exception}, checked or unchecked),
168 * the exception is logged and the dispatching continues to the next listener.
169 * <br>If you want to stop the dispatching before all listeners have been notified,
170 * use {@link #setInterrupted(boolean)}.
172 * @throws IllegalStateException if this event has already been fired
174 public final void fire() {
175 if (this.alreadyFired) {
176 throw new IllegalStateException("Already fired");
179 this.alreadyFired = true;
181 this.notifyListeners();
186 * @param listener
187 * <br>Not null
188 * <br>Input-output
190 protected abstract void notifyListener(L listener);
192 private final void notifyListeners() {
193 for (final L listener : this.getSource().getListeners()) {
194 this.tryToNotifyListener(listener);
196 if (this.isInterrupted()) {
197 break;
204 * @param listener
205 * <br>Not null
206 * <br>Input-output
208 private final void tryToNotifyListener(final L listener) {
209 try {
210 this.notifyListener(listener);
211 } catch (final Exception exception) {
212 Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, exception);