001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs.changeset;
003
004import java.util.ArrayList;
005import java.util.Collection;
006import java.util.Comparator;
007import java.util.HashSet;
008import java.util.List;
009import java.util.Set;
010
011import javax.swing.DefaultListModel;
012import javax.swing.DefaultListSelectionModel;
013
014import org.openstreetmap.josm.data.osm.Changeset;
015import org.openstreetmap.josm.data.osm.ChangesetCache;
016import org.openstreetmap.josm.data.osm.ChangesetCacheEvent;
017import org.openstreetmap.josm.data.osm.ChangesetCacheListener;
018import org.openstreetmap.josm.data.osm.DataSet;
019import org.openstreetmap.josm.data.osm.OsmPrimitive;
020import org.openstreetmap.josm.data.osm.Storage;
021import org.openstreetmap.josm.gui.util.GuiHelper;
022
023/**
024 * This is the model that backs a list of changesets
025 */
026public class ChangesetListModel extends DefaultListModel<Changeset> implements ChangesetCacheListener {
027    private final transient List<Changeset> data = new ArrayList<>();
028    private final transient Storage<Changeset> shownChangesets = new Storage<>(true);
029    private final DefaultListSelectionModel selectionModel;
030
031    /**
032     * Creates a new {@link ChangesetListModel}
033     * @param selectionModel The selection model to use for this list
034     */
035    public ChangesetListModel(DefaultListSelectionModel selectionModel) {
036        this.selectionModel = selectionModel;
037    }
038
039    /**
040     * Gets the list of changesets that are currently selected
041     * @return The selected changesets
042     */
043    public Set<Changeset> getSelectedChangesets() {
044        Set<Changeset> ret = new HashSet<>();
045        for (int i = 0; i < getSize(); i++) {
046            if (selectionModel.isSelectedIndex(i)) {
047                ret.add(data.get(i));
048            }
049        }
050        return ret;
051    }
052
053    /**
054     * Gets the IDs of the changesets that are selected
055     * @return The selected ids
056     */
057    public Set<Integer> getSelectedChangesetIds() {
058        Set<Integer> ret = new HashSet<>();
059        for (int i = 0; i < getSize(); i++) {
060            if (selectionModel.isSelectedIndex(i)) {
061                ret.add(data.get(i).getId());
062            }
063        }
064        return ret;
065    }
066
067    /**
068     * Sets the changesets to select
069     * @param changesets The changesets
070     */
071    public void setSelectedChangesets(Collection<Changeset> changesets) {
072        selectionModel.setValueIsAdjusting(true);
073        selectionModel.clearSelection();
074        if (changesets != null) {
075            for (Changeset cs: changesets) {
076                int idx = data.indexOf(cs);
077                if (idx >= 0) {
078                    selectionModel.addSelectionInterval(idx, idx);
079                }
080            }
081        }
082        selectionModel.setValueIsAdjusting(false);
083    }
084
085    protected void setChangesets(Collection<Changeset> changesets) {
086        shownChangesets.clear();
087        if (changesets != null) {
088            shownChangesets.addAll(changesets);
089        }
090        updateModel();
091    }
092
093    private void updateModel() {
094        Set<Changeset> sel = getSelectedChangesets();
095        data.clear();
096        data.addAll(shownChangesets);
097        ChangesetCache cache = ChangesetCache.getInstance();
098        for (Changeset cs: data) {
099            if (cache.contains(cs) && cache.get(cs.getId()) != cs) {
100                cs.mergeFrom(cache.get(cs.getId()));
101            }
102        }
103        sort();
104        fireIntervalAdded(this, 0, getSize());
105        setSelectedChangesets(sel);
106    }
107
108    /**
109     * Loads this list with the given changesets
110     * @param ids The ids of the changesets to display
111     */
112    public void initFromChangesetIds(Collection<Integer> ids) {
113        if (ids == null || ids.isEmpty()) {
114            setChangesets(null);
115            return;
116        }
117        Set<Changeset> changesets = new HashSet<>(ids.size());
118        for (int id: ids) {
119            if (id <= 0) {
120                continue;
121            }
122            changesets.add(new Changeset(id));
123        }
124        setChangesets(changesets);
125    }
126
127    /**
128     * Loads this list with the given changesets
129     * @param primitives The primitives of which the changesets should be displayed
130     */
131    public void initFromPrimitives(Collection<? extends OsmPrimitive> primitives) {
132        if (primitives == null) {
133            setChangesets(null);
134            return;
135        }
136        Set<Changeset> changesets = new HashSet<>();
137        for (OsmPrimitive p: primitives) {
138            if (p.getChangesetId() <= 0) {
139                continue;
140            }
141            changesets.add(new Changeset(p.getChangesetId()));
142        }
143        setChangesets(changesets);
144    }
145
146    /**
147     * Loads this list with the given changesets
148     * @param ds The data set to get all changesets from
149     */
150    public void initFromDataSet(DataSet ds) {
151        if (ds == null) {
152            setChangesets(null);
153            return;
154        }
155        Set<Changeset> changesets = new HashSet<>();
156        for (OsmPrimitive p: ds.allPrimitives()) {
157            if (p.getChangesetId() <= 0) {
158                continue;
159            }
160            changesets.add(new Changeset(p.getChangesetId()));
161        }
162        setChangesets(changesets);
163    }
164
165    @Override
166    public Changeset getElementAt(int idx) {
167        return data.get(idx);
168    }
169
170    @Override
171    public int getSize() {
172        return data.size();
173    }
174
175    protected void sort() {
176        data.sort(Comparator.comparingInt(Changeset::getId).reversed());
177    }
178
179    /**
180     * Replies true if  there is at least one selected open changeset
181     *
182     * @return true if  there is at least one selected open changeset
183     */
184    public boolean hasSelectedOpenChangesets() {
185        return !getSelectedOpenChangesets().isEmpty();
186    }
187
188    /**
189     * Replies the selected open changesets
190     *
191     * @return the selected open changesets
192     */
193    public List<Changeset> getSelectedOpenChangesets() {
194        List<Changeset> ret = new ArrayList<>();
195        for (int i = 0; i < getSize(); i++) {
196            if (selectionModel.isSelectedIndex(i)) {
197                Changeset cs = data.get(i);
198                if (cs.isOpen()) {
199                    ret.add(cs);
200                }
201            }
202        }
203        return ret;
204    }
205
206    /* ---------------------------------------------------------------------------- */
207    /* Interface ChangesetCacheListener                                             */
208    /* ---------------------------------------------------------------------------- */
209    @Override
210    public void changesetCacheUpdated(ChangesetCacheEvent event) {
211        Set<Changeset> sel = getSelectedChangesets();
212        for (Changeset cs: event.getAddedChangesets()) {
213            int idx = data.indexOf(cs);
214            if (idx >= 0 && data.get(idx) != cs) {
215                data.get(idx).mergeFrom(cs);
216            }
217        }
218        for (Changeset cs: event.getUpdatedChangesets()) {
219            int idx = data.indexOf(cs);
220            if (idx >= 0 && data.get(idx) != cs) {
221                data.get(idx).mergeFrom(cs);
222            }
223        }
224        for (Changeset cs: event.getRemovedChangesets()) {
225            int idx = data.indexOf(cs);
226            if (idx >= 0) {
227                // replace with an incomplete changeset
228                data.set(idx, new Changeset(cs.getId()));
229            }
230        }
231        GuiHelper.runInEDT(() -> {
232            fireContentsChanged(this, 0, getSize());
233            setSelectedChangesets(sel);
234        });
235    }
236}