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

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

see #8465 - use diamond operator where applicable

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