001 //License: GPL. Copyright 2007 by Immanuel Scholz and others
002 package org.openstreetmap.josm.io;
003
004 import static org.openstreetmap.josm.tools.I18n.tr;
005
006 import java.io.IOException;
007 import java.io.StringReader;
008 import java.util.Collection;
009 import java.util.Collections;
010 import java.util.HashMap;
011 import java.util.HashSet;
012 import java.util.Map;
013 import java.util.Set;
014
015 import javax.xml.parsers.ParserConfigurationException;
016 import javax.xml.parsers.SAXParserFactory;
017
018 import org.openstreetmap.josm.data.osm.Changeset;
019 import org.openstreetmap.josm.data.osm.IPrimitive;
020 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
021 import org.openstreetmap.josm.data.osm.PrimitiveId;
022 import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
023 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
024 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
025 import org.openstreetmap.josm.tools.CheckParameterUtil;
026 import org.xml.sax.Attributes;
027 import org.xml.sax.InputSource;
028 import org.xml.sax.Locator;
029 import org.xml.sax.SAXException;
030 import org.xml.sax.helpers.DefaultHandler;
031
032 public class DiffResultProcessor {
033
034 static private class DiffResultEntry {
035 public long new_id;
036 public int new_version;
037 }
038
039 /**
040 * mapping from old id to new id and version, the result of parsing the diff result
041 * replied by the server
042 */
043 private Map<PrimitiveId, DiffResultEntry> diffResults = new HashMap<PrimitiveId, DiffResultEntry>();
044 /**
045 * the set of processed primitives *after* the new id, the new version and the new changeset id
046 * is set
047 */
048 private Set<IPrimitive> processed;
049 /**
050 * the collection of primitives being uploaded
051 */
052 private Collection<? extends IPrimitive> primitives;
053
054 /**
055 * Creates a diff result reader
056 *
057 * @param primitives the collection of primitives which have been uploaded. If null,
058 * assumes an empty collection.
059 */
060 public DiffResultProcessor(Collection<? extends IPrimitive> primitives) {
061 if (primitives == null) {
062 primitives = Collections.emptyList();
063 }
064 this.primitives = primitives;
065 this.processed = new HashSet<IPrimitive>();
066 }
067
068 /**
069 * Parse the response from a diff upload to the OSM API.
070 *
071 * @param diffUploadResponse the response. Must not be null.
072 * @param progressMonitor a progress monitor. Defaults to {@link NullProgressMonitor#INSTANCE} if null
073 * @throws IllegalArgumentException thrown if diffUploadRequest is null
074 * @throws OsmDataParsingException thrown if the diffUploadRequest can't be parsed successfully
075 *
076 */
077 public void parse(String diffUploadResponse, ProgressMonitor progressMonitor) throws OsmDataParsingException {
078 if (progressMonitor == null) {
079 progressMonitor = NullProgressMonitor.INSTANCE;
080 }
081 CheckParameterUtil.ensureParameterNotNull(diffUploadResponse, "diffUploadResponse");
082 try {
083 progressMonitor.beginTask(tr("Parsing response from server..."));
084 InputSource inputSource = new InputSource(new StringReader(diffUploadResponse));
085 SAXParserFactory.newInstance().newSAXParser().parse(inputSource, new Parser());
086 } catch(IOException e) {
087 throw new OsmDataParsingException(e);
088 } catch(ParserConfigurationException e) {
089 throw new OsmDataParsingException(e);
090 } catch(OsmDataParsingException e) {
091 throw e;
092 } catch(SAXException e) {
093 throw new OsmDataParsingException(e);
094 } finally {
095 progressMonitor.finishTask();
096 }
097 }
098
099 /**
100 * Postprocesses the diff result read and parsed from the server.
101 *
102 * Uploaded objects are assigned their new id (if they got assigned a new
103 * id by the server), their new version (if the version was incremented),
104 * and the id of the changeset to which they were uploaded.
105 *
106 * @param cs the current changeset. Ignored if null.
107 * @param monitor the progress monitor. Set to {@link NullProgressMonitor#INSTANCE} if null
108 * @return the collection of processed primitives
109 */
110 protected Set<IPrimitive> postProcess(Changeset cs, ProgressMonitor monitor) {
111 if (monitor == null) {
112 monitor = NullProgressMonitor.INSTANCE;
113 }
114 try {
115 monitor.beginTask("Postprocessing uploaded data ...");
116 monitor.setTicksCount(primitives.size());
117 monitor.setTicks(0);
118 for (IPrimitive p : primitives) {
119 monitor.worked(1);
120 DiffResultEntry entry = diffResults.get(p.getPrimitiveId());
121 if (entry == null) {
122 continue;
123 }
124 processed.add(p);
125 if (!p.isDeleted()) {
126 p.setOsmId(entry.new_id, entry.new_version);
127 p.setVisible(true);
128 } else {
129 p.setVisible(false);
130 }
131 if (cs != null && !cs.isNew()) {
132 p.setChangesetId(cs.getId());
133 }
134 }
135 return processed;
136 } finally {
137 monitor.finishTask();
138 }
139 }
140
141 private class Parser extends DefaultHandler {
142 private Locator locator;
143
144 @Override
145 public void setDocumentLocator(Locator locator) {
146 this.locator = locator;
147 }
148
149 protected void throwException(String msg) throws OsmDataParsingException{
150 throw new OsmDataParsingException(msg).rememberLocation(locator);
151 }
152
153 @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
154 try {
155 if (qName.equals("diffResult")) {
156 // the root element, ignore
157 } else if (qName.equals("node") || qName.equals("way") || qName.equals("relation")) {
158
159 PrimitiveId id = new SimplePrimitiveId(
160 Long.parseLong(atts.getValue("old_id")),
161 OsmPrimitiveType.fromApiTypeName(qName)
162 );
163 DiffResultEntry entry = new DiffResultEntry();
164 if (atts.getValue("new_id") != null) {
165 entry.new_id = Long.parseLong(atts.getValue("new_id"));
166 }
167 if (atts.getValue("new_version") != null) {
168 entry.new_version = Integer.parseInt(atts.getValue("new_version"));
169 }
170 diffResults.put(id, entry);
171 } else {
172 throwException(tr("Unexpected XML element with name ''{0}''", qName));
173 }
174 } catch (NumberFormatException e) {
175 throw new OsmDataParsingException(e).rememberLocation(locator);
176 }
177 }
178 }
179 }