001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.changeset; 003 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.Comparator; 007import java.util.HashSet; 008import java.util.List; 009import java.util.Set; 010 011import javax.swing.DefaultListModel; 012import javax.swing.DefaultListSelectionModel; 013 014import org.openstreetmap.josm.data.osm.Changeset; 015import org.openstreetmap.josm.data.osm.ChangesetCache; 016import org.openstreetmap.josm.data.osm.ChangesetCacheEvent; 017import org.openstreetmap.josm.data.osm.ChangesetCacheListener; 018import org.openstreetmap.josm.data.osm.DataSet; 019import org.openstreetmap.josm.data.osm.OsmPrimitive; 020import org.openstreetmap.josm.data.osm.Storage; 021 022public class ChangesetListModel extends DefaultListModel<Changeset> implements ChangesetCacheListener { 023 private final transient List<Changeset> data = new ArrayList<>(); 024 private final transient Storage<Changeset> shownChangesets = new Storage<>(true); 025 private final DefaultListSelectionModel selectionModel; 026 027 public ChangesetListModel(DefaultListSelectionModel selectionModel) { 028 this.selectionModel = selectionModel; 029 } 030 031 public Set<Changeset> getSelectedChangesets() { 032 Set<Changeset> ret = new HashSet<>(); 033 for (int i = 0; i < getSize(); i++) { 034 if (selectionModel.isSelectedIndex(i)) { 035 ret.add(data.get(i)); 036 } 037 } 038 return ret; 039 } 040 041 public Set<Integer> getSelectedChangesetIds() { 042 Set<Integer> ret = new HashSet<>(); 043 for (int i = 0; i < getSize(); i++) { 044 if (selectionModel.isSelectedIndex(i)) { 045 ret.add(data.get(i).getId()); 046 } 047 } 048 return ret; 049 } 050 051 public void setSelectedChangesets(Collection<Changeset> changesets) { 052 selectionModel.clearSelection(); 053 if (changesets == null) return; 054 for (Changeset cs: changesets) { 055 int idx = data.indexOf(cs); 056 if (idx < 0) { 057 continue; 058 } 059 selectionModel.addSelectionInterval(idx, idx); 060 } 061 } 062 063 protected void setChangesets(Collection<Changeset> changesets) { 064 shownChangesets.clear(); 065 if (changesets != null) { 066 shownChangesets.addAll(changesets); 067 } 068 updateModel(); 069 } 070 071 private void updateModel() { 072 Set<Changeset> sel = getSelectedChangesets(); 073 data.clear(); 074 data.addAll(shownChangesets); 075 ChangesetCache cache = ChangesetCache.getInstance(); 076 for (Changeset cs: data) { 077 if (cache.contains(cs) && cache.get(cs.getId()) != cs) { 078 cs.mergeFrom(cache.get(cs.getId())); 079 } 080 } 081 sort(); 082 fireIntervalAdded(this, 0, getSize()); 083 setSelectedChangesets(sel); 084 } 085 086 public void initFromChangesetIds(Collection<Integer> ids) { 087 if (ids == null || ids.isEmpty()) { 088 setChangesets(null); 089 return; 090 } 091 Set<Changeset> changesets = new HashSet<>(ids.size()); 092 for (int id: ids) { 093 if (id <= 0) { 094 continue; 095 } 096 changesets.add(new Changeset(id)); 097 } 098 setChangesets(changesets); 099 } 100 101 public void initFromPrimitives(Collection<? extends OsmPrimitive> primitives) { 102 if (primitives == null) { 103 setChangesets(null); 104 return; 105 } 106 Set<Changeset> changesets = new HashSet<>(); 107 for (OsmPrimitive p: primitives) { 108 if (p.getChangesetId() <= 0) { 109 continue; 110 } 111 changesets.add(new Changeset(p.getChangesetId())); 112 } 113 setChangesets(changesets); 114 } 115 116 public void initFromDataSet(DataSet ds) { 117 if (ds == null) { 118 setChangesets(null); 119 return; 120 } 121 Set<Changeset> changesets = new HashSet<>(); 122 for (OsmPrimitive p: ds.allPrimitives()) { 123 if (p.getChangesetId() <= 0) { 124 continue; 125 } 126 changesets.add(new Changeset(p.getChangesetId())); 127 } 128 setChangesets(changesets); 129 } 130 131 @Override 132 public Changeset getElementAt(int idx) { 133 return data.get(idx); 134 } 135 136 @Override 137 public int getSize() { 138 return data.size(); 139 } 140 141 protected void sort() { 142 data.sort(Comparator.comparingInt(Changeset::getId).reversed()); 143 } 144 145 /** 146 * Replies true if there is at least one selected open changeset 147 * 148 * @return true if there is at least one selected open changeset 149 */ 150 public boolean hasSelectedOpenChangesets() { 151 return !getSelectedOpenChangesets().isEmpty(); 152 } 153 154 /** 155 * Replies the selected open changesets 156 * 157 * @return the selected open changesets 158 */ 159 public List<Changeset> getSelectedOpenChangesets() { 160 List<Changeset> ret = new ArrayList<>(); 161 for (int i = 0; i < getSize(); i++) { 162 if (selectionModel.isSelectedIndex(i)) { 163 Changeset cs = data.get(i); 164 if (cs.isOpen()) { 165 ret.add(cs); 166 } 167 } 168 } 169 return ret; 170 } 171 172 /* ---------------------------------------------------------------------------- */ 173 /* Interface ChangesetCacheListener */ 174 /* ---------------------------------------------------------------------------- */ 175 @Override 176 public void changesetCacheUpdated(ChangesetCacheEvent event) { 177 Set<Changeset> sel = getSelectedChangesets(); 178 for (Changeset cs: event.getAddedChangesets()) { 179 int idx = data.indexOf(cs); 180 if (idx >= 0 && data.get(idx) != cs) { 181 data.get(idx).mergeFrom(cs); 182 } 183 } 184 for (Changeset cs: event.getUpdatedChangesets()) { 185 int idx = data.indexOf(cs); 186 if (idx >= 0 && data.get(idx) != cs) { 187 data.get(idx).mergeFrom(cs); 188 } 189 } 190 for (Changeset cs: event.getRemovedChangesets()) { 191 int idx = data.indexOf(cs); 192 if (idx >= 0) { 193 // replace with an incomplete changeset 194 data.set(idx, new Changeset(cs.getId())); 195 } 196 } 197 fireContentsChanged(this, 0, getSize()); 198 setSelectedChangesets(sel); 199 } 200}