4 * Copyright 2010 Codist Monk.
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
25 package net
.sourceforge
.aprog
.events
;
27 import java
.util
.logging
.Level
;
28 import java
.util
.logging
.Logger
;
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
> {
50 public abstract void addListener(L listener
);
58 public abstract void removeListener(L listener
);
66 public abstract Iterable
<L
> getListeners();
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
<?
>> {
82 public abstract S
getSource();
85 * Time in milliseconds.
88 * <br>Range: {@code [0 .. Long.MAX_VALUE]}
90 public abstract long getTime();
94 * @return {@code true} if and only if the event should not be dispatched to the remaining listeners
96 public abstract boolean isInterrupted();
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
;
127 * @param time in milliseconds
128 * <br>Range: {@code [0 .. Long.MAX_VALUE]}
130 protected AbstractEvent(final S source
, final long time
) {
132 this.source
= source
;
141 protected AbstractEvent(final S source
) {
142 this(source
, System
.currentTimeMillis());
146 public final S
getSource() {
151 public final long getTime() {
156 public final boolean isInterrupted() {
157 return this.interrupted
;
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();
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()) {
208 private final void tryToNotifyListener(final L listener
) {
210 this.notifyListener(listener
);
211 } catch (final Exception exception
) {
212 Logger
.getLogger(this.getClass().getName()).log(Level
.SEVERE
, null, exception
);