source: josm/trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagConflictResolverModel.java@ 7642

Last change on this file since 7642 was 7642, checked in by Don-vip, 9 years ago

fix #10156 - combine ways: conflicts not shown when no conflicting values (regression from r6802)

  • Property svn:eol-style set to native
File size: 9.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.conflict.tags;
3
4import java.beans.PropertyChangeListener;
5import java.beans.PropertyChangeSupport;
6import java.util.ArrayList;
7import java.util.Collections;
8import java.util.Comparator;
9import java.util.HashMap;
10import java.util.HashSet;
11import java.util.List;
12import java.util.Map;
13import java.util.Set;
14
15import javax.swing.table.DefaultTableModel;
16
17import org.openstreetmap.josm.data.osm.TagCollection;
18import org.openstreetmap.josm.gui.util.GuiHelper;
19import org.openstreetmap.josm.tools.CheckParameterUtil;
20
21public class TagConflictResolverModel extends DefaultTableModel {
22 public static final String NUM_CONFLICTS_PROP = TagConflictResolverModel.class.getName() + ".numConflicts";
23
24 private TagCollection tags;
25 private List<String> displayedKeys;
26 private Set<String> keysWithConflicts;
27 private Map<String, MultiValueResolutionDecision> decisions;
28 private int numConflicts;
29 private PropertyChangeSupport support;
30 private boolean showTagsWithConflictsOnly = false;
31 private boolean showTagsWithMultiValuesOnly = false;
32
33 /**
34 * Constructs a new {@code TagConflictResolverModel}.
35 */
36 public TagConflictResolverModel() {
37 numConflicts = 0;
38 support = new PropertyChangeSupport(this);
39 }
40
41 public void addPropertyChangeListener(PropertyChangeListener listener) {
42 support.addPropertyChangeListener(listener);
43 }
44
45 public void removePropertyChangeListener(PropertyChangeListener listener) {
46 support.removePropertyChangeListener(listener);
47 }
48
49 protected void setNumConflicts(int numConflicts) {
50 int oldValue = this.numConflicts;
51 this.numConflicts = numConflicts;
52 if (oldValue != this.numConflicts) {
53 support.firePropertyChange(NUM_CONFLICTS_PROP, oldValue, this.numConflicts);
54 }
55 }
56
57 protected void refreshNumConflicts() {
58 int count = 0;
59 for (MultiValueResolutionDecision d : decisions.values()) {
60 if (!d.isDecided()) {
61 count++;
62 }
63 }
64 setNumConflicts(count);
65 }
66
67 protected void sort() {
68 Collections.sort(
69 displayedKeys,
70 new Comparator<String>() {
71 @Override
72 public int compare(String key1, String key2) {
73 if (decisions.get(key1).isDecided() && ! decisions.get(key2).isDecided())
74 return 1;
75 else if (!decisions.get(key1).isDecided() && decisions.get(key2).isDecided())
76 return -1;
77 return key1.compareTo(key2);
78 }
79 }
80 );
81 }
82
83 /**
84 * initializes the model from the current tags
85 *
86 */
87 public void rebuild() {
88 if (tags == null) return;
89 for(String key: tags.getKeys()) {
90 MultiValueResolutionDecision decision = new MultiValueResolutionDecision(tags.getTagsFor(key));
91 if (decisions.get(key) == null) {
92 decisions.put(key,decision);
93 }
94 }
95 displayedKeys.clear();
96 Set<String> keys = tags.getKeys();
97 if (showTagsWithConflictsOnly) {
98 keys.retainAll(keysWithConflicts);
99 if (showTagsWithMultiValuesOnly) {
100 Set<String> keysWithMultiValues = new HashSet<>();
101 for (String key: keys) {
102 if (decisions.get(key).canKeepAll()) {
103 keysWithMultiValues.add(key);
104 }
105 }
106 keys.retainAll(keysWithMultiValues);
107 }
108 for (String key: tags.getKeys()) {
109 if (!decisions.get(key).isDecided() && !keys.contains(key)) {
110 keys.add(key);
111 }
112 }
113 }
114 displayedKeys.addAll(keys);
115 refreshNumConflicts();
116 sort();
117 GuiHelper.runInEDTAndWait(new Runnable() {
118 @Override public void run() {
119 fireTableDataChanged();
120 }
121 });
122 }
123
124 /**
125 * Populates the model with the tags for which conflicts are to be resolved.
126 *
127 * @param tags the tag collection with the tags. Must not be null.
128 * @param keysWithConflicts the set of tag keys with conflicts
129 * @throws IllegalArgumentException thrown if tags is null
130 */
131 public void populate(TagCollection tags, Set<String> keysWithConflicts) {
132 CheckParameterUtil.ensureParameterNotNull(tags, "tags");
133 this.tags = tags;
134 displayedKeys = new ArrayList<>();
135 this.keysWithConflicts = keysWithConflicts == null ? new HashSet<String>() : keysWithConflicts;
136 decisions = new HashMap<>();
137 rebuild();
138 }
139
140 /**
141 * Returns the OSM key at the given row.
142 * @param row The table row
143 * @return the OSM key at the given row.
144 * @since 6616
145 */
146 public final String getKey(int row) {
147 return displayedKeys.get(row);
148 }
149
150 @Override
151 public int getRowCount() {
152 if (displayedKeys == null) return 0;
153 return displayedKeys.size();
154 }
155
156 @Override
157 public Object getValueAt(int row, int column) {
158 return getDecision(row);
159 }
160
161 @Override
162 public boolean isCellEditable(int row, int column) {
163 return column == 2;
164 }
165
166 @Override
167 public void setValueAt(Object value, int row, int column) {
168 MultiValueResolutionDecision decision = getDecision(row);
169 if (value instanceof String) {
170 decision.keepOne((String)value);
171 } else if (value instanceof MultiValueDecisionType) {
172 MultiValueDecisionType type = (MultiValueDecisionType)value;
173 switch(type) {
174 case KEEP_NONE:
175 decision.keepNone();
176 break;
177 case KEEP_ALL:
178 decision.keepAll();
179 break;
180 }
181 }
182 GuiHelper.runInEDTAndWait(new Runnable() {
183 @Override public void run() {
184 fireTableDataChanged();
185 }
186 });
187 refreshNumConflicts();
188 }
189
190 /**
191 * Replies true if each {@link MultiValueResolutionDecision} is decided.
192 *
193 * @return true if each {@link MultiValueResolutionDecision} is decided; false otherwise
194 */
195 public boolean isResolvedCompletely() {
196 return numConflicts == 0 && keysWithConflicts.isEmpty();
197 }
198
199 public int getNumConflicts() {
200 return numConflicts;
201 }
202
203 public int getNumDecisions() {
204 return decisions == null ? 0 : decisions.size();
205 }
206
207 //TODO Should this method work with all decisions or only with displayed decisions? For MergeNodes it should be
208 //all decisions, but this method is also used on other places, so I've made new method just for MergeNodes
209 public TagCollection getResolution() {
210 TagCollection tc = new TagCollection();
211 for (String key: displayedKeys) {
212 tc.add(decisions.get(key).getResolution());
213 }
214 return tc;
215 }
216
217 public TagCollection getAllResolutions() {
218 TagCollection tc = new TagCollection();
219 for (MultiValueResolutionDecision value: decisions.values()) {
220 tc.add(value.getResolution());
221 }
222 return tc;
223 }
224
225 /**
226 * Returns the conflict resolution decision at the given row.
227 * @param row The table row
228 * @return the conflict resolution decision at the given row.
229 */
230 public MultiValueResolutionDecision getDecision(int row) {
231 return decisions.get(getKey(row));
232 }
233
234 /**
235 * Sets whether all tags or only tags with conflicts are displayed
236 *
237 * @param showTagsWithConflictsOnly if true, only tags with conflicts are displayed
238 */
239 public void setShowTagsWithConflictsOnly(boolean showTagsWithConflictsOnly) {
240 this.showTagsWithConflictsOnly = showTagsWithConflictsOnly;
241 rebuild();
242 }
243
244 /**
245 * Sets whether all conflicts or only conflicts with multiple values are displayed
246 *
247 * @param showTagsWithMultiValuesOnly if true, only tags with multiple values are displayed
248 */
249 public void setShowTagsWithMultiValuesOnly(boolean showTagsWithMultiValuesOnly) {
250 this.showTagsWithMultiValuesOnly = showTagsWithMultiValuesOnly;
251 rebuild();
252 }
253
254 /**
255 * Prepare the default decisions for the current model
256 *
257 */
258 public void prepareDefaultTagDecisions() {
259 for (MultiValueResolutionDecision decision: decisions.values()) {
260 List<String> values = decision.getValues();
261 values.remove("");
262 if (values.size() == 1) {
263 // TODO: Do not suggest to keep the single value in order to avoid long highways to become tunnels+bridges+... (only if both primitives are tagged)
264 decision.keepOne(values.get(0));
265 } else {
266 // Do not suggest to keep all values in order to reduce the wrong usage of semicolon values, see #9104!
267 }
268 }
269 rebuild();
270 }
271
272 /**
273 * Returns the set of keys in conflict.
274 * @return the set of keys in conflict.
275 * @since 6616
276 */
277 public final Set<String> getKeysWithConflicts() {
278 return new HashSet<>(keysWithConflicts);
279 }
280}
Note: See TracBrowser for help on using the repository browser.