source: josm/trunk/src/org/openstreetmap/josm/data/osm/ChangesetCache.java @ 12743

Last change on this file since 12743 was 12743, checked in by Don-vip, 19 months ago

see #15229 - see #15182 - deprecate gui.JosmUserIdentityManager - replaced by data.UserIdentityManager

  • Property svn:eol-style set to native
File size: 8.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm;
3
4import java.util.ArrayList;
5import java.util.Collection;
6import java.util.HashMap;
7import java.util.HashSet;
8import java.util.List;
9import java.util.Map;
10import java.util.Set;
11import java.util.concurrent.CopyOnWriteArrayList;
12import java.util.stream.Collectors;
13
14import org.openstreetmap.josm.Main;
15import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
16import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
17import org.openstreetmap.josm.data.UserIdentityManager;
18import org.openstreetmap.josm.gui.util.GuiHelper;
19import org.openstreetmap.josm.tools.SubclassFilteredCollection;
20
21/**
22 * ChangesetCache is global in-memory cache for changesets downloaded from
23 * an OSM API server. The unique instance is available as singleton, see
24 * {@link #getInstance()}.
25 *
26 * Clients interested in cache updates can register for {@link ChangesetCacheEvent}s
27 * using {@link #addChangesetCacheListener(ChangesetCacheListener)}. They can use
28 * {@link #removeChangesetCacheListener(ChangesetCacheListener)} to unregister as
29 * cache event listener.
30 *
31 * The cache itself listens to {@link java.util.prefs.PreferenceChangeEvent}s. It
32 * clears itself if the OSM API URL is changed in the preferences.
33 *
34 * {@link ChangesetCacheEvent}s are delivered on the EDT.
35 *
36 */
37public final class ChangesetCache implements PreferenceChangedListener {
38    /** the unique instance */
39    private static final ChangesetCache INSTANCE = new ChangesetCache();
40
41    /** the cached changesets */
42    private final Map<Integer, Changeset> cache = new HashMap<>();
43
44    private final CopyOnWriteArrayList<ChangesetCacheListener> listeners = new CopyOnWriteArrayList<>();
45
46    /**
47     * Constructs a new {@code ChangesetCache}.
48     */
49    private ChangesetCache() {
50        Main.pref.addPreferenceChangeListener(this);
51    }
52
53    /**
54     * Replies the unique instance of the cache
55     * @return the unique instance of the cache
56     */
57    public static ChangesetCache getInstance() {
58        return INSTANCE;
59    }
60
61    /**
62     * Add a changeset cache listener.
63     * @param listener changeset cache listener to add
64     */
65    public void addChangesetCacheListener(ChangesetCacheListener listener) {
66        if (listener != null) {
67            listeners.addIfAbsent(listener);
68        }
69    }
70
71    /**
72     * Remove a changeset cache listener.
73     * @param listener changeset cache listener to remove
74     */
75    public void removeChangesetCacheListener(ChangesetCacheListener listener) {
76        if (listener != null) {
77            listeners.remove(listener);
78        }
79    }
80
81    private void fireChangesetCacheEvent(final ChangesetCacheEvent e) {
82        GuiHelper.runInEDT(() -> {
83            for (ChangesetCacheListener l: listeners) {
84                l.changesetCacheUpdated(e);
85            }
86        });
87    }
88
89    private void update(Changeset cs, DefaultChangesetCacheEvent e) {
90        if (cs == null) return;
91        if (cs.isNew()) return;
92        Changeset inCache = cache.get(cs.getId());
93        if (inCache != null) {
94            inCache.mergeFrom(cs);
95            e.rememberUpdatedChangeset(inCache);
96        } else {
97            e.rememberAddedChangeset(cs);
98            cache.put(cs.getId(), cs);
99        }
100    }
101
102    /**
103     * Update a single changeset.
104     * @param cs changeset to update
105     */
106    public void update(Changeset cs) {
107        DefaultChangesetCacheEvent e = new DefaultChangesetCacheEvent(this);
108        update(cs, e);
109        fireChangesetCacheEvent(e);
110    }
111
112    /**
113     * Update a collection of changesets.
114     * @param changesets changesets to update
115     */
116    public void update(Collection<Changeset> changesets) {
117        if (changesets == null || changesets.isEmpty()) return;
118        DefaultChangesetCacheEvent e = new DefaultChangesetCacheEvent(this);
119        for (Changeset cs: changesets) {
120            update(cs, e);
121        }
122        fireChangesetCacheEvent(e);
123    }
124
125    /**
126     * Determines if the cache contains an entry for given changeset identifier.
127     * @param id changeset id
128     * @return {@code true} if the cache contains an entry for {@code id}
129     */
130    public boolean contains(int id) {
131        if (id <= 0) return false;
132        return cache.get(id) != null;
133    }
134
135    /**
136     * Determines if the cache contains an entry for given changeset.
137     * @param cs changeset
138     * @return {@code true} if the cache contains an entry for {@code cs}
139     */
140    public boolean contains(Changeset cs) {
141        if (cs == null) return false;
142        if (cs.isNew()) return false;
143        return contains(cs.getId());
144    }
145
146    /**
147     * Returns the entry for given changeset identifier.
148     * @param id changeset id
149     * @return the entry for given changeset identifier, or null
150     */
151    public Changeset get(int id) {
152        return cache.get(id);
153    }
154
155    /**
156     * Returns the list of changesets contained in the cache.
157     * @return the list of changesets contained in the cache
158     */
159    public Set<Changeset> getChangesets() {
160        return new HashSet<>(cache.values());
161    }
162
163    private void remove(int id, DefaultChangesetCacheEvent e) {
164        if (id <= 0) return;
165        Changeset cs = cache.get(id);
166        if (cs == null) return;
167        cache.remove(id);
168        e.rememberRemovedChangeset(cs);
169    }
170
171    /**
172     * Remove the entry for the given changeset identifier.
173     * A {@link ChangesetCacheEvent} is fired.
174     * @param id changeset id
175     */
176    public void remove(int id) {
177        DefaultChangesetCacheEvent e = new DefaultChangesetCacheEvent(this);
178        remove(id, e);
179        if (!e.isEmpty()) {
180            fireChangesetCacheEvent(e);
181        }
182    }
183
184    /**
185     * Remove the entry for the given changeset.
186     * A {@link ChangesetCacheEvent} is fired.
187     * @param cs changeset
188     */
189    public void remove(Changeset cs) {
190        if (cs == null) return;
191        if (cs.isNew()) return;
192        remove(cs.getId());
193    }
194
195    /**
196     * Removes the changesets in <code>changesets</code> from the cache.
197     * A {@link ChangesetCacheEvent} is fired.
198     *
199     * @param changesets the changesets to remove. Ignored if null.
200     */
201    public void remove(Collection<Changeset> changesets) {
202        if (changesets == null) return;
203        DefaultChangesetCacheEvent evt = new DefaultChangesetCacheEvent(this);
204        for (Changeset cs : changesets) {
205            if (cs == null || cs.isNew()) {
206                continue;
207            }
208            remove(cs.getId(), evt);
209        }
210        if (!evt.isEmpty()) {
211            fireChangesetCacheEvent(evt);
212        }
213    }
214
215    /**
216     * Returns the number of changesets contained in the cache.
217     * @return the number of changesets contained in the cache
218     */
219    public int size() {
220        return cache.size();
221    }
222
223    /**
224     * Clears the cache.
225     */
226    public void clear() {
227        DefaultChangesetCacheEvent e = new DefaultChangesetCacheEvent(this);
228        for (Changeset cs: cache.values()) {
229            e.rememberRemovedChangeset(cs);
230        }
231        cache.clear();
232        fireChangesetCacheEvent(e);
233    }
234
235    /**
236     * Replies the list of open changesets.
237     * @return The list of open changesets
238     */
239    public List<Changeset> getOpenChangesets() {
240        return cache.values().stream()
241                .filter(Changeset::isOpen)
242                .collect(Collectors.toList());
243    }
244
245    /**
246     * If the current user {@link UserIdentityManager#isAnonymous() is known}, the {@link #getOpenChangesets() open changesets}
247     * for the {@link UserIdentityManager#isCurrentUser(User) current user} are returned. Otherwise,
248     * the unfiltered {@link #getOpenChangesets() open changesets} are returned.
249     *
250     * @return a list of changesets
251     */
252    public List<Changeset> getOpenChangesetsForCurrentUser() {
253        if (UserIdentityManager.getInstance().isAnonymous()) {
254            return getOpenChangesets();
255        } else {
256            return new ArrayList<>(SubclassFilteredCollection.filter(getOpenChangesets(),
257                    object -> UserIdentityManager.getInstance().isCurrentUser(object.getUser())));
258        }
259    }
260
261    /* ------------------------------------------------------------------------- */
262    /* interface PreferenceChangedListener                                       */
263    /* ------------------------------------------------------------------------- */
264    @Override
265    public void preferenceChanged(PreferenceChangeEvent e) {
266        if (e.getKey() == null || !"osm-server.url".equals(e.getKey()))
267            return;
268
269        // clear the cache when the API url changes
270        if (e.getOldValue() == null || e.getNewValue() == null || !e.getOldValue().equals(e.getNewValue())) {
271            clear();
272        }
273    }
274}
Note: See TracBrowser for help on using the repository browser.