source: josm/trunk/src/org/openstreetmap/josm/gui/conflict/pair/properties/PropertiesMerger.java@ 9996

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

sonar - use JLabel.setLabelFor where applicable + add/update unit tests

  • Property svn:eol-style set to native
File size: 19.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.conflict.pair.properties;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.GridBagConstraints;
7import java.awt.GridBagLayout;
8import java.awt.Insets;
9import java.awt.event.ActionEvent;
10import java.text.DecimalFormat;
11import java.util.List;
12import java.util.Observable;
13import java.util.Observer;
14
15import javax.swing.AbstractAction;
16import javax.swing.Action;
17import javax.swing.BorderFactory;
18import javax.swing.JButton;
19import javax.swing.JLabel;
20import javax.swing.JPanel;
21
22import org.openstreetmap.josm.data.conflict.Conflict;
23import org.openstreetmap.josm.data.coor.LatLon;
24import org.openstreetmap.josm.data.osm.OsmPrimitive;
25import org.openstreetmap.josm.gui.DefaultNameFormatter;
26import org.openstreetmap.josm.gui.conflict.ConflictColors;
27import org.openstreetmap.josm.gui.conflict.pair.IConflictResolver;
28import org.openstreetmap.josm.gui.conflict.pair.MergeDecisionType;
29import org.openstreetmap.josm.gui.history.VersionInfoPanel;
30import org.openstreetmap.josm.tools.ImageProvider;
31
32/**
33 * This class represents a UI component for resolving conflicts in some properties of {@link OsmPrimitive}.
34 * @since 1654
35 */
36public class PropertiesMerger extends JPanel implements Observer, IConflictResolver {
37 private static final DecimalFormat COORD_FORMATTER = new DecimalFormat("###0.0000000");
38
39 private JLabel lblMyCoordinates;
40 private JLabel lblMergedCoordinates;
41 private JLabel lblTheirCoordinates;
42
43 private JLabel lblMyDeletedState;
44 private JLabel lblMergedDeletedState;
45 private JLabel lblTheirDeletedState;
46
47 private JLabel lblMyReferrers;
48 private JLabel lblTheirReferrers;
49
50 private final transient PropertiesMergeModel model;
51 private final VersionInfoPanel mineVersionInfo = new VersionInfoPanel();
52 private final VersionInfoPanel theirVersionInfo = new VersionInfoPanel();
53
54 /**
55 * Constructs a new {@code PropertiesMerger}.
56 */
57 public PropertiesMerger() {
58 model = new PropertiesMergeModel();
59 model.addObserver(this);
60 build();
61 }
62
63 protected JLabel buildValueLabel(String name) {
64 JLabel lbl = new JLabel();
65 lbl.setName(name);
66 lbl.setHorizontalAlignment(JLabel.CENTER);
67 lbl.setOpaque(true);
68 lbl.setBorder(BorderFactory.createLoweredBevelBorder());
69 return lbl;
70 }
71
72 protected void buildHeaderRow() {
73 GridBagConstraints gc = new GridBagConstraints();
74
75 gc.gridx = 1;
76 gc.gridy = 0;
77 gc.gridwidth = 1;
78 gc.gridheight = 1;
79 gc.fill = GridBagConstraints.NONE;
80 gc.anchor = GridBagConstraints.CENTER;
81 gc.weightx = 0.0;
82 gc.weighty = 0.0;
83 gc.insets = new Insets(10, 0, 0, 0);
84 JLabel lblMyVersion = new JLabel(tr("My version"));
85 lblMyVersion.setToolTipText(tr("Properties in my dataset, i.e. the local dataset"));
86 lblMyVersion.setLabelFor(mineVersionInfo);
87 add(lblMyVersion, gc);
88
89 gc.gridx = 3;
90 JLabel lblMergedVersion = new JLabel(tr("Merged version"));
91 lblMergedVersion.setToolTipText(
92 tr("Properties in the merged element. They will replace properties in my elements when merge decisions are applied."));
93 add(lblMergedVersion, gc);
94
95 gc.gridx = 5;
96 JLabel lblTheirVersion = new JLabel(tr("Their version"));
97 lblTheirVersion.setToolTipText(tr("Properties in their dataset, i.e. the server dataset"));
98 lblMyVersion.setLabelFor(theirVersionInfo);
99 add(lblTheirVersion, gc);
100
101 gc.gridx = 1;
102 gc.gridy = 1;
103 gc.fill = GridBagConstraints.HORIZONTAL;
104 gc.anchor = GridBagConstraints.LINE_START;
105 gc.insets = new Insets(0, 0, 20, 0);
106 add(mineVersionInfo, gc);
107
108 gc.gridx = 5;
109 add(theirVersionInfo, gc);
110 }
111
112 protected void buildCoordinateConflictRows() {
113 GridBagConstraints gc = new GridBagConstraints();
114
115 gc.gridx = 0;
116 gc.gridy = 2;
117 gc.gridwidth = 1;
118 gc.gridheight = 1;
119 gc.fill = GridBagConstraints.HORIZONTAL;
120 gc.anchor = GridBagConstraints.LINE_START;
121 gc.weightx = 0.0;
122 gc.weighty = 0.0;
123 gc.insets = new Insets(0, 5, 0, 5);
124 add(new JLabel(tr("Coordinates:")), gc);
125
126 gc.gridx = 1;
127 gc.fill = GridBagConstraints.BOTH;
128 gc.anchor = GridBagConstraints.CENTER;
129 gc.weightx = 0.33;
130 gc.weighty = 0.0;
131 add(lblMyCoordinates = buildValueLabel("label.mycoordinates"), gc);
132
133 gc.gridx = 2;
134 gc.fill = GridBagConstraints.NONE;
135 gc.anchor = GridBagConstraints.CENTER;
136 gc.weightx = 0.0;
137 gc.weighty = 0.0;
138 KeepMyCoordinatesAction actKeepMyCoordinates = new KeepMyCoordinatesAction();
139 model.addObserver(actKeepMyCoordinates);
140 JButton btnKeepMyCoordinates = new JButton(actKeepMyCoordinates);
141 btnKeepMyCoordinates.setName("button.keepmycoordinates");
142 add(btnKeepMyCoordinates, gc);
143
144 gc.gridx = 3;
145 gc.fill = GridBagConstraints.BOTH;
146 gc.anchor = GridBagConstraints.CENTER;
147 gc.weightx = 0.33;
148 gc.weighty = 0.0;
149 add(lblMergedCoordinates = buildValueLabel("label.mergedcoordinates"), gc);
150
151 gc.gridx = 4;
152 gc.fill = GridBagConstraints.NONE;
153 gc.anchor = GridBagConstraints.CENTER;
154 gc.weightx = 0.0;
155 gc.weighty = 0.0;
156 KeepTheirCoordinatesAction actKeepTheirCoordinates = new KeepTheirCoordinatesAction();
157 model.addObserver(actKeepTheirCoordinates);
158 JButton btnKeepTheirCoordinates = new JButton(actKeepTheirCoordinates);
159 add(btnKeepTheirCoordinates, gc);
160
161 gc.gridx = 5;
162 gc.fill = GridBagConstraints.BOTH;
163 gc.anchor = GridBagConstraints.CENTER;
164 gc.weightx = 0.33;
165 gc.weighty = 0.0;
166 add(lblTheirCoordinates = buildValueLabel("label.theircoordinates"), gc);
167
168 // ---------------------------------------------------
169 gc.gridx = 3;
170 gc.gridy = 3;
171 gc.fill = GridBagConstraints.NONE;
172 gc.anchor = GridBagConstraints.CENTER;
173 gc.weightx = 0.0;
174 gc.weighty = 0.0;
175 UndecideCoordinateConflictAction actUndecideCoordinates = new UndecideCoordinateConflictAction();
176 model.addObserver(actUndecideCoordinates);
177 JButton btnUndecideCoordinates = new JButton(actUndecideCoordinates);
178 add(btnUndecideCoordinates, gc);
179 }
180
181 protected void buildDeletedStateConflictRows() {
182 GridBagConstraints gc = new GridBagConstraints();
183
184 gc.gridx = 0;
185 gc.gridy = 4;
186 gc.gridwidth = 1;
187 gc.gridheight = 1;
188 gc.fill = GridBagConstraints.BOTH;
189 gc.anchor = GridBagConstraints.LINE_START;
190 gc.weightx = 0.0;
191 gc.weighty = 0.0;
192 gc.insets = new Insets(0, 5, 0, 5);
193 add(new JLabel(tr("Deleted State:")), gc);
194
195 gc.gridx = 1;
196 gc.fill = GridBagConstraints.BOTH;
197 gc.anchor = GridBagConstraints.CENTER;
198 gc.weightx = 0.33;
199 gc.weighty = 0.0;
200 add(lblMyDeletedState = buildValueLabel("label.mydeletedstate"), gc);
201
202 gc.gridx = 2;
203 gc.fill = GridBagConstraints.NONE;
204 gc.anchor = GridBagConstraints.CENTER;
205 gc.weightx = 0.0;
206 gc.weighty = 0.0;
207 KeepMyDeletedStateAction actKeepMyDeletedState = new KeepMyDeletedStateAction();
208 model.addObserver(actKeepMyDeletedState);
209 JButton btnKeepMyDeletedState = new JButton(actKeepMyDeletedState);
210 btnKeepMyDeletedState.setName("button.keepmydeletedstate");
211 add(btnKeepMyDeletedState, gc);
212
213 gc.gridx = 3;
214 gc.fill = GridBagConstraints.BOTH;
215 gc.anchor = GridBagConstraints.CENTER;
216 gc.weightx = 0.33;
217 gc.weighty = 0.0;
218 add(lblMergedDeletedState = buildValueLabel("label.mergeddeletedstate"), gc);
219
220 gc.gridx = 4;
221 gc.fill = GridBagConstraints.NONE;
222 gc.anchor = GridBagConstraints.CENTER;
223 gc.weightx = 0.0;
224 gc.weighty = 0.0;
225 KeepTheirDeletedStateAction actKeepTheirDeletedState = new KeepTheirDeletedStateAction();
226 model.addObserver(actKeepTheirDeletedState);
227 JButton btnKeepTheirDeletedState = new JButton(actKeepTheirDeletedState);
228 btnKeepTheirDeletedState.setName("button.keeptheirdeletedstate");
229 add(btnKeepTheirDeletedState, gc);
230
231 gc.gridx = 5;
232 gc.fill = GridBagConstraints.BOTH;
233 gc.anchor = GridBagConstraints.CENTER;
234 gc.weightx = 0.33;
235 gc.weighty = 0.0;
236 add(lblTheirDeletedState = buildValueLabel("label.theirdeletedstate"), gc);
237
238 // ---------------------------------------------------
239 gc.gridx = 3;
240 gc.gridy = 5;
241 gc.fill = GridBagConstraints.NONE;
242 gc.anchor = GridBagConstraints.CENTER;
243 gc.weightx = 0.0;
244 gc.weighty = 0.0;
245 UndecideDeletedStateConflictAction actUndecideDeletedState = new UndecideDeletedStateConflictAction();
246 model.addObserver(actUndecideDeletedState);
247 JButton btnUndecideDeletedState = new JButton(actUndecideDeletedState);
248 btnUndecideDeletedState.setName("button.undecidedeletedstate");
249 add(btnUndecideDeletedState, gc);
250 }
251
252 protected void buildReferrersRow() {
253 GridBagConstraints gc = new GridBagConstraints();
254
255 gc.gridx = 0;
256 gc.gridy = 7;
257 gc.gridwidth = 1;
258 gc.gridheight = 1;
259 gc.fill = GridBagConstraints.BOTH;
260 gc.anchor = GridBagConstraints.LINE_START;
261 gc.weightx = 0.0;
262 gc.weighty = 0.0;
263 gc.insets = new Insets(0, 5, 0, 5);
264 add(new JLabel(tr("Referenced by:")), gc);
265
266 gc.gridx = 1;
267 gc.gridy = 7;
268 gc.fill = GridBagConstraints.BOTH;
269 gc.anchor = GridBagConstraints.CENTER;
270 gc.weightx = 0.33;
271 gc.weighty = 0.0;
272 add(lblMyReferrers = buildValueLabel("label.myreferrers"), gc);
273
274 gc.gridx = 5;
275 gc.gridy = 7;
276 gc.fill = GridBagConstraints.BOTH;
277 gc.anchor = GridBagConstraints.CENTER;
278 gc.weightx = 0.33;
279 gc.weighty = 0.0;
280 add(lblTheirReferrers = buildValueLabel("label.theirreferrers"), gc);
281 }
282
283 protected final void build() {
284 setLayout(new GridBagLayout());
285 buildHeaderRow();
286 buildCoordinateConflictRows();
287 buildDeletedStateConflictRows();
288 buildReferrersRow();
289 }
290
291 public String coordToString(LatLon coord) {
292 if (coord == null)
293 return tr("(none)");
294 StringBuilder sb = new StringBuilder();
295 sb.append('(')
296 .append(COORD_FORMATTER.format(coord.lat()))
297 .append(',')
298 .append(COORD_FORMATTER.format(coord.lon()))
299 .append(')');
300 return sb.toString();
301 }
302
303 public String deletedStateToString(Boolean deleted) {
304 if (deleted == null)
305 return tr("(none)");
306 if (deleted)
307 return tr("deleted");
308 else
309 return tr("not deleted");
310 }
311
312 public String referrersToString(List<OsmPrimitive> referrers) {
313 if (referrers.isEmpty())
314 return tr("(none)");
315 StringBuilder str = new StringBuilder("<html>");
316 for (OsmPrimitive r: referrers) {
317 str.append(r.getDisplayName(DefaultNameFormatter.getInstance())).append("<br>");
318 }
319 str.append("</html>");
320 return str.toString();
321 }
322
323 protected void updateCoordinates() {
324 lblMyCoordinates.setText(coordToString(model.getMyCoords()));
325 lblMergedCoordinates.setText(coordToString(model.getMergedCoords()));
326 lblTheirCoordinates.setText(coordToString(model.getTheirCoords()));
327 if (!model.hasCoordConflict()) {
328 lblMyCoordinates.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
329 lblMergedCoordinates.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
330 lblTheirCoordinates.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
331 } else {
332 if (!model.isDecidedCoord()) {
333 lblMyCoordinates.setBackground(ConflictColors.BGCOLOR_UNDECIDED.get());
334 lblMergedCoordinates.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
335 lblTheirCoordinates.setBackground(ConflictColors.BGCOLOR_UNDECIDED.get());
336 } else {
337 lblMyCoordinates.setBackground(
338 model.isCoordMergeDecision(MergeDecisionType.KEEP_MINE)
339 ? ConflictColors.BGCOLOR_DECIDED.get() : ConflictColors.BGCOLOR_NO_CONFLICT.get()
340 );
341 lblMergedCoordinates.setBackground(ConflictColors.BGCOLOR_DECIDED.get());
342 lblTheirCoordinates.setBackground(
343 model.isCoordMergeDecision(MergeDecisionType.KEEP_THEIR)
344 ? ConflictColors.BGCOLOR_DECIDED.get() : ConflictColors.BGCOLOR_NO_CONFLICT.get()
345 );
346 }
347 }
348 }
349
350 protected void updateDeletedState() {
351 lblMyDeletedState.setText(deletedStateToString(model.getMyDeletedState()));
352 lblMergedDeletedState.setText(deletedStateToString(model.getMergedDeletedState()));
353 lblTheirDeletedState.setText(deletedStateToString(model.getTheirDeletedState()));
354
355 if (!model.hasDeletedStateConflict()) {
356 lblMyDeletedState.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
357 lblMergedDeletedState.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
358 lblTheirDeletedState.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
359 } else {
360 if (!model.isDecidedDeletedState()) {
361 lblMyDeletedState.setBackground(ConflictColors.BGCOLOR_UNDECIDED.get());
362 lblMergedDeletedState.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
363 lblTheirDeletedState.setBackground(ConflictColors.BGCOLOR_UNDECIDED.get());
364 } else {
365 lblMyDeletedState.setBackground(
366 model.isDeletedStateDecision(MergeDecisionType.KEEP_MINE)
367 ? ConflictColors.BGCOLOR_DECIDED.get() : ConflictColors.BGCOLOR_NO_CONFLICT.get()
368 );
369 lblMergedDeletedState.setBackground(ConflictColors.BGCOLOR_DECIDED.get());
370 lblTheirDeletedState.setBackground(
371 model.isDeletedStateDecision(MergeDecisionType.KEEP_THEIR)
372 ? ConflictColors.BGCOLOR_DECIDED.get() : ConflictColors.BGCOLOR_NO_CONFLICT.get()
373 );
374 }
375 }
376 }
377
378 protected void updateReferrers() {
379 lblMyReferrers.setText(referrersToString(model.getMyReferrers()));
380 lblMyReferrers.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
381 lblTheirReferrers.setText(referrersToString(model.getTheirReferrers()));
382 lblTheirReferrers.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
383 }
384
385 @Override
386 public void update(Observable o, Object arg) {
387 updateCoordinates();
388 updateDeletedState();
389 updateReferrers();
390 }
391
392 public PropertiesMergeModel getModel() {
393 return model;
394 }
395
396 class KeepMyCoordinatesAction extends AbstractAction implements Observer {
397 KeepMyCoordinatesAction() {
398 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeepmine"));
399 putValue(Action.SHORT_DESCRIPTION, tr("Keep my coordinates"));
400 }
401
402 @Override
403 public void actionPerformed(ActionEvent e) {
404 model.decideCoordsConflict(MergeDecisionType.KEEP_MINE);
405 }
406
407 @Override
408 public void update(Observable o, Object arg) {
409 setEnabled(model.hasCoordConflict() && !model.isDecidedCoord() && model.getMyCoords() != null);
410 }
411 }
412
413 class KeepTheirCoordinatesAction extends AbstractAction implements Observer {
414 KeepTheirCoordinatesAction() {
415 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeeptheir"));
416 putValue(Action.SHORT_DESCRIPTION, tr("Keep their coordinates"));
417 }
418
419 @Override
420 public void actionPerformed(ActionEvent e) {
421 model.decideCoordsConflict(MergeDecisionType.KEEP_THEIR);
422 }
423
424 @Override
425 public void update(Observable o, Object arg) {
426 setEnabled(model.hasCoordConflict() && !model.isDecidedCoord() && model.getTheirCoords() != null);
427 }
428 }
429
430 class UndecideCoordinateConflictAction extends AbstractAction implements Observer {
431 UndecideCoordinateConflictAction() {
432 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagundecide"));
433 putValue(Action.SHORT_DESCRIPTION, tr("Undecide conflict between different coordinates"));
434 }
435
436 @Override
437 public void actionPerformed(ActionEvent e) {
438 model.decideCoordsConflict(MergeDecisionType.UNDECIDED);
439 }
440
441 @Override
442 public void update(Observable o, Object arg) {
443 setEnabled(model.hasCoordConflict() && model.isDecidedCoord());
444 }
445 }
446
447 class KeepMyDeletedStateAction extends AbstractAction implements Observer {
448 KeepMyDeletedStateAction() {
449 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeepmine"));
450 putValue(Action.SHORT_DESCRIPTION, tr("Keep my deleted state"));
451 }
452
453 @Override
454 public void actionPerformed(ActionEvent e) {
455 model.decideDeletedStateConflict(MergeDecisionType.KEEP_MINE);
456 }
457
458 @Override
459 public void update(Observable o, Object arg) {
460 setEnabled(model.hasDeletedStateConflict() && !model.isDecidedDeletedState());
461 }
462 }
463
464 class KeepTheirDeletedStateAction extends AbstractAction implements Observer {
465 KeepTheirDeletedStateAction() {
466 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeeptheir"));
467 putValue(Action.SHORT_DESCRIPTION, tr("Keep their deleted state"));
468 }
469
470 @Override
471 public void actionPerformed(ActionEvent e) {
472 model.decideDeletedStateConflict(MergeDecisionType.KEEP_THEIR);
473 }
474
475 @Override
476 public void update(Observable o, Object arg) {
477 setEnabled(model.hasDeletedStateConflict() && !model.isDecidedDeletedState());
478 }
479 }
480
481 class UndecideDeletedStateConflictAction extends AbstractAction implements Observer {
482 UndecideDeletedStateConflictAction() {
483 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagundecide"));
484 putValue(Action.SHORT_DESCRIPTION, tr("Undecide conflict between deleted state"));
485 }
486
487 @Override
488 public void actionPerformed(ActionEvent e) {
489 model.decideDeletedStateConflict(MergeDecisionType.UNDECIDED);
490 }
491
492 @Override
493 public void update(Observable o, Object arg) {
494 setEnabled(model.hasDeletedStateConflict() && model.isDecidedDeletedState());
495 }
496 }
497
498 @Override
499 public void deletePrimitive(boolean deleted) {
500 if (deleted) {
501 if (model.getMergedCoords() == null) {
502 model.decideCoordsConflict(MergeDecisionType.KEEP_MINE);
503 }
504 } else {
505 model.decideCoordsConflict(MergeDecisionType.UNDECIDED);
506 }
507 }
508
509 @Override
510 public void populate(Conflict<? extends OsmPrimitive> conflict) {
511 model.populate(conflict);
512 mineVersionInfo.update(conflict.getMy(), true);
513 theirVersionInfo.update(conflict.getTheir(), false);
514 }
515
516 @Override
517 public void decideRemaining(MergeDecisionType decision) {
518 if (!model.isDecidedCoord()) {
519 model.decideDeletedStateConflict(decision);
520 }
521 if (!model.isDecidedCoord()) {
522 model.decideCoordsConflict(decision);
523 }
524 }
525}
Note: See TracBrowser for help on using the repository browser.