001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs.changeset;
003
004import java.beans.PropertyChangeListener;
005import java.beans.PropertyChangeSupport;
006import java.util.ArrayList;
007import java.util.Collection;
008import java.util.Comparator;
009import java.util.List;
010import java.util.Set;
011import java.util.stream.Collectors;
012import java.util.stream.IntStream;
013
014import javax.swing.DefaultListSelectionModel;
015import javax.swing.table.AbstractTableModel;
016
017import org.openstreetmap.josm.data.osm.Changeset;
018import org.openstreetmap.josm.data.osm.ChangesetCache;
019import org.openstreetmap.josm.data.osm.ChangesetCacheEvent;
020import org.openstreetmap.josm.data.osm.ChangesetCacheListener;
021import org.openstreetmap.josm.gui.util.GuiHelper;
022import org.openstreetmap.josm.gui.util.TableHelper;
023
024/**
025 * This is the model for the changeset cache manager dialog.
026 */
027public class ChangesetCacheManagerModel extends AbstractTableModel implements ChangesetCacheListener {
028
029    /** the name of the property for the currently selected changeset in the detail view */
030    public static final String CHANGESET_IN_DETAIL_VIEW_PROP = ChangesetCacheManagerModel.class.getName() + ".changesetInDetailView";
031
032    private final transient List<Changeset> data = new ArrayList<>();
033    private final DefaultListSelectionModel selectionModel;
034    private transient Changeset changesetInDetailView;
035    private final PropertyChangeSupport support = new PropertyChangeSupport(this);
036
037    /**
038     * Creates a new ChangesetCacheManagerModel that is based on the selectionModel
039     * @param selectionModel A new selection model that should be used.
040     */
041    public ChangesetCacheManagerModel(DefaultListSelectionModel selectionModel) {
042        this.selectionModel = selectionModel;
043    }
044
045    /**
046     * Adds a property change listener to this model.
047     * @param listener The listener
048     */
049    public void addPropertyChangeListener(PropertyChangeListener listener) {
050        support.addPropertyChangeListener(listener);
051    }
052
053    /**
054     * Removes a property change listener from this model.
055     * @param listener The listener
056     */
057    public void removePropertyChangeListener(PropertyChangeListener listener) {
058        support.removePropertyChangeListener(listener);
059    }
060
061    /**
062     * Sets the changeset currently displayed in the detail view. Fires a property change event
063     * for the property {@link #CHANGESET_IN_DETAIL_VIEW_PROP} if necessary.
064     *
065     * @param cs the changeset currently displayed in the detail view.
066     */
067    public void setChangesetInDetailView(Changeset cs) {
068        Changeset oldValue = changesetInDetailView;
069        changesetInDetailView = cs;
070        if (oldValue != cs) {
071            support.firePropertyChange(CHANGESET_IN_DETAIL_VIEW_PROP, oldValue, changesetInDetailView);
072        }
073    }
074
075    /**
076     * Replies true if there is at least one selected changeset
077     *
078     * @return true if there is at least one selected changeset
079     */
080    public boolean hasSelectedChangesets() {
081        return selectionModel.getMinSelectionIndex() >= 0;
082    }
083
084    /**
085     * Replies the list of selected changesets
086     *
087     * @return the list of selected changesets
088     */
089    public List<Changeset> getSelectedChangesets() {
090        List<Changeset> ret = new ArrayList<>();
091        for (int i = 0; i < data.size(); i++) {
092            Changeset cs = data.get(i);
093            if (selectionModel.isSelectedIndex(i)) {
094                ret.add(cs);
095            }
096        }
097        return ret;
098    }
099
100    /**
101     * Replies a set of ids of the selected changesets
102     *
103     * @return a set of ids of the selected changesets
104     */
105    public Set<Integer> getSelectedChangesetIds() {
106        return getSelectedChangesets().stream().map(Changeset::getId).collect(Collectors.toSet());
107    }
108
109    /**
110     * Selects the changesets in <code>selected</code>.
111     *
112     * @param selected the collection of changesets to select. Ignored if empty.
113     */
114    public void setSelectedChangesets(Collection<Changeset> selected) {
115        GuiHelper.runInEDTAndWait(() -> TableHelper.setSelectedIndices(selectionModel,
116                selected != null ? selected.stream().mapToInt(data::indexOf) : IntStream.empty()));
117    }
118
119    @Override
120    public int getColumnCount() {
121        return 8;
122    }
123
124    @Override
125    public int getRowCount() {
126        return data.size();
127    }
128
129    @Override
130    public Changeset getValueAt(int row, int column) {
131        return data.get(row);
132    }
133
134    /**
135     * Initializes the data that is displayed using the changeset cache.
136     */
137    public void init() {
138        ChangesetCache cc = ChangesetCache.getInstance();
139        List<Changeset> selected = getSelectedChangesets();
140        data.clear();
141        data.addAll(cc.getChangesets());
142        sort();
143        fireTableDataChanged();
144        setSelectedChangesets(selected);
145
146        cc.addChangesetCacheListener(this);
147    }
148
149    /**
150     * Destroys and unregisters this model.
151     */
152    public void tearDown() {
153        ChangesetCache.getInstance().removeChangesetCacheListener(this);
154    }
155
156    /**
157     * Gets the selection model this table is based on.
158     * @return The selection model.
159     */
160    public DefaultListSelectionModel getSelectionModel() {
161        return selectionModel;
162    }
163
164    protected void sort() {
165        data.sort(Comparator.comparingInt(Changeset::getId).reversed());
166    }
167
168    /* ------------------------------------------------------------------------------ */
169    /* interface ChangesetCacheListener                                               */
170    /* ------------------------------------------------------------------------------ */
171    @Override
172    public void changesetCacheUpdated(ChangesetCacheEvent event) {
173        List<Changeset> selected = getSelectedChangesets();
174        data.addAll(event.getAddedChangesets());
175        data.removeAll(event.getRemovedChangesets());
176        for (Changeset cs: event.getUpdatedChangesets()) {
177            int idx = data.indexOf(cs);
178            if (idx >= 0) {
179                Changeset mine = data.get(idx);
180                if (mine != cs) {
181                    mine.mergeFrom(cs);
182                }
183            }
184        }
185        GuiHelper.runInEDT(() -> {
186            sort();
187            fireTableDataChanged();
188            setSelectedChangesets(selected);
189        });
190    }
191}