Gridarta Editor
AbstractIndex.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.model.index;
21 
22 import java.io.IOException;
23 import java.io.ObjectInputStream;
24 import java.io.ObjectOutputStream;
25 import java.io.Serializable;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.Map;
32 import java.util.Map.Entry;
33 import java.util.concurrent.CopyOnWriteArrayList;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
36 
42 public class AbstractIndex<V extends Serializable> implements Index<V> {
43 
48  @NotNull
49  private final Object sync = new Object();
50 
54  @NotNull
55  private final Collection<IndexListener<V>> indexListeners = new CopyOnWriteArrayList<>();
56 
61  @NotNull
62  private final Map<V, Long> timestamps = new HashMap<>();
63 
68  @NotNull
69  private final Map<V, String> names = new HashMap<>();
70 
75  @NotNull
76  private final Collection<V> pending = new HashSet<>();
77 
82  private boolean modified;
83 
87  private boolean transaction;
88 
94  @NotNull
95  private final Collection<V> transactionDelete = new HashSet<>();
96 
97  @Override
98  public int size() {
99  return timestamps.size();
100  }
101 
102  @NotNull
103  @Override
104  public Collection<V> findPartialName(@NotNull final String name) {
105  synchronized (sync) {
106  final String nameLowerCase = name.toLowerCase();
107  final Collection<V> result = new HashSet<>();
108  for (final Entry<V, String> e : names.entrySet()) {
109  if (e.getValue().toLowerCase().contains(nameLowerCase)) {
110  result.add(e.getKey());
111  }
112  }
113  return result;
114  }
115  }
116 
117  @Override
118  public void beginUpdate() {
119  synchronized (sync) {
120  transaction = true;
121  transactionDelete.clear();
122  transactionDelete.addAll(timestamps.keySet());
123  }
124  }
125 
126  @Override
127  public void endUpdate() {
128  synchronized (sync) {
129  if (!transaction) {
130  throw new IllegalStateException("no transaction is active");
131  }
132  transaction = false;
133 
134  final Collection<V> tmp = new ArrayList<>(transactionDelete);
135  transactionDelete.clear();
136  if (!tmp.isEmpty()) {
137  modified = true;
138  for (final V value : tmp) {
139  timestamps.remove(value);
140  pending.remove(value);
141  names.remove(value);
142  }
143  for (final V value : tmp) {
144  for (final IndexListener<V> listener : indexListeners) {
145  listener.valueRemoved(value);
146  listener.nameChanged();
147  }
148  }
149  }
150  }
151  }
152 
153  @Override
154  public void add(@NotNull final V value, final long timestamp) {
155  synchronized (sync) {
156  final Long oldTimestamp = timestamps.put(value, timestamp);
157  final boolean notifyPending;
158  final boolean notifyAdded;
159  if (oldTimestamp == null || oldTimestamp != timestamp) {
160  notifyPending = pending.add(value) && pending.size() == 1;
161  notifyAdded = oldTimestamp == null;
162  modified = true;
163  } else {
164  notifyPending = false;
165  notifyAdded = false;
166  }
167  transactionDelete.remove(value);
168  if (notifyAdded) {
169  for (final IndexListener<V> listener : indexListeners) {
170  listener.valueAdded(value);
171  }
172  }
173  if (notifyPending) {
174  for (final IndexListener<V> listener : indexListeners) {
175  listener.pendingChanged();
176  }
177  }
178  }
179  }
180 
181  @Override
182  public void remove(@NotNull final V value) {
183  synchronized (sync) {
184  if (timestamps.remove(value) == null) {
185  return;
186  }
187  modified = true;
188  pending.remove(value);
189  names.remove(value);
190  for (final IndexListener<V> listener : indexListeners) {
191  listener.valueRemoved(value);
192  listener.nameChanged();
193  }
194  }
195  }
196 
197  @Override
198  public void setPending(@NotNull final V value) {
199  synchronized (sync) {
200  final boolean notifyPending = pending.add(value) && pending.size() == 1;
201  if (!timestamps.containsKey(value)) {
202  modified = true;
203  timestamps.put(value, 0L);
204  for (final IndexListener<V> listener : indexListeners) {
205  listener.valueAdded(value);
206  }
207  }
208  if (notifyPending) {
209  for (final IndexListener<V> listener : indexListeners) {
210  listener.pendingChanged();
211  }
212  }
213  }
214  }
215 
216  @Override
217  public void setName(@NotNull final V value, final long timestamp, @NotNull final String name) {
218  synchronized (sync) {
219  timestamps.put(value, timestamp);
220  final String oldName = names.put(value, name);
221  if (oldName != null && oldName.equals(name)) {
222  return;
223  }
224  modified = true;
225  for (final IndexListener<V> listener : indexListeners) {
226  listener.nameChanged();
227  }
228  }
229  }
230 
231  @Nullable
232  @Override
233  public String getName(@NotNull final V value) {
234  synchronized (sync) {
235  return names.get(value);
236  }
237  }
238 
239  @Nullable
240  @Override
241  public V removePending() {
242  synchronized (sync) {
243  final Iterator<V> it = pending.iterator();
244  if (!it.hasNext()) {
245  return null;
246  }
247 
248  final V result = it.next();
249  it.remove();
250  return result;
251  }
252  }
253 
254  @Override
255  public boolean hasPending() {
256  synchronized (sync) {
257  return !pending.isEmpty();
258  }
259  }
260 
261  @Override
262  public boolean isModified() {
263  synchronized (sync) {
264  return modified;
265  }
266  }
267 
268  @Override
269  public void addIndexListener(@NotNull final IndexListener<V> listener) {
270  indexListeners.add(listener);
271  }
272 
273  @Override
274  public void removeIndexListener(@NotNull final IndexListener<V> listener) {
275  indexListeners.remove(listener);
276  }
277 
278  @Override
279  public void save(@NotNull final ObjectOutputStream objectOutputStream) throws IOException {
280  synchronized (sync) {
281  objectOutputStream.writeObject(timestamps);
282  objectOutputStream.writeObject(names);
283  modified = false;
284  }
285  }
286 
290  @Override
291  @SuppressWarnings("unchecked")
292  public void load(@NotNull final ObjectInputStream objectInputStream) throws IOException {
293  synchronized (sync) {
294  final Map<V, Long> tmpTimestamps;
295  final Map<V, String> tmpNames;
296  try {
297  tmpTimestamps = (Map<V, Long>) objectInputStream.readObject();
298  tmpNames = (Map<V, String>) objectInputStream.readObject();
299  } catch (final ClassNotFoundException ex) {
300  throw new IOException(ex.getMessage(), ex);
301  }
302  if (transaction) {
303  throw new IOException("cannot restore state within active transaction");
304  }
305 
306  // drop excess elements from tmpNames to force a consistent state
307  tmpNames.keySet().retainAll(tmpTimestamps.keySet());
308 
309  clear();
310  timestamps.putAll(tmpTimestamps);
311  names.putAll(tmpNames);
312  }
313  }
314 
315  @Override
316  public void clear() {
317  synchronized (sync) {
318  timestamps.clear();
319  names.clear();
320  pending.clear();
321  transactionDelete.clear();
322  modified = false;
323  }
324  }
325 
326  @Override
327  public void indexingFinished() {
328  for (final IndexListener<V> listener : indexListeners) {
329  listener.indexingFinished();
330  }
331  }
332 
333 }
net.sf.gridarta.model.index.AbstractIndex.modified
boolean modified
Definition: AbstractIndex.java:82
net.sf.gridarta.model.index.AbstractIndex.removeIndexListener
void removeIndexListener(@NotNull final IndexListener< V > listener)
Definition: AbstractIndex.java:274
net.sf.gridarta.model.index.AbstractIndex.add
void add(@NotNull final V value, final long timestamp)
Definition: AbstractIndex.java:154
net.sf.gridarta.model.index.AbstractIndex.addIndexListener
void addIndexListener(@NotNull final IndexListener< V > listener)
Definition: AbstractIndex.java:269
net.sf.gridarta.model.index.AbstractIndex.transaction
boolean transaction
Definition: AbstractIndex.java:87
net.sf.gridarta.model.index.AbstractIndex.names
final Map< V, String > names
Definition: AbstractIndex.java:69
net.sf.gridarta.model.index.AbstractIndex.setName
void setName(@NotNull final V value, final long timestamp, @NotNull final String name)
Definition: AbstractIndex.java:217
net.sf.gridarta.model.index.AbstractIndex.setPending
void setPending(@NotNull final V value)
Definition: AbstractIndex.java:198
net.sf.gridarta.model.index.AbstractIndex.indexListeners
final Collection< IndexListener< V > > indexListeners
Definition: AbstractIndex.java:55
net.sf.gridarta.model.index.AbstractIndex.beginUpdate
void beginUpdate()
Definition: AbstractIndex.java:118
net.sf.gridarta.model.index.AbstractIndex.removePending
V removePending()
Definition: AbstractIndex.java:241
net.sf.gridarta.model.index.AbstractIndex.getName
String getName(@NotNull final V value)
Definition: AbstractIndex.java:233
net.sf.gridarta.model.index.AbstractIndex.save
void save(@NotNull final ObjectOutputStream objectOutputStream)
Definition: AbstractIndex.java:279
net.sf.gridarta.model.index.AbstractIndex.timestamps
final Map< V, Long > timestamps
Definition: AbstractIndex.java:62
net.sf.gridarta.model.index.AbstractIndex.transactionDelete
final Collection< V > transactionDelete
Definition: AbstractIndex.java:95
net.sf.gridarta.model.index.AbstractIndex.isModified
boolean isModified()
Definition: AbstractIndex.java:262
net.sf.gridarta.model.index.AbstractIndex.findPartialName
Collection< V > findPartialName(@NotNull final String name)
Definition: AbstractIndex.java:104
net.sf.gridarta.model.index.AbstractIndex
Definition: AbstractIndex.java:42
net.sf.gridarta.model.index.IndexListener
Definition: IndexListener.java:30
net.sf.gridarta.model.index.AbstractIndex.clear
void clear()
Definition: AbstractIndex.java:316
net.sf.gridarta.model.index.Index
Definition: Index.java:41
net.sf.gridarta.model.index.AbstractIndex.pending
final Collection< V > pending
Definition: AbstractIndex.java:76
net.sf.gridarta.model.index.AbstractIndex.indexingFinished
void indexingFinished()
Definition: AbstractIndex.java:327
net.sf.gridarta.model.index.AbstractIndex.size
int size()
Definition: AbstractIndex.java:98
net.sf.gridarta.model.index.AbstractIndex.hasPending
boolean hasPending()
Definition: AbstractIndex.java:255
net.sf.gridarta.model.index.AbstractIndex.load
void load(@NotNull final ObjectInputStream objectInputStream)
Definition: AbstractIndex.java:292
net.sf.gridarta.model.index.AbstractIndex.endUpdate
void endUpdate()
Definition: AbstractIndex.java:127
net.sf.gridarta.model.index.AbstractIndex.sync
final Object sync
Definition: AbstractIndex.java:49