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