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