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 }