source: josm/trunk/src/org/openstreetmap/josm/actions/MergeLayerAction.java@ 12048

Last change on this file since 12048 was 11885, checked in by Don-vip, 7 years ago

add basic unit tests for MergeLayerAction + improve javadoc, code cleanup

  • Property svn:eol-style set to native
File size: 6.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.event.ActionEvent;
8import java.awt.event.KeyEvent;
9import java.util.Collection;
10import java.util.Collections;
11import java.util.List;
12import java.util.concurrent.Future;
13
14import org.openstreetmap.josm.Main;
15import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
16import org.openstreetmap.josm.gui.layer.Layer;
17import org.openstreetmap.josm.gui.layer.OsmDataLayer;
18import org.openstreetmap.josm.gui.util.GuiHelper;
19import org.openstreetmap.josm.tools.ImageProvider;
20import org.openstreetmap.josm.tools.Shortcut;
21import org.openstreetmap.josm.tools.Utils;
22
23/**
24 * Action that merges two or more OSM data layers.
25 * @since 1890
26 */
27public class MergeLayerAction extends AbstractMergeAction {
28
29 /**
30 * Constructs a new {@code MergeLayerAction}.
31 */
32 public MergeLayerAction() {
33 super(tr("Merge layer"), "dialogs/mergedown",
34 tr("Merge the current layer into another layer"),
35 Shortcut.registerShortcut("system:merge", tr("Edit: {0}",
36 tr("Merge")), KeyEvent.VK_M, Shortcut.CTRL),
37 true, "action/mergelayer", true);
38 putValue("help", ht("/Action/MergeLayer"));
39 }
40
41 /**
42 * Submits merge of layers.
43 * @param targetLayers possible target layers
44 * @param sourceLayers source layers
45 * @return a Future representing pending completion of the merge task, or {@code null}
46 * @since 11885 (return type)
47 */
48 protected Future<?> doMerge(List<Layer> targetLayers, final Collection<Layer> sourceLayers) {
49 final Layer targetLayer = askTargetLayer(targetLayers);
50 if (targetLayer == null)
51 return null;
52 final Object actionName = getValue(NAME);
53 return Main.worker.submit(() -> {
54 final long start = System.currentTimeMillis();
55 boolean layerMerged = false;
56 for (final Layer sourceLayer: sourceLayers) {
57 if (sourceLayer != null && !sourceLayer.equals(targetLayer)) {
58 if (sourceLayer instanceof OsmDataLayer && targetLayer instanceof OsmDataLayer
59 && ((OsmDataLayer) sourceLayer).isUploadDiscouraged() != ((OsmDataLayer) targetLayer).isUploadDiscouraged()
60 && Boolean.TRUE.equals(GuiHelper.runInEDTAndWaitAndReturn(() ->
61 warnMergingUploadDiscouragedLayers(sourceLayer, targetLayer)))) {
62 break;
63 }
64 targetLayer.mergeFrom(sourceLayer);
65 GuiHelper.runInEDTAndWait(() -> Main.getLayerManager().removeLayer(sourceLayer));
66 layerMerged = true;
67 }
68 }
69 if (layerMerged) {
70 Main.getLayerManager().setActiveLayer(targetLayer);
71 Main.info(tr("{0} completed in {1}", actionName, Utils.getDurationString(System.currentTimeMillis() - start)));
72 }
73 });
74 }
75
76 /**
77 * Merges a list of layers together.
78 * @param sourceLayers The layers to merge
79 * @return a Future representing pending completion of the merge task, or {@code null}
80 * @since 11885 (return type)
81 */
82 public Future<?> merge(List<Layer> sourceLayers) {
83 return doMerge(sourceLayers, sourceLayers);
84 }
85
86 /**
87 * Merges the given source layer with another one, determined at runtime.
88 * @param sourceLayer The source layer to merge
89 * @return a Future representing pending completion of the merge task, or {@code null}
90 * @since 11885 (return type)
91 */
92 public Future<?> merge(Layer sourceLayer) {
93 if (sourceLayer == null)
94 return null;
95 List<Layer> targetLayers = LayerListDialog.getInstance().getModel().getPossibleMergeTargets(sourceLayer);
96 if (targetLayers.isEmpty()) {
97 warnNoTargetLayersForSourceLayer(sourceLayer);
98 return null;
99 }
100 return doMerge(targetLayers, Collections.singleton(sourceLayer));
101 }
102
103 @Override
104 public void actionPerformed(ActionEvent e) {
105 merge(getSourceLayer());
106 }
107
108 @Override
109 protected void updateEnabledState() {
110 GuiHelper.runInEDT(() -> {
111 final Layer sourceLayer = getSourceLayer();
112 if (sourceLayer == null) {
113 setEnabled(false);
114 } else {
115 try {
116 setEnabled(!LayerListDialog.getInstance().getModel().getPossibleMergeTargets(sourceLayer).isEmpty());
117 } catch (IllegalStateException e) {
118 // May occur when destroying last layer / exiting JOSM, see #14476
119 setEnabled(false);
120 Main.error(e);
121 }
122 }
123 });
124 }
125
126 /**
127 * Returns the source layer.
128 * @return the source layer
129 */
130 protected Layer getSourceLayer() {
131 return Main.getLayerManager().getActiveLayer();
132 }
133
134 /**
135 * Warns about a discouraged merge operation, ask for confirmation.
136 * @param sourceLayer The source layer
137 * @param targetLayer The target layer
138 * @return {@code true} if the user wants to cancel, {@code false} if they want to continue
139 */
140 public static final boolean warnMergingUploadDiscouragedLayers(Layer sourceLayer, Layer targetLayer) {
141 return GuiHelper.warnUser(tr("Merging layers with different upload policies"),
142 "<html>" +
143 tr("You are about to merge data between layers ''{0}'' and ''{1}''.<br /><br />"+
144 "These layers have different upload policies and should not been merged as it.<br />"+
145 "Merging them will result to enforce the stricter policy (upload discouraged) to ''{1}''.<br /><br />"+
146 "<b>This is not the recommended way of merging such data</b>.<br />"+
147 "You should instead check and merge each object, one by one, by using ''<i>Merge selection</i>''.<br /><br />"+
148 "Are you sure you want to continue?",
149 Utils.escapeReservedCharactersHTML(sourceLayer.getName()),
150 Utils.escapeReservedCharactersHTML(targetLayer.getName()),
151 Utils.escapeReservedCharactersHTML(targetLayer.getName()))+
152 "</html>",
153 ImageProvider.get("dialogs", "mergedown"), tr("Ignore this hint and merge anyway"));
154 }
155}
Note: See TracBrowser for help on using the repository browser.