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        selectionModel.setValueIsAdjusting(true);
107        selectionModel.clearSelection();
108        if (selected != null) {
109            for (Changeset cs: selected) {
110                final int idx = data.indexOf(cs);
111                if (idx >= 0) {
112                    selectionModel.addSelectionInterval(idx, idx);
113                }
114            }
115        }
116        GuiHelper.runInEDTAndWait(() -> selectionModel.setValueIsAdjusting(false));
117    }
118
119    @Override
120    public int getColumnCount() {
121        return 5;
122    }
123
124    @Override
125    public int getRowCount() {
126        return data.size();
127    }
128
129    @Override
130    public Object getValueAt(int row, int column) {
131        return data.get(row);
132    }
133
134    public void init() {
135        ChangesetCache cc = ChangesetCache.getInstance();
136        List<Changeset> selected = getSelectedChangesets();
137        data.clear();
138        data.addAll(cc.getChangesets());
139        sort();
140        fireTableDataChanged();
141        setSelectedChangesets(selected);
142
143        cc.addChangesetCacheListener(this);
144    }
145
146    public void tearDown() {
147        ChangesetCache.getInstance().removeChangesetCacheListener(this);
148    }
149
150    public DefaultListSelectionModel getSelectionModel() {
151        return selectionModel;
152    }
153
154    protected void sort() {
155        data.sort(Comparator.comparingInt(Changeset::getId).reversed());
156    }
157
158    /* ------------------------------------------------------------------------------ */
159    /* interface ChangesetCacheListener                                               */
160    /* ------------------------------------------------------------------------------ */
161    @Override
162    public void changesetCacheUpdated(ChangesetCacheEvent event) {
163        List<Changeset> selected = getSelectedChangesets();
164        for (Changeset cs: event.getAddedChangesets()) {
165            data.add(cs);
166        }
167        for (Changeset cs: event.getRemovedChangesets()) {
168            data.remove(cs);
169        }
170        for (Changeset cs: event.getUpdatedChangesets()) {
171            int idx = data.indexOf(cs);
172            if (idx >= 0) {
173                Changeset mine = data.get(idx);
174                if (mine != cs) {
175                    mine.mergeFrom(cs);
176                }
177            }
178        }
179        sort();
180        fireTableDataChanged();
181        setSelectedChangesets(selected);
182    }
183}