source: josm/trunk/src/org/openstreetmap/josm/io/DiffResultProcessor.java@ 13346

Last change on this file since 13346 was 12470, checked in by bastiK, 7 years ago

see #14794 - javadoc

  • Property svn:eol-style set to native
File size: 7.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.IOException;
7import java.io.StringReader;
8import java.util.Collection;
9import java.util.Collections;
10import java.util.Date;
11import java.util.HashMap;
12import java.util.HashSet;
13import java.util.Map;
14import java.util.Optional;
15import java.util.Set;
16
17import javax.xml.parsers.ParserConfigurationException;
18
19import org.openstreetmap.josm.data.osm.Changeset;
20import org.openstreetmap.josm.data.osm.DataSet;
21import org.openstreetmap.josm.data.osm.OsmPrimitive;
22import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
23import org.openstreetmap.josm.data.osm.PrimitiveId;
24import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
25import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
26import org.openstreetmap.josm.gui.progress.ProgressMonitor;
27import org.openstreetmap.josm.tools.CheckParameterUtil;
28import org.openstreetmap.josm.tools.Utils;
29import org.openstreetmap.josm.tools.XmlParsingException;
30import org.xml.sax.Attributes;
31import org.xml.sax.InputSource;
32import org.xml.sax.Locator;
33import org.xml.sax.SAXException;
34import org.xml.sax.helpers.DefaultHandler;
35
36/**
37 * Helper class to process the OSM API server response to a "diff" upload.
38 * <p>
39 * New primitives (uploaded with negative id) will be assigned a positive id, etc.
40 * The goal is to have a clean state, just like a fresh download (assuming no
41 * concurrent uploads by other users have happened in the meantime).
42 * <p>
43 * @see <a href="https://wiki.openstreetmap.org/wiki/API_v0.6#Response_10">API 0.6 diff upload response</a>
44 */
45public class DiffResultProcessor {
46
47 private static class DiffResultEntry {
48 private long newId;
49 private int newVersion;
50 }
51
52 /**
53 * mapping from old id to new id and version, the result of parsing the diff result
54 * replied by the server
55 */
56 private final Map<PrimitiveId, DiffResultEntry> diffResults = new HashMap<>();
57 /**
58 * the set of processed primitives *after* the new id, the new version and the new changeset id is set
59 */
60 private final Set<OsmPrimitive> processed;
61 /**
62 * the collection of primitives being uploaded
63 */
64 private final Collection<? extends OsmPrimitive> primitives;
65
66 /**
67 * Creates a diff result reader
68 *
69 * @param primitives the collection of primitives which have been uploaded. If null,
70 * assumes an empty collection.
71 */
72 public DiffResultProcessor(Collection<? extends OsmPrimitive> primitives) {
73 this.primitives = Optional.ofNullable(primitives).orElseGet(Collections::emptyList);
74 this.processed = new HashSet<>();
75 }
76
77 /**
78 * Parse the response from a diff upload to the OSM API.
79 *
80 * @param diffUploadResponse the response. Must not be null.
81 * @param progressMonitor a progress monitor. Defaults to {@link NullProgressMonitor#INSTANCE} if null
82 * @throws IllegalArgumentException if diffUploadRequest is null
83 * @throws XmlParsingException if the diffUploadRequest can't be parsed successfully
84 *
85 */
86 public void parse(String diffUploadResponse, ProgressMonitor progressMonitor) throws XmlParsingException {
87 if (progressMonitor == null) {
88 progressMonitor = NullProgressMonitor.INSTANCE;
89 }
90 CheckParameterUtil.ensureParameterNotNull(diffUploadResponse, "diffUploadResponse");
91 try {
92 progressMonitor.beginTask(tr("Parsing response from server..."));
93 InputSource inputSource = new InputSource(new StringReader(diffUploadResponse));
94 Utils.parseSafeSAX(inputSource, new Parser());
95 } catch (XmlParsingException e) {
96 throw e;
97 } catch (IOException | ParserConfigurationException | SAXException e) {
98 throw new XmlParsingException(e);
99 } finally {
100 progressMonitor.finishTask();
101 }
102 }
103
104 /**
105 * Postprocesses the diff result read and parsed from the server.
106 *
107 * Uploaded objects are assigned their new id (if they got assigned a new
108 * id by the server), their new version (if the version was incremented),
109 * and the id of the changeset to which they were uploaded.
110 *
111 * @param cs the current changeset. Ignored if null.
112 * @param monitor the progress monitor. Set to {@link NullProgressMonitor#INSTANCE} if null
113 * @return the collection of processed primitives
114 */
115 protected Set<OsmPrimitive> postProcess(Changeset cs, ProgressMonitor monitor) {
116 if (monitor == null) {
117 monitor = NullProgressMonitor.INSTANCE;
118 }
119 DataSet ds = null;
120 if (!primitives.isEmpty()) {
121 ds = primitives.iterator().next().getDataSet();
122 }
123 if (ds != null) {
124 ds.beginUpdate();
125 }
126 try {
127 monitor.beginTask("Postprocessing uploaded data ...");
128 monitor.setTicksCount(primitives.size());
129 monitor.setTicks(0);
130 for (OsmPrimitive p : primitives) {
131 monitor.worked(1);
132 DiffResultEntry entry = diffResults.get(p.getPrimitiveId());
133 if (entry == null) {
134 continue;
135 }
136 processed.add(p);
137 if (!p.isDeleted()) {
138 p.setOsmId(entry.newId, entry.newVersion);
139 p.setVisible(true);
140 } else {
141 p.setVisible(false);
142 }
143 if (cs != null && !cs.isNew()) {
144 p.setChangesetId(cs.getId());
145 p.setUser(cs.getUser());
146 // TODO is there a way to obtain the timestamp for non-closed changesets?
147 p.setTimestamp(Utils.firstNonNull(cs.getClosedAt(), new Date()));
148 }
149 }
150 return processed;
151 } finally {
152 if (ds != null) {
153 ds.endUpdate();
154 }
155 monitor.finishTask();
156 }
157 }
158
159 private class Parser extends DefaultHandler {
160 private Locator locator;
161
162 @Override
163 public void setDocumentLocator(Locator locator) {
164 this.locator = locator;
165 }
166
167 protected void throwException(String msg) throws XmlParsingException {
168 throw new XmlParsingException(msg).rememberLocation(locator);
169 }
170
171 @Override
172 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
173 try {
174 switch (qName) {
175 case "diffResult":
176 // the root element, ignore
177 break;
178 case "node":
179 case "way":
180 case "relation":
181 PrimitiveId id = new SimplePrimitiveId(
182 Long.parseLong(atts.getValue("old_id")),
183 OsmPrimitiveType.fromApiTypeName(qName)
184 );
185 DiffResultEntry entry = new DiffResultEntry();
186 if (atts.getValue("new_id") != null) {
187 entry.newId = Long.parseLong(atts.getValue("new_id"));
188 }
189 if (atts.getValue("new_version") != null) {
190 entry.newVersion = Integer.parseInt(atts.getValue("new_version"));
191 }
192 diffResults.put(id, entry);
193 break;
194 default:
195 throwException(tr("Unexpected XML element with name ''{0}''", qName));
196 }
197 } catch (NumberFormatException e) {
198 throw new XmlParsingException(e).rememberLocation(locator);
199 }
200 }
201 }
202}
Note: See TracBrowser for help on using the repository browser.