001 // License: GPL. For details, see LICENSE file.
002 package org.openstreetmap.josm.gui.io;
003
004 import static org.openstreetmap.josm.tools.CheckParameterUtil.ensureParameterNotNull;
005 import static org.openstreetmap.josm.tools.I18n.tr;
006
007 import java.io.IOException;
008 import java.util.List;
009 import java.util.Set;
010
011 import org.openstreetmap.josm.actions.AutoScaleAction;
012 import org.openstreetmap.josm.data.osm.DataSet;
013 import org.openstreetmap.josm.data.osm.DataSetMerger;
014 import org.openstreetmap.josm.data.osm.Node;
015 import org.openstreetmap.josm.data.osm.OsmPrimitive;
016 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
017 import org.openstreetmap.josm.data.osm.PrimitiveId;
018 import org.openstreetmap.josm.data.osm.Relation;
019 import org.openstreetmap.josm.data.osm.Way;
020 import org.openstreetmap.josm.gui.ExceptionDialogUtil;
021 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
022 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
023 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
024 import org.openstreetmap.josm.gui.util.GuiHelper;
025 import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
026 import org.openstreetmap.josm.io.OsmServerObjectReader;
027 import org.openstreetmap.josm.io.OsmTransferException;
028 import org.xml.sax.SAXException;
029
030 public class DownloadPrimitivesTask extends PleaseWaitRunnable {
031 private DataSet ds;
032 private boolean canceled;
033 private Exception lastException;
034 private List<PrimitiveId> ids;
035
036 private Set<PrimitiveId> missingPrimitives;
037
038 private OsmDataLayer layer;
039 private boolean fullRelation;
040 private MultiFetchServerObjectReader multiObjectReader;
041 private OsmServerObjectReader objectReader;
042
043 /**
044 * Creates the task
045 *
046 * @param layer the layer in which primitives are updated. Must not be null.
047 * @param toUpdate a collection of primitives to update from the server. Set to
048 * the empty collection if null.
049 * @param fullRelation true if a full download is required, i.e.,
050 * a download including the immediate children of a relation.
051 * @throws IllegalArgumentException thrown if layer is null.
052 */
053 public DownloadPrimitivesTask(OsmDataLayer layer, List<PrimitiveId> ids, boolean fullRelation) throws IllegalArgumentException {
054 super(tr("Download objects"), false /* don't ignore exception */);
055 ensureParameterNotNull(layer, "layer");
056 this.ids = ids;
057 this.layer = layer;
058 this.fullRelation = fullRelation;
059 }
060
061 @Override
062 protected void cancel() {
063 canceled = true;
064 synchronized(this) {
065 if (multiObjectReader != null) {
066 multiObjectReader.cancel();
067 }
068 if (objectReader != null) {
069 objectReader.cancel();
070 }
071 }
072 }
073
074 @Override
075 protected void finish() {
076 if (canceled)
077 return;
078 if (lastException != null) {
079 ExceptionDialogUtil.explainException(lastException);
080 return;
081 }
082 GuiHelper.runInEDTAndWait(new Runnable() {
083 public void run() {
084 layer.mergeFrom(ds);
085 AutoScaleAction.zoomTo(ds.allPrimitives());
086 layer.onPostDownloadFromServer();
087 }
088 });
089 }
090
091 protected void initMultiFetchReader(MultiFetchServerObjectReader reader) {
092 getProgressMonitor().indeterminateSubTask(tr("Initializing nodes to download ..."));
093 for (PrimitiveId id : ids) {
094 OsmPrimitive osm = layer.data.getPrimitiveById(id);
095 if (osm == null) {
096 switch (id.getType()) {
097 case NODE:
098 osm = new Node(id.getUniqueId());
099 break;
100 case WAY:
101 osm = new Way(id.getUniqueId());
102 break;
103 case RELATION:
104 osm = new Relation(id.getUniqueId());
105 break;
106 default: throw new AssertionError();
107 }
108 }
109 reader.append(osm);
110 }
111 }
112
113 @Override
114 protected void realRun() throws SAXException, IOException, OsmTransferException {
115 this.ds = new DataSet();
116 DataSet theirDataSet;
117 try {
118 synchronized(this) {
119 if (canceled) return;
120 multiObjectReader = new MultiFetchServerObjectReader();
121 }
122 initMultiFetchReader(multiObjectReader);
123 theirDataSet = multiObjectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
124 missingPrimitives = multiObjectReader.getMissingPrimitives();
125 synchronized(this) {
126 multiObjectReader = null;
127 }
128 DataSetMerger merger = new DataSetMerger(ds, theirDataSet);
129 merger.merge();
130
131 // if incomplete relation members exist, download them too
132 for (Relation r : ds.getRelations()) {
133 if (canceled) return;
134 if (r.hasIncompleteMembers()) {
135 synchronized(this) {
136 if (canceled) return;
137 objectReader = new OsmServerObjectReader(r.getId(), OsmPrimitiveType.RELATION, fullRelation);
138 }
139 theirDataSet = objectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
140 synchronized (this) {
141 objectReader = null;
142 }
143 merger = new DataSetMerger(ds, theirDataSet);
144 merger.merge();
145 }
146 }
147
148 // a way loaded with MultiFetch may have incomplete nodes because at least one of its
149 // nodes isn't present in the local data set. We therefore fully load all
150 // ways with incomplete nodes.
151 //
152 for (Way w : ds.getWays()) {
153 if (canceled) return;
154 if (w.hasIncompleteNodes()) {
155 synchronized(this) {
156 if (canceled) return;
157 objectReader = new OsmServerObjectReader(w.getId(), OsmPrimitiveType.WAY, true /* full */);
158 }
159 theirDataSet = objectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
160 synchronized (this) {
161 objectReader = null;
162 }
163 merger = new DataSetMerger(ds, theirDataSet);
164 merger.merge();
165 }
166 }
167
168 } catch(Exception e) {
169 if (canceled) return;
170 lastException = e;
171 }
172 }
173
174 /**
175 * replies the set of ids of all primitives for which a fetch request to the
176 * server was submitted but which are not available from the server (the server
177 * replied a return code of 404)
178 *
179 * @return the set of ids of missing primitives
180 */
181 public Set<PrimitiveId> getMissingPrimitives() {
182 return missingPrimitives;
183 }
184
185 }