001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs.changeset;
003
004import java.util.ArrayList;
005import java.util.HashSet;
006import java.util.Iterator;
007import java.util.List;
008import java.util.Set;
009
010import javax.swing.DefaultListSelectionModel;
011import javax.swing.table.AbstractTableModel;
012
013import org.openstreetmap.josm.data.osm.ChangesetDataSet;
014import org.openstreetmap.josm.data.osm.ChangesetDataSet.ChangesetDataSetEntry;
015import org.openstreetmap.josm.data.osm.ChangesetDataSet.ChangesetModificationType;
016import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
017
018/**
019 * This is the table model for the content of a changeset.
020 *
021 */
022public class ChangesetContentTableModel extends AbstractTableModel {
023
024    private final transient List<ChangesetContentEntry> data = new ArrayList<>();
025    private final DefaultListSelectionModel selectionModel;
026
027    public ChangesetContentTableModel(DefaultListSelectionModel selectionModel) {
028        this.selectionModel = selectionModel;
029    }
030
031    /**
032     * Replies true if there is at least one selected primitive in the table model
033     *
034     * @return true if there is at least one selected primitive in the table model
035     */
036    public boolean hasSelectedPrimitives() {
037        return selectionModel.getMinSelectionIndex() >= 0;
038    }
039
040    public void setSelectedByIdx(int row) {
041        selectionModel.setSelectionInterval(row, row);
042    }
043
044    /**
045     * Replies the selection model
046     * @return the selection model
047     */
048    public DefaultListSelectionModel getSelectionModel() {
049        return selectionModel;
050    }
051
052    public Set<HistoryOsmPrimitive> getSelectedPrimitives() {
053        Set<HistoryOsmPrimitive> ret = new HashSet<>();
054        for (int i = 0; i < data.size(); i++) {
055            if (selectionModel.isSelectedIndex(i)) {
056                ret.add(data.get(i).getPrimitive());
057            }
058        }
059        return ret;
060    }
061
062    /**
063     * Populates the model with the content of a changeset. If ds is null, the table is cleared.
064     *
065     * @param ds the changeset content.
066     */
067    public void populate(ChangesetDataSet ds) {
068        this.data.clear();
069        if (ds == null) {
070            fireTableDataChanged();
071            return;
072        }
073        for (Iterator<ChangesetDataSetEntry> it = ds.iterator(); it.hasNext();) {
074            data.add(new ChangesetContentEntry(it.next()));
075        }
076        sort();
077        fireTableDataChanged();
078    }
079
080    protected void sort() {
081        data.sort((c1, c2) -> {
082                if (c1.getModificationType().equals(c2.getModificationType())) {
083                    long id1 = c1.getPrimitive().getId();
084                    long id2 = c2.getPrimitive().getId();
085
086                    if (id1 == id2)
087                        return 0;
088                    else if (id1 < id2)
089                        return -1;
090                    return 1;
091                }
092                switch(c1.getModificationType()) {
093                case CREATED: return -1;
094                case UPDATED:
095                    switch(c2.getModificationType()) {
096                    case CREATED: return 1;
097                    default: return -1;
098                    }
099                case DELETED:
100                    return 1;
101                }
102                // should not happen
103                return 0;
104            }
105        );
106    }
107
108    /* -------------------------------------------------------------- */
109    /* interface TableModel                                           */
110    /* -------------------------------------------------------------- */
111    @Override
112    public int getColumnCount() {
113        return 3;
114    }
115
116    @Override
117    public int getRowCount() {
118        return data.size();
119    }
120
121    @Override
122    public Object getValueAt(int row, int col) {
123        switch(col) {
124        case 0: return data.get(row).getModificationType();
125        default: return data.get(row).getPrimitive();
126        }
127    }
128
129    /**
130     * The type used internally to keep information about {@link HistoryOsmPrimitive}
131     * with their {@link ChangesetModificationType}.
132     *
133     */
134    private static class ChangesetContentEntry implements ChangesetDataSetEntry {
135        private final ChangesetModificationType modificationType;
136        private final HistoryOsmPrimitive primitive;
137
138        ChangesetContentEntry(ChangesetModificationType modificationType, HistoryOsmPrimitive primitive) {
139            this.modificationType = modificationType;
140            this.primitive = primitive;
141        }
142
143        ChangesetContentEntry(ChangesetDataSetEntry entry) {
144            this(entry.getModificationType(), entry.getPrimitive());
145        }
146
147        @Override
148        public ChangesetModificationType getModificationType() {
149            return modificationType;
150        }
151
152        @Override
153        public HistoryOsmPrimitive getPrimitive() {
154            return primitive;
155        }
156    }
157}