001 // License: GPL. For details, see LICENSE file.
002 package org.openstreetmap.josm.actions.downloadtasks;
003
004 import static org.openstreetmap.josm.tools.I18n.tr;
005 import static org.openstreetmap.josm.tools.I18n.trn;
006
007 import java.io.IOException;
008 import java.text.MessageFormat;
009 import java.util.Collection;
010 import java.util.Set;
011 import java.util.HashSet;
012 import java.util.HashMap;
013 import java.util.Map;
014 import java.util.Map.Entry;
015
016 import javax.swing.JOptionPane;
017 import javax.swing.SwingUtilities;
018
019 import org.openstreetmap.josm.Main;
020 import org.openstreetmap.josm.data.osm.DataSet;
021 import org.openstreetmap.josm.data.osm.DataSetMerger;
022 import org.openstreetmap.josm.data.osm.Node;
023 import org.openstreetmap.josm.data.osm.OsmPrimitive;
024 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
025 import org.openstreetmap.josm.data.osm.PrimitiveId;
026 import org.openstreetmap.josm.data.osm.Way;
027 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
028 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
029 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
030 import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
031 import org.openstreetmap.josm.io.OsmServerBackreferenceReader;
032 import org.openstreetmap.josm.io.OsmServerReader;
033 import org.openstreetmap.josm.io.OsmTransferException;
034 import org.openstreetmap.josm.tools.CheckParameterUtil;
035 import org.openstreetmap.josm.tools.ExceptionUtil;
036 import org.xml.sax.SAXException;
037
038 /**
039 * The asynchronous task for downloading referring primitives
040 *
041 */
042 public class DownloadReferrersTask extends PleaseWaitRunnable {
043 private boolean canceled;
044 private Exception lastException;
045 private OsmServerReader reader;
046 /** the target layer */
047 private OsmDataLayer targetLayer;
048 /** the collection of child primitives */
049 private Map<Long, OsmPrimitiveType> children;
050 /** the parents */
051 private DataSet parents;
052
053 /**
054 * constructor
055 *
056 * @param targetLayer the target layer for the downloaded primitives. Must not be null.
057 * @param children the collection of child primitives for which parents are to be downloaded
058 *
059 */
060 public DownloadReferrersTask(OsmDataLayer targetLayer, Collection<OsmPrimitive> children) {
061 super("Download referrers", false /* don't ignore exception*/);
062 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer");
063 canceled = false;
064 this.children = new HashMap<Long, OsmPrimitiveType>();
065 if (children != null) {
066 for (OsmPrimitive p: children) {
067 if (! p.isNew()) {
068 this.children.put(p.getId(), OsmPrimitiveType.from(p));
069 }
070 }
071 }
072 this.targetLayer = targetLayer;
073 parents = new DataSet();
074 }
075
076 /**
077 * constructor
078 *
079 * @param targetLayer the target layer for the downloaded primitives. Must not be null.
080 * @param primitives the collection of children for which parents are to be downloaded. Children
081 * are specified by their id and their type.
082 *
083 */
084 public DownloadReferrersTask(OsmDataLayer targetLayer, Map<Long, OsmPrimitiveType> children) {
085 super("Download referrers", false /* don't ignore exception*/);
086 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer");
087 canceled = false;
088 this.children = new HashMap<Long, OsmPrimitiveType>();
089 if (children != null) {
090 for (Entry<Long, OsmPrimitiveType> entry : children.entrySet()) {
091 if (entry.getKey() > 0 && entry.getValue() != null) {
092 children.put(entry.getKey(), entry.getValue());
093 }
094 }
095 }
096 this.targetLayer = targetLayer;
097 parents = new DataSet();
098 }
099
100 /**
101 * constructor
102 *
103 * @param targetLayer the target layer. Must not be null.
104 * @param id the primitive id. id > 0 required.
105 * @param type the primitive type. type != null required
106 * @exception IllegalArgumentException thrown if id <= 0
107 * @exception IllegalArgumentException thrown if type == null
108 * @exception IllegalArgumentException thrown if targetLayer == null
109 *
110 */
111 public DownloadReferrersTask(OsmDataLayer targetLayer, long id, OsmPrimitiveType type) throws IllegalArgumentException {
112 super("Download referrers", false /* don't ignore exception*/);
113 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer");
114 if (id <= 0)
115 throw new IllegalArgumentException(MessageFormat.format("Id > 0 required, got {0}", id));
116 CheckParameterUtil.ensureParameterNotNull(type, "type");
117 canceled = false;
118 this.children = new HashMap<Long, OsmPrimitiveType>();
119 this.children.put(id, type);
120 this.targetLayer = targetLayer;
121 parents = new DataSet();
122 }
123
124 /**
125 * constructor
126 *
127 * @param targetLayer the target layer. Must not be null.
128 * @param primitiveId a PrimitiveId object.
129 * @exception IllegalArgumentException thrown if id <= 0
130 * @exception IllegalArgumentException thrown if targetLayer == null
131 *
132 */
133 public DownloadReferrersTask(OsmDataLayer targetLayer, PrimitiveId primitiveId) throws IllegalArgumentException {
134 super("Download referrers", false /* don't ignore exception*/);
135 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer");
136 if (primitiveId.isNew())
137 throw new IllegalArgumentException(MessageFormat.format("Cannot download referrers for new primitives (ID {0})", primitiveId.getUniqueId()));
138 canceled = false;
139 this.children = new HashMap<Long, OsmPrimitiveType>();
140 this.children.put(primitiveId.getUniqueId(), primitiveId.getType());
141 this.targetLayer = targetLayer;
142 parents = new DataSet();
143 }
144
145 @Override
146 protected void cancel() {
147 canceled = true;
148 synchronized(this) {
149 if (reader != null) {
150 reader.cancel();
151 }
152 }
153 }
154
155 @Override
156 protected void finish() {
157 if (canceled)
158 return;
159 if (lastException != null) {
160 ExceptionUtil.explainException(lastException);
161 return;
162 }
163
164 DataSetMerger visitor = new DataSetMerger(targetLayer.data, parents);
165 visitor.merge();
166 SwingUtilities.invokeLater(
167 new Runnable() {
168 public void run() {
169 targetLayer.onPostDownloadFromServer();
170 Main.map.mapView.repaint();
171 }
172 }
173 );
174 if (visitor.getConflicts().isEmpty())
175 return;
176 targetLayer.getConflicts().add(visitor.getConflicts());
177 JOptionPane.showMessageDialog(
178 Main.parent,
179 trn("There was {0} conflict during import.",
180 "There were {0} conflicts during import.",
181 visitor.getConflicts().size(),
182 visitor.getConflicts().size()
183 ),
184 trn("Conflict during download", "Conflicts during download", visitor.getConflicts().size()),
185 JOptionPane.WARNING_MESSAGE
186 );
187 Main.map.conflictDialog.unfurlDialog();
188 Main.map.repaint();
189 }
190
191 protected void downloadParents(long id, OsmPrimitiveType type, ProgressMonitor progressMonitor) throws OsmTransferException{
192 reader = new OsmServerBackreferenceReader(id, type);
193 DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
194 synchronized(this) { // avoid race condition in cancel()
195 reader = null;
196 }
197 Collection<Way> ways = ds.getWays();
198
199 DataSetMerger merger;
200 if (!ways.isEmpty()) {
201 Set<Node> nodes = new HashSet<Node>();
202 for (Way w: ways) {
203 // Ensure each node is only listed once
204 nodes.addAll(w.getNodes());
205 }
206 // Don't retrieve any nodes we've already grabbed
207 nodes.removeAll(targetLayer.data.getNodes());
208 if (!nodes.isEmpty()) {
209 reader = new MultiFetchServerObjectReader();
210 ((MultiFetchServerObjectReader)reader).append(nodes);
211 DataSet wayNodes = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
212 synchronized(this) { // avoid race condition in cancel()
213 reader = null;
214 }
215 merger = new DataSetMerger(ds, wayNodes);
216 merger.merge();
217 }
218 }
219 merger = new DataSetMerger(parents, ds);
220 merger.merge();
221 }
222
223 @Override
224 protected void realRun() throws SAXException, IOException, OsmTransferException {
225 try {
226 progressMonitor.setTicksCount(children.size());
227 int i=1;
228 for (Entry<Long, OsmPrimitiveType> entry: children.entrySet()) {
229 if (canceled)
230 return;
231 String msg = "";
232 switch(entry.getValue()) {
233 case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i+1,children.size(), entry.getKey()); break;
234 case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i+1,children.size(), entry.getKey()); break;
235 case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i+1,children.size(), entry.getKey()); break;
236 }
237 progressMonitor.subTask(msg);
238 downloadParents(entry.getKey(), entry.getValue(), progressMonitor);
239 i++;
240 }
241 } catch(Exception e) {
242 if (canceled)
243 return;
244 lastException = e;
245 }
246 }
247 }