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

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

fix #10775 - allow to sum all numeric values as conflict resolution of capacity

  • Property svn:eol-style set to native
File size: 9.3 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 case SUM_ALL_NUMERIC:
181 decision.sumAllNumeric();
182 break;
183 }
184 }
185 GuiHelper.runInEDTAndWait(new Runnable() {
186 @Override public void run() {
187 fireTableDataChanged();
188 }
189 });
190 refreshNumConflicts();
191 }
192
193 /**
194 * Replies true if each {@link MultiValueResolutionDecision} is decided.
195 *
196 * @return true if each {@link MultiValueResolutionDecision} is decided; false otherwise
197 */
198 public boolean isResolvedCompletely() {
199 return numConflicts == 0 && keysWithConflicts != null && keysWithConflicts.isEmpty();
200 }
201
202 public int getNumConflicts() {
203 return numConflicts;
204 }
205
206 public int getNumDecisions() {
207 return decisions == null ? 0 : decisions.size();
208 }
209
210 //TODO Should this method work with all decisions or only with displayed decisions? For MergeNodes it should be
211 //all decisions, but this method is also used on other places, so I've made new method just for MergeNodes
212 public TagCollection getResolution() {
213 TagCollection tc = new TagCollection();
214 for (String key: displayedKeys) {
215 tc.add(decisions.get(key).getResolution());
216 }
217 return tc;
218 }
219
220 public TagCollection getAllResolutions() {
221 TagCollection tc = new TagCollection();
222 for (MultiValueResolutionDecision value: decisions.values()) {
223 tc.add(value.getResolution());
224 }
225 return tc;
226 }
227
228 /**
229 * Returns the conflict resolution decision at the given row.
230 * @param row The table row
231 * @return the conflict resolution decision at the given row.
232 */
233 public MultiValueResolutionDecision getDecision(int row) {
234 return decisions.get(getKey(row));
235 }
236
237 /**
238 * Sets whether all tags or only tags with conflicts are displayed
239 *
240 * @param showTagsWithConflictsOnly if true, only tags with conflicts are displayed
241 */
242 public void setShowTagsWithConflictsOnly(boolean showTagsWithConflictsOnly) {
243 this.showTagsWithConflictsOnly = showTagsWithConflictsOnly;
244 rebuild();
245 }
246
247 /**
248 * Sets whether all conflicts or only conflicts with multiple values are displayed
249 *
250 * @param showTagsWithMultiValuesOnly if true, only tags with multiple values are displayed
251 */
252 public void setShowTagsWithMultiValuesOnly(boolean showTagsWithMultiValuesOnly) {
253 this.showTagsWithMultiValuesOnly = showTagsWithMultiValuesOnly;
254 rebuild();
255 }
256
257 /**
258 * Prepare the default decisions for the current model
259 *
260 */
261 public void prepareDefaultTagDecisions() {
262 for (MultiValueResolutionDecision decision: decisions.values()) {
263 List<String> values = decision.getValues();
264 values.remove("");
265 if (values.size() == 1) {
266 // 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)
267 decision.keepOne(values.get(0));
268 } else {
269 // Do not suggest to keep all values in order to reduce the wrong usage of semicolon values, see #9104!
270 }
271 }
272 rebuild();
273 }
274
275 /**
276 * Returns the set of keys in conflict.
277 * @return the set of keys in conflict.
278 * @since 6616
279 */
280 public final Set<String> getKeysWithConflicts() {
281 return new HashSet<>(keysWithConflicts);
282 }
283}
Note: See TracBrowser for help on using the repository browser.