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