[2512] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
| 2 | package org.openstreetmap.josm.gui.conflict.pair.tags;
|
---|
| 3 |
|
---|
| 4 | import java.beans.PropertyChangeEvent;
|
---|
| 5 | import java.beans.PropertyChangeListener;
|
---|
| 6 | import java.util.ArrayList;
|
---|
| 7 | import java.util.HashSet;
|
---|
[2945] | 8 | import java.util.List;
|
---|
[2512] | 9 | import java.util.Set;
|
---|
| 10 |
|
---|
| 11 | import javax.swing.table.DefaultTableModel;
|
---|
| 12 |
|
---|
[6887] | 13 | import org.openstreetmap.josm.command.conflict.TagConflictResolveCommand;
|
---|
[3034] | 14 | import org.openstreetmap.josm.data.conflict.Conflict;
|
---|
[2512] | 15 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
| 16 | import org.openstreetmap.josm.gui.conflict.pair.MergeDecisionType;
|
---|
| 17 |
|
---|
| 18 | /**
|
---|
[5832] | 19 | * This is the {@link javax.swing.table.TableModel} used in the tables of the {@link TagMerger}.
|
---|
[2512] | 20 | *
|
---|
[5266] | 21 | * The model can {@link #populate(OsmPrimitive, OsmPrimitive)} itself from the conflicts
|
---|
| 22 | * in the tag sets of two {@link OsmPrimitive}s. Internally, it keeps a list of {@link TagMergeItem}s.
|
---|
[2512] | 23 | *
|
---|
[5266] | 24 | * {@link #decide(int, MergeDecisionType)} and {@link #decide(int[], MergeDecisionType)} can be used
|
---|
[2512] | 25 | * to remember a merge decision for a specific row in the model.
|
---|
| 26 | *
|
---|
[5266] | 27 | * The model notifies {@link PropertyChangeListener}s about updates of the number of
|
---|
| 28 | * undecided tags (see {@link #PROP_NUM_UNDECIDED_TAGS}).
|
---|
[2512] | 29 | *
|
---|
| 30 | */
|
---|
| 31 | public class TagMergeModel extends DefaultTableModel {
|
---|
[6883] | 32 | public static final String PROP_NUM_UNDECIDED_TAGS = TagMergeModel.class.getName() + ".numUndecidedTags";
|
---|
[2512] | 33 |
|
---|
| 34 | /** the list of tag merge items */
|
---|
[2945] | 35 | private final List<TagMergeItem> tagMergeItems;
|
---|
[2512] | 36 |
|
---|
| 37 | /** the property change listeners */
|
---|
[2945] | 38 | private final List<PropertyChangeListener> listeners;
|
---|
[2512] | 39 |
|
---|
| 40 | private int numUndecidedTags = 0;
|
---|
| 41 |
|
---|
| 42 | public TagMergeModel() {
|
---|
[7005] | 43 | tagMergeItems = new ArrayList<>();
|
---|
| 44 | listeners = new ArrayList<>();
|
---|
[2512] | 45 | }
|
---|
| 46 |
|
---|
| 47 | public void addPropertyChangeListener(PropertyChangeListener listener) {
|
---|
| 48 | synchronized(listeners) {
|
---|
| 49 | if (listener == null) return;
|
---|
| 50 | if (listeners.contains(listener)) return;
|
---|
| 51 | listeners.add(listener);
|
---|
| 52 | }
|
---|
| 53 | }
|
---|
| 54 |
|
---|
| 55 | public void removePropertyChangeListener(PropertyChangeListener listener) {
|
---|
| 56 | synchronized(listeners) {
|
---|
| 57 | if (listener == null) return;
|
---|
| 58 | if (!listeners.contains(listener)) return;
|
---|
| 59 | listeners.remove(listener);
|
---|
| 60 | }
|
---|
| 61 | }
|
---|
| 62 |
|
---|
| 63 | /**
|
---|
[5266] | 64 | * notifies {@link PropertyChangeListener}s about an update of {@link TagMergeModel#PROP_NUM_UNDECIDED_TAGS}
|
---|
[2512] | 65 |
|
---|
| 66 | * @param oldValue the old value
|
---|
| 67 | * @param newValue the new value
|
---|
| 68 | */
|
---|
| 69 | protected void fireNumUndecidedTagsChanged(int oldValue, int newValue) {
|
---|
| 70 | PropertyChangeEvent evt = new PropertyChangeEvent(this,PROP_NUM_UNDECIDED_TAGS,oldValue, newValue);
|
---|
| 71 | synchronized(listeners) {
|
---|
| 72 | for(PropertyChangeListener l : listeners) {
|
---|
| 73 | l.propertyChange(evt);
|
---|
| 74 | }
|
---|
| 75 | }
|
---|
| 76 | }
|
---|
| 77 |
|
---|
| 78 | /**
|
---|
| 79 | * refreshes the number of undecided tag conflicts after an update in the list of
|
---|
[5266] | 80 | * {@link TagMergeItem}s. Notifies {@link PropertyChangeListener} if necessary.
|
---|
[2512] | 81 | *
|
---|
| 82 | */
|
---|
| 83 | protected void refreshNumUndecidedTags() {
|
---|
| 84 | int newValue=0;
|
---|
| 85 | for(TagMergeItem item: tagMergeItems) {
|
---|
| 86 | if (MergeDecisionType.UNDECIDED.equals(item.getMergeDecision())) {
|
---|
| 87 | newValue++;
|
---|
| 88 | }
|
---|
| 89 | }
|
---|
| 90 | int oldValue = numUndecidedTags;
|
---|
| 91 | numUndecidedTags = newValue;
|
---|
| 92 | fireNumUndecidedTagsChanged(oldValue, numUndecidedTags);
|
---|
| 93 |
|
---|
| 94 | }
|
---|
| 95 |
|
---|
| 96 | /**
|
---|
| 97 | * Populate the model with conflicts between the tag sets of the two
|
---|
[5266] | 98 | * {@link OsmPrimitive} <code>my</code> and <code>their</code>.
|
---|
[2512] | 99 | *
|
---|
| 100 | * @param my my primitive (i.e. the primitive from the local dataset)
|
---|
| 101 | * @param their their primitive (i.e. the primitive from the server dataset)
|
---|
| 102 | *
|
---|
| 103 | */
|
---|
| 104 | public void populate(OsmPrimitive my, OsmPrimitive their) {
|
---|
| 105 | tagMergeItems.clear();
|
---|
[7005] | 106 | Set<String> keys = new HashSet<>();
|
---|
[2512] | 107 | keys.addAll(my.keySet());
|
---|
| 108 | keys.addAll(their.keySet());
|
---|
| 109 | for(String key : keys) {
|
---|
| 110 | String myValue = my.get(key);
|
---|
| 111 | String theirValue = their.get(key);
|
---|
| 112 | if (myValue == null || theirValue == null || ! myValue.equals(theirValue)) {
|
---|
| 113 | tagMergeItems.add(
|
---|
| 114 | new TagMergeItem(key, my, their)
|
---|
| 115 | );
|
---|
| 116 | }
|
---|
| 117 | }
|
---|
| 118 | fireTableDataChanged();
|
---|
| 119 | refreshNumUndecidedTags();
|
---|
| 120 | }
|
---|
| 121 |
|
---|
| 122 | /**
|
---|
[5266] | 123 | * add a {@link TagMergeItem} to the model
|
---|
[2512] | 124 | *
|
---|
| 125 | * @param item the item
|
---|
| 126 | */
|
---|
| 127 | public void addItem(TagMergeItem item) {
|
---|
| 128 | if (item != null) {
|
---|
| 129 | tagMergeItems.add(item);
|
---|
| 130 | fireTableDataChanged();
|
---|
| 131 | refreshNumUndecidedTags();
|
---|
| 132 | }
|
---|
| 133 | }
|
---|
| 134 |
|
---|
| 135 | protected void rememberDecision(int row, MergeDecisionType decision) {
|
---|
| 136 | TagMergeItem item = tagMergeItems.get(row);
|
---|
| 137 | item.decide(decision);
|
---|
| 138 | }
|
---|
| 139 |
|
---|
| 140 | /**
|
---|
[5266] | 141 | * set the merge decision of the {@link TagMergeItem} in row <code>row</code>
|
---|
[2512] | 142 | * to <code>decision</code>.
|
---|
| 143 | *
|
---|
[5832] | 144 | * @param row the row
|
---|
[2512] | 145 | * @param decision the decision
|
---|
| 146 | */
|
---|
| 147 | public void decide(int row, MergeDecisionType decision) {
|
---|
| 148 | rememberDecision(row, decision);
|
---|
| 149 | fireTableRowsUpdated(row, row);
|
---|
| 150 | refreshNumUndecidedTags();
|
---|
| 151 | }
|
---|
| 152 |
|
---|
| 153 | /**
|
---|
[5266] | 154 | * set the merge decision of all {@link TagMergeItem} given by indices in <code>rows</code>
|
---|
[2512] | 155 | * to <code>decision</code>.
|
---|
| 156 | *
|
---|
[5832] | 157 | * @param rows the array of row indices
|
---|
[2512] | 158 | * @param decision the decision
|
---|
| 159 | */
|
---|
| 160 | public void decide(int [] rows, MergeDecisionType decision) {
|
---|
| 161 | if (rows == null || rows.length == 0)
|
---|
| 162 | return;
|
---|
| 163 | for (int row : rows) {
|
---|
| 164 | rememberDecision(row, decision);
|
---|
| 165 | }
|
---|
| 166 | fireTableDataChanged();
|
---|
| 167 | refreshNumUndecidedTags();
|
---|
| 168 | }
|
---|
| 169 |
|
---|
| 170 | @Override
|
---|
| 171 | public int getRowCount() {
|
---|
| 172 | return tagMergeItems == null ? 0 : tagMergeItems.size();
|
---|
| 173 | }
|
---|
| 174 |
|
---|
| 175 | @Override
|
---|
| 176 | public Object getValueAt(int row, int column) {
|
---|
| 177 | // return the tagMergeItem for both columns. The cell
|
---|
| 178 | // renderer will dispatch on the column index and get
|
---|
| 179 | // the key or the value from the TagMergeItem
|
---|
| 180 | //
|
---|
| 181 | return tagMergeItems.get(row);
|
---|
| 182 | }
|
---|
| 183 |
|
---|
| 184 | @Override
|
---|
| 185 | public boolean isCellEditable(int row, int column) {
|
---|
| 186 | return false;
|
---|
| 187 | }
|
---|
| 188 |
|
---|
[3034] | 189 | public TagConflictResolveCommand buildResolveCommand(Conflict<? extends OsmPrimitive> conflict) {
|
---|
| 190 | return new TagConflictResolveCommand(conflict, tagMergeItems);
|
---|
[2512] | 191 | }
|
---|
| 192 |
|
---|
| 193 | public boolean isResolvedCompletely() {
|
---|
| 194 | for (TagMergeItem item: tagMergeItems) {
|
---|
| 195 | if (item.getMergeDecision().equals(MergeDecisionType.UNDECIDED))
|
---|
| 196 | return false;
|
---|
| 197 | }
|
---|
| 198 | return true;
|
---|
| 199 | }
|
---|
| 200 |
|
---|
| 201 | public int getNumResolvedConflicts() {
|
---|
| 202 | int n = 0;
|
---|
| 203 | for (TagMergeItem item: tagMergeItems) {
|
---|
| 204 | if (!item.getMergeDecision().equals(MergeDecisionType.UNDECIDED)) {
|
---|
| 205 | n++;
|
---|
| 206 | }
|
---|
| 207 | }
|
---|
| 208 | return n;
|
---|
| 209 |
|
---|
| 210 | }
|
---|
[2945] | 211 |
|
---|
| 212 | public int getFirstUndecided(int startIndex) {
|
---|
| 213 | for (int i=startIndex; i<tagMergeItems.size(); i++) {
|
---|
| 214 | if (tagMergeItems.get(i).getMergeDecision() == MergeDecisionType.UNDECIDED)
|
---|
| 215 | return i;
|
---|
| 216 | }
|
---|
| 217 | return -1;
|
---|
| 218 | }
|
---|
[2512] | 219 | }
|
---|