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