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

Last change on this file since 16438 was 16438, checked in by simon04, 4 years ago

see #19251 - Java 8: use Stream

  • Property svn:eol-style set to native
File size: 17.7 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.event.ActionEvent;
7import java.text.DecimalFormat;
8import java.util.Arrays;
9import java.util.List;
10import java.util.stream.Collectors;
11
12import javax.swing.AbstractAction;
13import javax.swing.Action;
14import javax.swing.BorderFactory;
15import javax.swing.JButton;
16import javax.swing.JComponent;
17import javax.swing.JLabel;
18import javax.swing.JPanel;
19import javax.swing.event.ChangeEvent;
20import javax.swing.event.ChangeListener;
21
22import org.openstreetmap.josm.data.conflict.Conflict;
23import org.openstreetmap.josm.data.coor.LatLon;
24import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
25import org.openstreetmap.josm.data.osm.OsmPrimitive;
26import org.openstreetmap.josm.gui.conflict.ConflictColors;
27import org.openstreetmap.josm.gui.conflict.pair.AbstractMergePanel;
28import org.openstreetmap.josm.gui.conflict.pair.IConflictResolver;
29import org.openstreetmap.josm.gui.conflict.pair.MergeDecisionType;
30import org.openstreetmap.josm.gui.history.VersionInfoPanel;
31import org.openstreetmap.josm.tools.GBC;
32import org.openstreetmap.josm.tools.ImageProvider;
33import org.openstreetmap.josm.tools.Utils;
34
35/**
36 * This class represents a UI component for resolving conflicts in some properties of {@link OsmPrimitive}.
37 * @since 1654
38 */
39public class PropertiesMerger extends AbstractMergePanel implements ChangeListener, IConflictResolver {
40 private static final DecimalFormat COORD_FORMATTER = new DecimalFormat("###0.0000000");
41
42 private final JLabel lblMyCoordinates = buildValueLabel("label.mycoordinates");
43 private final JLabel lblMergedCoordinates = buildValueLabel("label.mergedcoordinates");
44 private final JLabel lblTheirCoordinates = buildValueLabel("label.theircoordinates");
45
46 private final JLabel lblMyDeletedState = buildValueLabel("label.mydeletedstate");
47 private final JLabel lblMergedDeletedState = buildValueLabel("label.mergeddeletedstate");
48 private final JLabel lblTheirDeletedState = buildValueLabel("label.theirdeletedstate");
49
50 private final JLabel lblMyReferrers = buildValueLabel("label.myreferrers");
51 private final JLabel lblTheirReferrers = buildValueLabel("label.theirreferrers");
52
53 private final transient PropertiesMergeModel model = new PropertiesMergeModel();
54 private final VersionInfoPanel mineVersionInfo = new VersionInfoPanel();
55 private final VersionInfoPanel theirVersionInfo = new VersionInfoPanel();
56
57 /**
58 * Constructs a new {@code PropertiesMerger}.
59 */
60 public PropertiesMerger() {
61 model.addChangeListener(this);
62 buildRows();
63 }
64
65 @Override
66 protected List<? extends MergeRow> getRows() {
67 return Arrays.asList(
68 new AbstractMergePanel.TitleRow(),
69 new VersionInfoRow(),
70 new MergeCoordinatesRow(),
71 new UndecideCoordinatesRow(),
72 new MergeDeletedStateRow(),
73 new UndecideDeletedStateRow(),
74 new ReferrersRow(),
75 new EmptyFillRow());
76 }
77
78 protected static JLabel buildValueLabel(String name) {
79 JLabel lbl = new JLabel();
80 lbl.setName(name);
81 lbl.setHorizontalAlignment(JLabel.CENTER);
82 lbl.setOpaque(true);
83 lbl.setBorder(BorderFactory.createLoweredBevelBorder());
84 return lbl;
85 }
86
87 protected static String coordToString(LatLon coord) {
88 if (coord == null)
89 return tr("(none)");
90 StringBuilder sb = new StringBuilder();
91 sb.append('(')
92 .append(COORD_FORMATTER.format(coord.lat()))
93 .append(',')
94 .append(COORD_FORMATTER.format(coord.lon()))
95 .append(')');
96 return sb.toString();
97 }
98
99 protected static String deletedStateToString(Boolean deleted) {
100 if (deleted == null)
101 return tr("(none)");
102 if (deleted)
103 return tr("deleted");
104 else
105 return tr("not deleted");
106 }
107
108 protected static String referrersToString(List<OsmPrimitive> referrers) {
109 if (referrers.isEmpty())
110 return tr("(none)");
111 return referrers.stream()
112 .map(r -> Utils.escapeReservedCharactersHTML(r.getDisplayName(DefaultNameFormatter.getInstance())) + "<br>")
113 .collect(Collectors.joining("", "<html>", "</html>"));
114 }
115
116 protected void updateCoordinates() {
117 lblMyCoordinates.setText(coordToString(model.getMyCoords()));
118 lblMergedCoordinates.setText(coordToString(model.getMergedCoords()));
119 lblTheirCoordinates.setText(coordToString(model.getTheirCoords()));
120 if (!model.hasCoordConflict()) {
121 lblMyCoordinates.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
122 lblMergedCoordinates.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
123 lblTheirCoordinates.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
124 } else {
125 if (!model.isDecidedCoord()) {
126 lblMyCoordinates.setBackground(ConflictColors.BGCOLOR_UNDECIDED.get());
127 lblMergedCoordinates.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
128 lblTheirCoordinates.setBackground(ConflictColors.BGCOLOR_UNDECIDED.get());
129 } else {
130 lblMyCoordinates.setBackground(
131 model.isCoordMergeDecision(MergeDecisionType.KEEP_MINE)
132 ? ConflictColors.BGCOLOR_DECIDED.get() : ConflictColors.BGCOLOR_NO_CONFLICT.get()
133 );
134 lblMergedCoordinates.setBackground(ConflictColors.BGCOLOR_DECIDED.get());
135 lblTheirCoordinates.setBackground(
136 model.isCoordMergeDecision(MergeDecisionType.KEEP_THEIR)
137 ? ConflictColors.BGCOLOR_DECIDED.get() : ConflictColors.BGCOLOR_NO_CONFLICT.get()
138 );
139 }
140 }
141 }
142
143 protected void updateDeletedState() {
144 lblMyDeletedState.setText(deletedStateToString(model.getMyDeletedState()));
145 lblMergedDeletedState.setText(deletedStateToString(model.getMergedDeletedState()));
146 lblTheirDeletedState.setText(deletedStateToString(model.getTheirDeletedState()));
147
148 if (!model.hasDeletedStateConflict()) {
149 lblMyDeletedState.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
150 lblMergedDeletedState.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
151 lblTheirDeletedState.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
152 } else {
153 if (!model.isDecidedDeletedState()) {
154 lblMyDeletedState.setBackground(ConflictColors.BGCOLOR_UNDECIDED.get());
155 lblMergedDeletedState.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
156 lblTheirDeletedState.setBackground(ConflictColors.BGCOLOR_UNDECIDED.get());
157 } else {
158 lblMyDeletedState.setBackground(
159 model.isDeletedStateDecision(MergeDecisionType.KEEP_MINE)
160 ? ConflictColors.BGCOLOR_DECIDED.get() : ConflictColors.BGCOLOR_NO_CONFLICT.get()
161 );
162 lblMergedDeletedState.setBackground(ConflictColors.BGCOLOR_DECIDED.get());
163 lblTheirDeletedState.setBackground(
164 model.isDeletedStateDecision(MergeDecisionType.KEEP_THEIR)
165 ? ConflictColors.BGCOLOR_DECIDED.get() : ConflictColors.BGCOLOR_NO_CONFLICT.get()
166 );
167 }
168 }
169 }
170
171 protected void updateReferrers() {
172 lblMyReferrers.setText(referrersToString(model.getMyReferrers()));
173 lblMyReferrers.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
174 lblTheirReferrers.setText(referrersToString(model.getTheirReferrers()));
175 lblTheirReferrers.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
176 }
177
178 @Override
179 public void stateChanged(ChangeEvent e) {
180 updateCoordinates();
181 updateDeletedState();
182 updateReferrers();
183 }
184
185 /**
186 * Returns properties merge model.
187 * @return properties merge model
188 */
189 public PropertiesMergeModel getModel() {
190 return model;
191 }
192
193 private final class MergeDeletedStateRow extends AbstractMergePanel.MergeRow {
194 @Override
195 protected JComponent rowTitle() {
196 return new JLabel(tr("Deleted State:"));
197 }
198
199 @Override
200 protected JComponent mineField() {
201 return lblMyDeletedState;
202 }
203
204 @Override
205 protected JComponent mineButton() {
206 KeepMyDeletedStateAction actKeepMyDeletedState = new KeepMyDeletedStateAction();
207 model.addChangeListener(actKeepMyDeletedState);
208 JButton btnKeepMyDeletedState = new JButton(actKeepMyDeletedState);
209 btnKeepMyDeletedState.setName("button.keepmydeletedstate");
210 return btnKeepMyDeletedState;
211 }
212
213 @Override
214 protected JComponent merged() {
215 return lblMergedDeletedState;
216 }
217
218 @Override
219 protected JComponent theirsButton() {
220 KeepTheirDeletedStateAction actKeepTheirDeletedState = new KeepTheirDeletedStateAction();
221 model.addChangeListener(actKeepTheirDeletedState);
222 JButton btnKeepTheirDeletedState = new JButton(actKeepTheirDeletedState);
223 btnKeepTheirDeletedState.setName("button.keeptheirdeletedstate");
224 return btnKeepTheirDeletedState;
225 }
226
227 @Override
228 protected JComponent theirsField() {
229 return lblTheirDeletedState;
230 }
231 }
232
233 private final class MergeCoordinatesRow extends AbstractMergePanel.MergeRow {
234 @Override
235 protected JComponent rowTitle() {
236 return new JLabel(tr("Coordinates:"));
237 }
238
239 @Override
240 protected JComponent mineField() {
241 return lblMyCoordinates;
242 }
243
244 @Override
245 protected JComponent mineButton() {
246 KeepMyCoordinatesAction actKeepMyCoordinates = new KeepMyCoordinatesAction();
247 model.addChangeListener(actKeepMyCoordinates);
248 JButton btnKeepMyCoordinates = new JButton(actKeepMyCoordinates);
249 btnKeepMyCoordinates.setName("button.keepmycoordinates");
250 return btnKeepMyCoordinates;
251 }
252
253 @Override
254 protected JComponent merged() {
255 return lblMergedCoordinates;
256 }
257
258 @Override
259 protected JComponent theirsButton() {
260 KeepTheirCoordinatesAction actKeepTheirCoordinates = new KeepTheirCoordinatesAction();
261 model.addChangeListener(actKeepTheirCoordinates);
262 JButton btnKeepTheirCoordinates = new JButton(actKeepTheirCoordinates);
263 btnKeepTheirCoordinates.setName("button.keeptheircoordinates");
264 return btnKeepTheirCoordinates;
265 }
266
267 @Override
268 protected JComponent theirsField() {
269 return lblTheirCoordinates;
270 }
271 }
272
273 private final class UndecideCoordinatesRow extends AbstractUndecideRow {
274 @Override
275 protected UndecideCoordinateConflictAction createAction() {
276 UndecideCoordinateConflictAction action = new UndecideCoordinateConflictAction();
277 model.addChangeListener(action);
278 return action;
279 }
280
281 @Override
282 protected String getButtonName() {
283 return "button.undecidecoordinates";
284 }
285 }
286
287 private final class UndecideDeletedStateRow extends AbstractUndecideRow {
288 @Override
289 protected UndecideDeletedStateConflictAction createAction() {
290 UndecideDeletedStateConflictAction action = new UndecideDeletedStateConflictAction();
291 model.addChangeListener(action);
292 return action;
293 }
294
295 @Override
296 protected String getButtonName() {
297 return "button.undecidedeletedstate";
298 }
299 }
300
301 private final class VersionInfoRow extends AbstractMergePanel.MergeRowWithoutButton {
302 @Override
303 protected JComponent mineField() {
304 return mineVersionInfo;
305 }
306
307 @Override
308 protected JComponent theirsField() {
309 return theirVersionInfo;
310 }
311 }
312
313 private final class ReferrersRow extends AbstractMergePanel.MergeRow {
314 @Override
315 protected JComponent rowTitle() {
316 return new JLabel(tr("Referenced by:"));
317 }
318
319 @Override
320 protected JComponent mineField() {
321 return lblMyReferrers;
322 }
323
324 @Override
325 protected JComponent theirsField() {
326 return lblTheirReferrers;
327 }
328 }
329
330 private static final class EmptyFillRow extends AbstractMergePanel.MergeRow {
331 @Override
332 protected JComponent merged() {
333 return new JPanel();
334 }
335
336 @Override
337 protected void addConstraints(GBC constraints, int columnIndex) {
338 super.addConstraints(constraints, columnIndex);
339 // fill to bottom
340 constraints.weighty = 1;
341 }
342 }
343
344 class KeepMyCoordinatesAction extends AbstractAction implements ChangeListener {
345 KeepMyCoordinatesAction() {
346 new ImageProvider("dialogs/conflict", "tagkeepmine").getResource().attachImageIcon(this, true);
347 putValue(Action.SHORT_DESCRIPTION, tr("Keep my coordinates"));
348 }
349
350 @Override
351 public void actionPerformed(ActionEvent e) {
352 model.decideCoordsConflict(MergeDecisionType.KEEP_MINE);
353 }
354
355 @Override
356 public void stateChanged(ChangeEvent e) {
357 setEnabled(model.hasCoordConflict() && !model.isDecidedCoord() && model.getMyCoords() != null);
358 }
359 }
360
361 class KeepTheirCoordinatesAction extends AbstractAction implements ChangeListener {
362 KeepTheirCoordinatesAction() {
363 new ImageProvider("dialogs/conflict", "tagkeeptheir").getResource().attachImageIcon(this, true);
364 putValue(Action.SHORT_DESCRIPTION, tr("Keep their coordinates"));
365 }
366
367 @Override
368 public void actionPerformed(ActionEvent e) {
369 model.decideCoordsConflict(MergeDecisionType.KEEP_THEIR);
370 }
371
372 @Override
373 public void stateChanged(ChangeEvent e) {
374 setEnabled(model.hasCoordConflict() && !model.isDecidedCoord() && model.getTheirCoords() != null);
375 }
376 }
377
378 class UndecideCoordinateConflictAction extends AbstractAction implements ChangeListener {
379 UndecideCoordinateConflictAction() {
380 new ImageProvider("dialogs/conflict", "tagundecide").getResource().attachImageIcon(this, true);
381 putValue(Action.SHORT_DESCRIPTION, tr("Undecide conflict between different coordinates"));
382 }
383
384 @Override
385 public void actionPerformed(ActionEvent e) {
386 model.decideCoordsConflict(MergeDecisionType.UNDECIDED);
387 }
388
389 @Override
390 public void stateChanged(ChangeEvent e) {
391 setEnabled(model.hasCoordConflict() && model.isDecidedCoord());
392 }
393 }
394
395 class KeepMyDeletedStateAction extends AbstractAction implements ChangeListener {
396 KeepMyDeletedStateAction() {
397 new ImageProvider("dialogs/conflict", "tagkeepmine").getResource().attachImageIcon(this, true);
398 putValue(Action.SHORT_DESCRIPTION, tr("Keep my deleted state"));
399 }
400
401 @Override
402 public void actionPerformed(ActionEvent e) {
403 model.decideDeletedStateConflict(MergeDecisionType.KEEP_MINE);
404 }
405
406 @Override
407 public void stateChanged(ChangeEvent e) {
408 setEnabled(model.hasDeletedStateConflict() && !model.isDecidedDeletedState());
409 }
410 }
411
412 class KeepTheirDeletedStateAction extends AbstractAction implements ChangeListener {
413 KeepTheirDeletedStateAction() {
414 new ImageProvider("dialogs/conflict", "tagkeeptheir").getResource().attachImageIcon(this, true);
415 putValue(Action.SHORT_DESCRIPTION, tr("Keep their deleted state"));
416 }
417
418 @Override
419 public void actionPerformed(ActionEvent e) {
420 model.decideDeletedStateConflict(MergeDecisionType.KEEP_THEIR);
421 }
422
423 @Override
424 public void stateChanged(ChangeEvent e) {
425 setEnabled(model.hasDeletedStateConflict() && !model.isDecidedDeletedState());
426 }
427 }
428
429 class UndecideDeletedStateConflictAction extends AbstractAction implements ChangeListener {
430 UndecideDeletedStateConflictAction() {
431 new ImageProvider("dialogs/conflict", "tagundecide").getResource().attachImageIcon(this, true);
432 putValue(Action.SHORT_DESCRIPTION, tr("Undecide conflict between deleted state"));
433 }
434
435 @Override
436 public void actionPerformed(ActionEvent e) {
437 model.decideDeletedStateConflict(MergeDecisionType.UNDECIDED);
438 }
439
440 @Override
441 public void stateChanged(ChangeEvent e) {
442 setEnabled(model.hasDeletedStateConflict() && model.isDecidedDeletedState());
443 }
444 }
445
446 @Override
447 public void deletePrimitive(boolean deleted) {
448 if (deleted) {
449 if (model.getMergedCoords() == null) {
450 model.decideCoordsConflict(MergeDecisionType.KEEP_MINE);
451 }
452 } else {
453 model.decideCoordsConflict(MergeDecisionType.UNDECIDED);
454 }
455 }
456
457 @Override
458 public void populate(Conflict<? extends OsmPrimitive> conflict) {
459 model.populate(conflict);
460 mineVersionInfo.update(conflict.getMy(), true);
461 theirVersionInfo.update(conflict.getTheir(), false);
462 }
463
464 @Override
465 public void decideRemaining(MergeDecisionType decision) {
466 if (!model.isDecidedCoord()) {
467 model.decideDeletedStateConflict(decision);
468 }
469 if (!model.isDecidedCoord()) {
470 model.decideCoordsConflict(decision);
471 }
472 }
473}
Note: See TracBrowser for help on using the repository browser.