| | 1 | // License: GPL. For details, see LICENSE file. |
| | 2 | package org.openstreetmap.josm.gui.io; |
| | 3 | |
| | 4 | import org.openstreetmap.josm.data.APIDataSet; |
| | 5 | import org.openstreetmap.josm.data.osm.Changeset; |
| | 6 | import org.openstreetmap.josm.data.osm.DataSet; |
| | 7 | import org.openstreetmap.josm.data.osm.OsmPrimitive; |
| | 8 | import org.openstreetmap.josm.gui.MainApplication; |
| | 9 | import org.openstreetmap.josm.gui.layer.OsmDataLayer; |
| | 10 | import org.openstreetmap.josm.gui.progress.ProgressTaskId; |
| | 11 | import org.openstreetmap.josm.gui.util.GuiHelper; |
| | 12 | import org.openstreetmap.josm.io.UploadStrategySpecification; |
| | 13 | import org.openstreetmap.josm.tools.I18n; |
| | 14 | |
| | 15 | import javax.swing.*; |
| | 16 | import java.awt.*; |
| | 17 | import java.util.List; |
| | 18 | import java.util.Optional; |
| | 19 | |
| | 20 | /** |
| | 21 | * Allows to upload the primitives in background |
| | 22 | */ |
| | 23 | public class AsynchronousUploadPrimitivesTask extends UploadPrimitivesTask { |
| | 24 | |
| | 25 | /** |
| | 26 | * Static instance |
| | 27 | */ |
| | 28 | private static AsynchronousUploadPrimitivesTask asynchronousUploadPrimitivesTask = null; |
| | 29 | private static final boolean HIDE_UPLOADING_PRIMITIVES = false; |
| | 30 | private static final String TRANSIENT_LAYER_NAME = "Transient upload layer"; |
| | 31 | |
| | 32 | /** |
| | 33 | * Member fields |
| | 34 | */ |
| | 35 | private final ProgressTaskId taskId; |
| | 36 | private final List<OsmPrimitive> osmPrimitiveList; |
| | 37 | private final OsmDataLayer uploadDataLayer; |
| | 38 | |
| | 39 | /** |
| | 40 | * Private constructor to restrict creating more Asynchronous upload tasks |
| | 41 | * |
| | 42 | * @param uploadStrategySpecification |
| | 43 | * @param osmDataLayer |
| | 44 | * @param apiDataSet |
| | 45 | * @param changeset |
| | 46 | */ |
| | 47 | private AsynchronousUploadPrimitivesTask(UploadStrategySpecification uploadStrategySpecification, OsmDataLayer osmDataLayer, APIDataSet apiDataSet, Changeset changeset) { |
| | 48 | super(uploadStrategySpecification, |
| | 49 | osmDataLayer, |
| | 50 | apiDataSet, |
| | 51 | changeset); |
| | 52 | |
| | 53 | uploadDataLayer = osmDataLayer; |
| | 54 | |
| | 55 | // Get the primitives that are added/deleted/edited |
| | 56 | osmPrimitiveList = apiDataSet.getPrimitives(); |
| | 57 | |
| | 58 | // Create a ProgressTaskId for background upload |
| | 59 | taskId = new ProgressTaskId("core", "async-upload"); |
| | 60 | } |
| | 61 | |
| | 62 | /** |
| | 63 | * Creates a transient OsmDataLayer with DataSet containing the primitives to be added/updated/deleted |
| | 64 | * |
| | 65 | * @return |
| | 66 | */ |
| | 67 | private static OsmDataLayer createTransientOsmDataLayer(String name) { |
| | 68 | DataSet dataSet = new DataSet(); |
| | 69 | return new OsmDataLayer(dataSet, name, null); |
| | 70 | } |
| | 71 | |
| | 72 | /** |
| | 73 | * Disables the list of primitives and their referrers |
| | 74 | * @param osmPrimitiveList |
| | 75 | */ |
| | 76 | private static void disablePrimitives(List <OsmPrimitive> osmPrimitiveList) { |
| | 77 | for (OsmPrimitive p : osmPrimitiveList) { |
| | 78 | if(!p.isDeleted()) { |
| | 79 | p.setDisabledState(HIDE_UPLOADING_PRIMITIVES); |
| | 80 | for (OsmPrimitive ref : p.getReferrers()) { |
| | 81 | ref.setDisabledState(HIDE_UPLOADING_PRIMITIVES); |
| | 82 | } |
| | 83 | } |
| | 84 | } |
| | 85 | } |
| | 86 | |
| | 87 | /** |
| | 88 | * Enables the list of primitives provided |
| | 89 | * @param osmPrimitiveList |
| | 90 | */ |
| | 91 | private static void enablePrimitives(List <OsmPrimitive> osmPrimitiveList) { |
| | 92 | for (OsmPrimitive p : osmPrimitiveList) { |
| | 93 | |
| | 94 | // Only enable the primitives that are still present |
| | 95 | if (!p.isDeleted()) { |
| | 96 | p.unsetDisabledState(); |
| | 97 | |
| | 98 | // Enable the referrers of all primitives |
| | 99 | for (OsmPrimitive ref : p.getReferrers()) { |
| | 100 | if (!ref.isDeleted()) |
| | 101 | ref.unsetDisabledState(); |
| | 102 | } |
| | 103 | } |
| | 104 | } |
| | 105 | } |
| | 106 | |
| | 107 | /** |
| | 108 | * Creates an instance of AsynchronousUploadPrimitiveTask |
| | 109 | * |
| | 110 | * @param uploadStrategySpecification |
| | 111 | * @param dataLayer |
| | 112 | * @param apiDataSet |
| | 113 | * @param changeset |
| | 114 | * @return Optional<AsynchronousUploadPrimitivesTask> |
| | 115 | */ |
| | 116 | public static Optional<AsynchronousUploadPrimitivesTask> createAsynchronousUploadTask |
| | 117 | (UploadStrategySpecification uploadStrategySpecification, |
| | 118 | OsmDataLayer dataLayer, APIDataSet apiDataSet, Changeset changeset) { |
| | 119 | synchronized (AsynchronousUploadPrimitivesTask.class) { |
| | 120 | if (asynchronousUploadPrimitivesTask != null) { |
| | 121 | if (!GraphicsEnvironment.isHeadless()) { |
| | 122 | GuiHelper.runInEDTAndWait(() -> |
| | 123 | JOptionPane.showMessageDialog(MainApplication.parent, |
| | 124 | I18n.tr("A background upload is already in progress. Kindly wait for it to finish before uploading new changes"))); |
| | 125 | } |
| | 126 | return Optional.empty(); |
| | 127 | } else { |
| | 128 | // Get a transient upload layer |
| | 129 | OsmDataLayer transientLayer = createTransientOsmDataLayer(TRANSIENT_LAYER_NAME); |
| | 130 | MainApplication.getLayerManager().addLayer(transientLayer); |
| | 131 | MainApplication.getLayerManager().setActiveLayer(transientLayer); |
| | 132 | |
| | 133 | // Create an asynchronous upload task with the transient layer |
| | 134 | asynchronousUploadPrimitivesTask = new AsynchronousUploadPrimitivesTask( |
| | 135 | uploadStrategySpecification, |
| | 136 | dataLayer, |
| | 137 | apiDataSet, |
| | 138 | changeset); |
| | 139 | return Optional.ofNullable(asynchronousUploadPrimitivesTask); |
| | 140 | } |
| | 141 | } |
| | 142 | } |
| | 143 | |
| | 144 | /** |
| | 145 | * Get the current upload task |
| | 146 | * @return Optional<AsynchronousUploadPrimitivesTask> |
| | 147 | */ |
| | 148 | public static Optional<AsynchronousUploadPrimitivesTask> getCurrentAsynchronousUploadTask () { |
| | 149 | return Optional.ofNullable(asynchronousUploadPrimitivesTask); |
| | 150 | } |
| | 151 | |
| | 152 | /** |
| | 153 | * Lock the dataset |
| | 154 | */ |
| | 155 | protected void lockDataSet () { |
| | 156 | uploadDataLayer.data.beginUpdate(); |
| | 157 | } |
| | 158 | |
| | 159 | /** |
| | 160 | * Unlock the upload task dataset |
| | 161 | */ |
| | 162 | protected void unlockDataSet () { |
| | 163 | uploadDataLayer.data.endUpdate(); |
| | 164 | } |
| | 165 | |
| | 166 | /** |
| | 167 | * Merge the transient upload layer with the current edit layer |
| | 168 | */ |
| | 169 | private void mergeTransientLayer () { |
| | 170 | OsmDataLayer currentEditLayer = MainApplication.getLayerManager().getEditLayer(); |
| | 171 | currentEditLayer.mergeFrom(uploadDataLayer); |
| | 172 | MainApplication.getLayerManager().removeLayer(uploadDataLayer); |
| | 173 | currentEditLayer.rename(uploadDataLayer.getName()); |
| | 174 | } |
| | 175 | |
| | 176 | @Override |
| | 177 | protected void cleanupAfterUpload () { |
| | 178 | // The cleanup happens on EDT. Hence unlocking the dataset |
| | 179 | unlockDataSet(); |
| | 180 | super.cleanupAfterUpload(); |
| | 181 | } |
| | 182 | |
| | 183 | @Override |
| | 184 | public ProgressTaskId canRunInBackground() { |
| | 185 | return taskId; |
| | 186 | } |
| | 187 | |
| | 188 | @Override |
| | 189 | protected void realRun() { |
| | 190 | // Lock the dataset for any changes from other threads |
| | 191 | lockDataSet(); |
| | 192 | super.realRun(); |
| | 193 | } |
| | 194 | |
| | 195 | @Override |
| | 196 | protected void cancel() { |
| | 197 | super.cancel(); |
| | 198 | asynchronousUploadPrimitivesTask = null; |
| | 199 | } |
| | 200 | |
| | 201 | @Override |
| | 202 | protected void finish() { |
| | 203 | try { |
| | 204 | // Note: Always merge back the layers. |
| | 205 | // Even in cases with conflicts/failures the users changes should not be discarded. |
| | 206 | mergeTransientLayer(); |
| | 207 | |
| | 208 | super.finish(); |
| | 209 | } finally { |
| | 210 | asynchronousUploadPrimitivesTask = null; |
| | 211 | } |
| | 212 | } |
| | 213 | } |
| | 214 | No newline at end of file |