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