001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.history; 003 004import static org.openstreetmap.josm.tools.I18n.marktr; 005import static org.openstreetmap.josm.tools.I18n.tr; 006 007import java.awt.Component; 008import java.io.IOException; 009import java.text.MessageFormat; 010import java.util.Collection; 011import java.util.HashSet; 012import java.util.Set; 013 014import org.openstreetmap.josm.data.osm.Changeset; 015import org.openstreetmap.josm.data.osm.OsmPrimitive; 016import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 017import org.openstreetmap.josm.data.osm.PrimitiveId; 018import org.openstreetmap.josm.data.osm.SimplePrimitiveId; 019import org.openstreetmap.josm.data.osm.history.History; 020import org.openstreetmap.josm.data.osm.history.HistoryDataSet; 021import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive; 022import org.openstreetmap.josm.gui.ExceptionDialogUtil; 023import org.openstreetmap.josm.gui.PleaseWaitRunnable; 024import org.openstreetmap.josm.io.ChangesetQuery; 025import org.openstreetmap.josm.io.OsmServerChangesetReader; 026import org.openstreetmap.josm.io.OsmServerHistoryReader; 027import org.openstreetmap.josm.io.OsmTransferException; 028import org.openstreetmap.josm.tools.CheckParameterUtil; 029import org.xml.sax.SAXException; 030 031/** 032 * Loads the object history of an collection of objects from the 033 * server. 034 * 035 * It provides a fluent API for configuration. 036 * 037 * Sample usage: 038 * 039 * <pre> 040 * HistoryLoadTask task = new HistoryLoadTask() 041 * .add(1, OsmPrimitiveType.NODE) 042 * .add(1233, OsmPrimitiveType.WAY) 043 * .add(37234, OsmPrimitveType.RELATION) 044 * .add(aHistoryItem); 045 * 046 * Main.worker.execute(task); 047 * 048 * </pre> 049 */ 050public class HistoryLoadTask extends PleaseWaitRunnable { 051 052 private boolean canceled; 053 private Exception lastException; 054 private final Set<PrimitiveId> toLoad; 055 private HistoryDataSet loadedData; 056 private OsmServerHistoryReader reader; 057 058 /** 059 * Constructs a new {@code HistoryLoadTask}. 060 */ 061 public HistoryLoadTask() { 062 super(tr("Load history"), true); 063 toLoad = new HashSet<>(); 064 } 065 066 /** 067 * Constructs a new {@code HistoryLoadTask}. 068 * 069 * @param parent the component to be used as reference to find the 070 * parent for {@link org.openstreetmap.josm.gui.PleaseWaitDialog}. 071 * Must not be <code>null</code>. 072 * @throws IllegalArgumentException if parent is <code>null</code> 073 */ 074 public HistoryLoadTask(Component parent) { 075 super(parent, tr("Load history"), true); 076 CheckParameterUtil.ensureParameterNotNull(parent, "parent"); 077 toLoad = new HashSet<>(); 078 } 079 080 /** 081 * Adds an object whose history is to be loaded. 082 * 083 * @param id the object id 084 * @param type the object type 085 * @return this task 086 */ 087 public HistoryLoadTask add(long id, OsmPrimitiveType type) { 088 if (id <= 0) 089 throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' > 0 expected. Got {1}.", "id", id)); 090 CheckParameterUtil.ensureParameterNotNull(type, "type"); 091 SimplePrimitiveId pid = new SimplePrimitiveId(id, type); 092 toLoad.add(pid); 093 return this; 094 } 095 096 /** 097 * Adds an object whose history is to be loaded. 098 * 099 * @param pid the primitive id. Must not be null. Id > 0 required. 100 * @return this task 101 */ 102 public HistoryLoadTask add(PrimitiveId pid) { 103 CheckParameterUtil.ensureValidPrimitiveId(pid, "pid"); 104 toLoad.add(pid); 105 return this; 106 } 107 108 /** 109 * Adds an object to be loaded, the object is specified by a history item. 110 * 111 * @param primitive the history item 112 * @return this task 113 * @throws IllegalArgumentException if primitive is null 114 */ 115 public HistoryLoadTask add(HistoryOsmPrimitive primitive) { 116 CheckParameterUtil.ensureParameterNotNull(primitive, "primitive"); 117 toLoad.add(primitive.getPrimitiveId()); 118 return this; 119 } 120 121 /** 122 * Adds an object to be loaded, the object is specified by an already loaded object history. 123 * 124 * @param history the history. Must not be null. 125 * @return this task 126 * @throws IllegalArgumentException if history is null 127 */ 128 public HistoryLoadTask add(History history) { 129 CheckParameterUtil.ensureParameterNotNull(history, "history"); 130 toLoad.add(history.getPrimitiveId()); 131 return this; 132 } 133 134 /** 135 * Adds an object to be loaded, the object is specified by an OSM primitive. 136 * 137 * @param primitive the OSM primitive. Must not be null. primitive.getId() > 0 required. 138 * @return this task 139 * @throws IllegalArgumentException if the primitive is null 140 * @throws IllegalArgumentException if primitive.getId() <= 0 141 */ 142 public HistoryLoadTask add(OsmPrimitive primitive) { 143 CheckParameterUtil.ensureValidPrimitiveId(primitive, "primitive"); 144 toLoad.add(primitive.getPrimitiveId()); 145 return this; 146 } 147 148 /** 149 * Adds a collection of objects to loaded, specified by a collection of OSM primitives. 150 * 151 * @param primitives the OSM primitives. Must not be <code>null</code>. 152 * <code>primitive.getId() > 0</code> required. 153 * @return this task 154 * @throws IllegalArgumentException if primitives is <code>null</code> 155 * @throws IllegalArgumentException if one of the ids in the collection <= 0 156 */ 157 public HistoryLoadTask add(Collection<? extends OsmPrimitive> primitives) { 158 CheckParameterUtil.ensureParameterNotNull(primitives, "primitives"); 159 for (OsmPrimitive primitive: primitives) { 160 if (primitive == null) { 161 continue; 162 } 163 add(primitive); 164 } 165 return this; 166 } 167 168 @Override 169 protected void cancel() { 170 if (reader != null) { 171 reader.cancel(); 172 } 173 canceled = true; 174 } 175 176 @Override 177 protected void finish() { 178 if (isCanceled()) 179 return; 180 if (lastException != null) { 181 ExceptionDialogUtil.explainException(lastException); 182 return; 183 } 184 HistoryDataSet.getInstance().mergeInto(loadedData); 185 } 186 187 @Override 188 protected void realRun() throws SAXException, IOException, OsmTransferException { 189 loadedData = new HistoryDataSet(); 190 try { 191 progressMonitor.setTicksCount(toLoad.size()); 192 for (PrimitiveId pid: toLoad) { 193 if (canceled) { 194 break; 195 } 196 String msg = ""; 197 switch(pid.getType()) { 198 case NODE: msg = marktr("Loading history for node {0}"); break; 199 case WAY: msg = marktr("Loading history for way {0}"); break; 200 case RELATION: msg = marktr("Loading history for relation {0}"); break; 201 } 202 progressMonitor.indeterminateSubTask(tr(msg, 203 Long.toString(pid.getUniqueId()))); 204 reader = null; 205 HistoryDataSet ds = null; 206 try { 207 reader = new OsmServerHistoryReader(pid.getType(), pid.getUniqueId()); 208 ds = reader.parseHistory(progressMonitor.createSubTaskMonitor(1, false)); 209 // load corresponding changesets (mostly for changeset comment) 210 for (final Changeset i : new OsmServerChangesetReader().queryChangesets( 211 new ChangesetQuery().forChangesetIds(ds.getChangesetIds()), progressMonitor.createSubTaskMonitor(1, false))) { 212 ds.putChangeset(i); 213 } 214 } catch (OsmTransferException e) { 215 if (canceled) 216 return; 217 throw e; 218 } 219 loadedData.mergeInto(ds); 220 } 221 } catch (OsmTransferException e) { 222 lastException = e; 223 return; 224 } 225 } 226 227 public boolean isCanceled() { 228 return canceled; 229 } 230 231 public Exception getLastException() { 232 return lastException; 233 } 234}