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