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 Collection<? extends OsmPrimitive> toUpdate; 037 private 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 thrown if layer is null. 048 */ 049 public UpdatePrimitivesTask(OsmDataLayer layer, Collection<? extends OsmPrimitive> toUpdate) throws IllegalArgumentException{ 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 } else if (primitive instanceof Way) { 095 Way way = (Way)primitive; 096 for (Node node: way.getNodes()) { 097 if (!node.isNew()) { 098 reader.append(node); 099 } 100 } 101 } 102 } 103 } 104 105 protected void initMultiFetchReaderWithWays(MultiFetchServerObjectReader reader) { 106 getProgressMonitor().indeterminateSubTask(tr("Initializing ways to update ...")); 107 for (OsmPrimitive primitive : toUpdate) { 108 if (primitive instanceof Way && !primitive.isNew()) { 109 reader.append(primitive); 110 } 111 } 112 } 113 114 protected void initMultiFetchReaderWithRelations(MultiFetchServerObjectReader reader) { 115 getProgressMonitor().indeterminateSubTask(tr("Initializing relations to update ...")); 116 for (OsmPrimitive primitive : toUpdate) { 117 if (primitive instanceof Relation && !primitive.isNew()) { 118 reader.append(primitive); 119 } 120 } 121 } 122 123 @Override 124 protected void realRun() throws SAXException, IOException, OsmTransferException { 125 this.ds = new DataSet(); 126 DataSet theirDataSet; 127 try { 128 synchronized(this) { 129 if (canceled) return; 130 multiObjectReader = new MultiFetchServerObjectReader(); 131 } 132 initMultiFetchReaderWithNodes(multiObjectReader); 133 initMultiFetchReaderWithWays(multiObjectReader); 134 initMultiFetchReaderWithRelations(multiObjectReader); 135 theirDataSet = multiObjectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)); 136 synchronized(this) { 137 multiObjectReader = null; 138 } 139 DataSetMerger merger = new DataSetMerger(ds, theirDataSet); 140 merger.merge(); 141 // a way loaded with MultiFetch may have incomplete nodes because at least one of its 142 // nodes isn't present in the local data set. We therefore fully load all 143 // ways with incomplete nodes. 144 // 145 for (Way w : ds.getWays()) { 146 if (canceled) return; 147 if (w.hasIncompleteNodes()) { 148 synchronized(this) { 149 if (canceled) return; 150 objectReader = new OsmServerObjectReader(w.getId(), OsmPrimitiveType.WAY, true /* full */); 151 } 152 theirDataSet = objectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)); 153 synchronized (this) { 154 objectReader = null; 155 } 156 merger = new DataSetMerger(ds, theirDataSet); 157 merger.merge(); 158 } 159 } 160 } catch(Exception e) { 161 if (canceled) 162 return; 163 lastException = e; 164 } 165 } 166}