001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.io; 003 004import static org.openstreetmap.josm.tools.CheckParameterUtil.ensureParameterNotNull; 005import static org.openstreetmap.josm.tools.I18n.tr; 006 007import java.io.IOException; 008import java.util.Collection; 009import java.util.Collections; 010 011import org.openstreetmap.josm.data.osm.DataSet; 012import org.openstreetmap.josm.data.osm.DataSetMerger; 013import org.openstreetmap.josm.data.osm.Node; 014import org.openstreetmap.josm.data.osm.OsmPrimitive; 015import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 016import org.openstreetmap.josm.data.osm.Relation; 017import org.openstreetmap.josm.data.osm.Way; 018import org.openstreetmap.josm.gui.ExceptionDialogUtil; 019import org.openstreetmap.josm.gui.PleaseWaitRunnable; 020import org.openstreetmap.josm.gui.layer.OsmDataLayer; 021import org.openstreetmap.josm.gui.progress.ProgressMonitor; 022import org.openstreetmap.josm.gui.util.GuiHelper; 023import org.openstreetmap.josm.io.MultiFetchServerObjectReader; 024import org.openstreetmap.josm.io.OsmServerObjectReader; 025import org.openstreetmap.josm.io.OsmTransferException; 026import org.xml.sax.SAXException; 027 028/** 029 * The asynchronous task for updating a collection of objects using multi fetch. 030 * 031 */ 032public class UpdatePrimitivesTask extends PleaseWaitRunnable { 033 private DataSet ds; 034 private boolean canceled; 035 private Exception lastException; 036 private final Collection<? extends OsmPrimitive> toUpdate; 037 private final OsmDataLayer layer; 038 private MultiFetchServerObjectReader multiObjectReader; 039 private OsmServerObjectReader objectReader; 040 041 /** 042 * Creates the task 043 * 044 * @param layer the layer in which primitives are updated. Must not be null. 045 * @param toUpdate a collection of primitives to update from the server. Set to 046 * the empty collection if null. 047 * @throws IllegalArgumentException if layer is null. 048 */ 049 public UpdatePrimitivesTask(OsmDataLayer layer, Collection<? extends OsmPrimitive> toUpdate) { 050 super(tr("Update objects"), false /* don't ignore exception */); 051 ensureParameterNotNull(layer, "layer"); 052 if (toUpdate == null) { 053 toUpdate = Collections.emptyList(); 054 } 055 this.layer = layer; 056 this.toUpdate = toUpdate; 057 } 058 059 @Override 060 protected void cancel() { 061 canceled = true; 062 synchronized (this) { 063 if (multiObjectReader != null) { 064 multiObjectReader.cancel(); 065 } 066 if (objectReader != null) { 067 objectReader.cancel(); 068 } 069 } 070 } 071 072 @Override 073 protected void finish() { 074 if (canceled) 075 return; 076 if (lastException != null) { 077 ExceptionDialogUtil.explainException(lastException); 078 return; 079 } 080 GuiHelper.runInEDTAndWait(new Runnable() { 081 @Override 082 public void run() { 083 layer.mergeFrom(ds); 084 layer.onPostDownloadFromServer(); 085 } 086 }); 087 } 088 089 protected void initMultiFetchReaderWithNodes(MultiFetchServerObjectReader reader) { 090 getProgressMonitor().indeterminateSubTask(tr("Initializing nodes to update ...")); 091 for (OsmPrimitive primitive : toUpdate) { 092 if (primitive instanceof Node && !primitive.isNew()) { 093 reader.append(primitive); 094 } 095 } 096 } 097 098 protected void initMultiFetchReaderWithWays(MultiFetchServerObjectReader reader) { 099 getProgressMonitor().indeterminateSubTask(tr("Initializing ways to update ...")); 100 for (OsmPrimitive primitive : toUpdate) { 101 if (primitive instanceof Way && !primitive.isNew()) { 102 // this also adds way nodes 103 reader.append(primitive); 104 } 105 } 106 } 107 108 protected void initMultiFetchReaderWithRelations(MultiFetchServerObjectReader reader) { 109 getProgressMonitor().indeterminateSubTask(tr("Initializing relations to update ...")); 110 for (OsmPrimitive primitive : toUpdate) { 111 if (primitive instanceof Relation && !primitive.isNew()) { 112 // this also adds relation members 113 reader.append(primitive); 114 } 115 } 116 } 117 118 @Override 119 protected void realRun() throws SAXException, IOException, OsmTransferException { 120 this.ds = new DataSet(); 121 DataSet theirDataSet; 122 try { 123 synchronized (this) { 124 if (canceled) return; 125 multiObjectReader = MultiFetchServerObjectReader.create(); 126 } 127 initMultiFetchReaderWithNodes(multiObjectReader); 128 initMultiFetchReaderWithWays(multiObjectReader); 129 initMultiFetchReaderWithRelations(multiObjectReader); 130 theirDataSet = multiObjectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)); 131 synchronized (this) { 132 multiObjectReader = null; 133 } 134 DataSetMerger merger = new DataSetMerger(ds, theirDataSet); 135 merger.merge(); 136 // a way loaded with MultiFetch may have incomplete nodes because at least one of its 137 // nodes isn't present in the local data set. We therefore fully load all 138 // ways with incomplete nodes. 139 // 140 for (Way w : ds.getWays()) { 141 if (canceled) return; 142 if (w.hasIncompleteNodes()) { 143 synchronized (this) { 144 if (canceled) return; 145 objectReader = new OsmServerObjectReader(w.getId(), OsmPrimitiveType.WAY, true /* full */); 146 } 147 theirDataSet = objectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)); 148 synchronized (this) { 149 objectReader = null; 150 } 151 merger = new DataSetMerger(ds, theirDataSet); 152 merger.merge(); 153 } 154 } 155 } catch (Exception e) { 156 if (canceled) 157 return; 158 lastException = e; 159 } 160 } 161}