Gridarta Editor
DelayedMapModelListenerManager.java
Go to the documentation of this file.
1 /*
2  * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games.
3  * Copyright (C) 2000-2015 The Gridarta Developers.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 package net.sf.gridarta.gui.delayedmapmodel;
21 
22 import java.lang.reflect.InvocationTargetException;
23 import java.util.IdentityHashMap;
24 import java.util.Map;
25 import java.util.Set;
26 import javax.swing.SwingUtilities;
40 import net.sf.gridarta.utils.Exiter;
42 import net.sf.gridarta.utils.Size2D;
43 import org.apache.log4j.Category;
44 import org.apache.log4j.Logger;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
47 
52 public class DelayedMapModelListenerManager<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> {
53 
57  @NotNull
58  private static final Category LOG = Logger.getLogger(DelayedMapModelListenerManager.class);
59 
64  private static final long DELAY = 500L;
65 
69  @NotNull
71 
76  @NotNull
77  private final Map<MapModel<G, A, R>, MapModelListenerImpl> mapModelListeners = new IdentityHashMap<>();
78 
83  @NotNull
84  private final Map<MapModel<G, A, R>, MapArchObjectListenerImpl> mapArchObjectListeners = new IdentityHashMap<>();
85 
90  private long timeout;
91 
95  @NotNull
96  private final Object sync = new Object();
97 
102  @NotNull
103  private final Map<MapModel<G, A, R>, Void> scheduledMapModels = new IdentityHashMap<>();
104 
108  @NotNull
110 
115  @NotNull
117 
118  @Override
119  public void currentMapChanged(@Nullable final MapControl<G, A, R> mapControl) {
120  // ignore
121  }
122 
123  @Override
124  public void mapCreated(@NotNull final MapControl<G, A, R> mapControl, final boolean interactive) {
125  final MapModel<G, A, R> mapModel = mapControl.getMapModel();
126  final MapModelListenerImpl mapModelListener = new MapModelListenerImpl(mapModel);
127  mapModel.addMapModelListener(mapModelListener);
128  mapModelListeners.put(mapModel, mapModelListener);
129  final MapArchObjectListenerImpl mapArchObjectListener = new MapArchObjectListenerImpl(mapModel);
130  mapModel.getMapArchObject().addMapArchObjectListener(mapArchObjectListener);
131  mapArchObjectListeners.put(mapModel, mapArchObjectListener);
132  scheduleMapModel(mapModel);
133  }
134 
135  @Override
136  public void mapClosing(@NotNull final MapControl<G, A, R> mapControl) {
137  // ignore
138  }
139 
140  @Override
141  public void mapClosed(@NotNull final MapControl<G, A, R> mapControl) {
142  final MapModel<G, A, R> mapModel = mapControl.getMapModel();
143  final MapModelListener<G, A, R> mapModelListener = mapModelListeners.remove(mapModel);
144  if (mapModelListener != null) {
145  mapModel.removeMapModelListener(mapModelListener);
146  }
147  final MapArchObjectListener mapArchObjectListener = mapArchObjectListeners.remove(mapModel);
148  if (mapArchObjectListener != null) {
149  mapModel.getMapArchObject().removeMapArchObjectListener(mapArchObjectListener);
150  }
151  synchronized (sync) {
152  scheduledMapModels.remove(mapControl.getMapModel());
153  }
154  }
155 
156  };
157 
161  private final Thread thread = new Thread(new Runnable() {
162 
163  @Override
164  public void run() {
165  try {
166  final Map<MapModel<G, A, R>, Void> mapModels = new IdentityHashMap<>();
167  while (!Thread.currentThread().isInterrupted()) {
168  while (true) {
169  final long now = System.currentTimeMillis();
170  synchronized (sync) {
171  if (timeout == 0L) {
172  sync.wait();
173  } else {
174  final long diff = timeout - now;
175  if (diff <= 0L) {
176  timeout = 0L;
177  mapModels.putAll(scheduledMapModels);
178  scheduledMapModels.clear();
179  break;
180  }
181 
182  sync.wait(diff);
183  }
184  }
185  }
186 
187  try {
188  SwingUtilities.invokeAndWait(new Runnable() {
189 
190  @Override
191  public void run() {
192  for (final Map.Entry<MapModel<G, A, R>, Void> e : mapModels.entrySet()) {
193  final MapModel<G, A, R> mapModel = e.getKey();
194  for (final DelayedMapModelListener<G, A, R> listener : listeners.getListeners()) {
195  listener.mapModelChanged(mapModel);
196  }
197  }
198  }
199  });
200  } catch (final InvocationTargetException ex) {
201  LOG.error("InvocationTargetException: " + ex.getMessage(), ex);
202  }
203  mapModels.clear();
204  }
205  } catch (final InterruptedException ignored) {
206  Thread.currentThread().interrupt();
207  }
208  }
209 
210  });
211 
217  public DelayedMapModelListenerManager(@NotNull final MapManager<G, A, R> mapManager, @NotNull final Exiter exiter) {
218  this.mapManager = mapManager;
219  mapManager.addMapManagerListener(mapManagerListener);
220 
221  final ExiterListener exiterListener = new ExiterListener() {
222 
223  @Override
224  public void preExitNotify() {
225  thread.interrupt();
226  try {
227  thread.join();
228  } catch (final InterruptedException ignored) {
229  Thread.currentThread().interrupt();
230  LOG.warn("DelayedMapModelListenerManager was interrupted");
231  }
232  }
233 
234  @Override
235  public void appExitNotify() {
236  // ignore
237  }
238 
239  @Override
240  public void waitExitNotify() {
241  // ignore
242  }
243 
244  };
245  exiter.addExiterListener(exiterListener);
246  }
247 
251  public void start() {
252  thread.start();
254  }
255 
260  public void addDelayedMapModelListener(@NotNull final DelayedMapModelListener<G, A, R> listener) {
261  listeners.add(listener);
262  for (final MapControl<G, A, R> mapControl : mapManager.getOpenedMaps()) {
263  listener.mapModelChanged(mapControl.getMapModel());
264  }
265  }
266 
272  listeners.remove(listener);
273  }
274 
279  public void scheduleMapModel(@NotNull final MapModel<G, A, R> mapModel) {
280  final long now = System.currentTimeMillis();
281  synchronized (sync) {
282  scheduledMapModels.put(mapModel, null);
283  timeout = now + DELAY;
284  sync.notifyAll();
285  }
286  }
287 
291  public void scheduleAllMapModels() {
292  final long now = System.currentTimeMillis();
293  synchronized (sync) {
294  for (final MapControl<G, A, R> mapControl : mapManager.getOpenedMaps()) {
295  scheduledMapModels.put(mapControl.getMapModel(), null);
296  }
297  timeout = now + DELAY;
298  sync.notifyAll();
299  }
300  }
301 
307  private class MapModelListenerImpl implements MapModelListener<G, A, R> {
308 
313 
318  private MapModelListenerImpl(@NotNull final MapModel<G, A, R> mapModel) {
319  this.mapModel = mapModel;
320  }
321 
322  @Override
323  public void mapSizeChanged(@NotNull final Size2D newSize) {
324  scheduleMapModel(mapModel);
325  }
326 
327  @Override
328  public void mapSquaresChanged(@NotNull final Set<MapSquare<G, A, R>> mapSquares) {
329  scheduleMapModel(mapModel);
330  }
331 
332  @Override
333  public void mapObjectsChanged(@NotNull final Set<G> gameObjects, @NotNull final Set<G> transientGameObjects) {
334  scheduleMapModel(mapModel);
335  }
336 
337  @Override
338  public void errorsChanged(@NotNull final ErrorCollector<G, A, R> errors) {
339  // ignore
340  }
341 
342  @Override
343  public void mapFileChanged(@Nullable final MapFile oldMapFile) {
344  scheduleMapModel(mapModel);
345  }
346 
347  @Override
348  public void modifiedChanged() {
349  // ignore
350  }
351 
352  }
353 
360 
365 
370  private MapArchObjectListenerImpl(@NotNull final MapModel<G, A, R> mapModel) {
371  this.mapModel = mapModel;
372  }
373 
374  @Override
375  public void mapMetaChanged() {
376  scheduleMapModel(mapModel);
377  }
378 
379  @Override
380  public void mapSizeChanged(@NotNull final Size2D mapSize) {
381  // ignore
382  }
383 
384  }
385 
386 }
Interface for listeners listening on map arch object changes.
void mapObjectsChanged(@NotNull final Set< G > gameObjects, @NotNull final Set< G > transientGameObjects)
One or more GameObjects on a map have changed.
A MapModel reflects the data of a map.
Definition: MapModel.java:75
A MapManager manages all opened maps.
Definition: MapManager.java:37
T [] getListeners()
Returns an array of all the listeners.
long timeout
The timestamp when to deliver scheduled map models.
This package contains the framework for validating maps.
final MapManagerListener< G, A, R > mapManagerListener
The MapManagerListener used to detect created and closed MapControl instances.
void removeDelayedMapModelListener(@NotNull final DelayedMapModelListener< G, A, R > listener)
Removes a DelayedMapModelListener to be notified.
Interface for listeners listening on MapModel events.
void addMapModelListener(@NotNull MapModelListener< G, A, R > listener)
Register a map listener.
Base package of all Gridarta classes.
DelayedMapModelListenerManager(@NotNull final MapManager< G, A, R > mapManager, @NotNull final Exiter exiter)
Creates a new instance.
MapArchObjectListenerImpl(@NotNull final MapModel< G, A, R > mapModel)
Creates a new instance.
Reflects a game object (object on a map).
Definition: GameObject.java:36
Exits the application.
Definition: Exiter.java:28
Interface for listeners listening to MapManager changes.
final Map< MapModel< G, A, R >, Void > scheduledMapModels
The MapModels having pending changes.
void remove(@NotNull final T listener)
Removes a listener.
Interface for listeners interested in Exiter related events.
final Map< MapModel< G, A, R >, MapArchObjectListenerImpl > mapArchObjectListeners
All known MapModel instances.
GameObjects are the objects based on Archetypes found on maps.
MapModelListenerImpl(@NotNull final MapModel< G, A, R > mapModel)
Creates a new instance.
void add(@NotNull final T listener)
Adds a listener.
void mapSquaresChanged(@NotNull final Set< MapSquare< G, A, R >> mapSquares)
Squares of a map have changed.
void removeMapModelListener(@NotNull MapModelListener< G, A, R > listener)
Unregister a map listener.
final Map< MapModel< G, A, R >, MapModelListenerImpl > mapModelListeners
All known MapModel instances.
Type-safe version of EventListenerList.
A getMapArchObject()
Returns the Map Arch Object with the meta information about the map.
void addMapManagerListener(@NotNull MapManagerListener< G, A, R > listener)
Adds a MapManagerListener to be notified.
Currently nothing more than a marker interface for unification.
Definition: MapControl.java:35
An interface for classes that collect errors.
static final Category LOG
The Logger for printing log messages.
void scheduleMapModel(@NotNull final MapModel< G, A, R > mapModel)
Schedules a MapModel which has been changed.
Interface for listeners interested in delayed map model change events.
Provides support for delayed notification of MapModel changes.
void errorsChanged(@NotNull final ErrorCollector< G, A, R > errors)
The errors of a map model have changed.
List< MapControl< G, A, R > > getOpenedMaps()
Returns all opened maps.
void addDelayedMapModelListener(@NotNull final DelayedMapModelListener< G, A, R > listener)
Adds a DelayedMapModelListener to be notified.
The location of a map file with a map directory.
Definition: MapFile.java:31
final EventListenerList2< DelayedMapModelListener< G, A, R > > listeners
The listeners to notify.
The class Size2D represents a 2d rectangular area.
Definition: Size2D.java:30