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