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

Last change on this file since 13453 was 13453, checked in by Don-vip, 8 months ago

fix #8039, fix #10456: final fixes for the read-only/locked layers:

  • rename "read-only" to "locked" (in XML and Java classes/interfaces)
  • add a new download policy (true/never) to allow private layers forbidding only to download data, but allowing everything else

This leads to:

  • normal layers: download allowed, modifications allowed, upload allowed
  • private layers: download allowed or not (download=true/never), modifications allowed, upload allowed or not (upload=true/discouraged/never)
  • locked layers: nothing allowed, the data cannot be modified in any way
  • Property svn:eol-style set to native
File size: 7.9 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        boolean readOnly = false;
124        if (ds != null) {
125            readOnly = ds.isLocked();
126            if (readOnly) {
127                ds.unlock();
128            }
129            ds.beginUpdate();
130        }
131        try {
132            monitor.beginTask("Postprocessing uploaded data ...");
133            monitor.setTicksCount(primitives.size());
134            monitor.setTicks(0);
135            for (OsmPrimitive p : primitives) {
136                monitor.worked(1);
137                DiffResultEntry entry = diffResults.get(p.getPrimitiveId());
138                if (entry == null) {
139                    continue;
140                }
141                processed.add(p);
142                if (!p.isDeleted()) {
143                    p.setOsmId(entry.newId, entry.newVersion);
144                    p.setVisible(true);
145                } else {
146                    p.setVisible(false);
147                }
148                if (cs != null && !cs.isNew()) {
149                    p.setChangesetId(cs.getId());
150                    p.setUser(cs.getUser());
151                    // TODO is there a way to obtain the timestamp for non-closed changesets?
152                    p.setTimestamp(Utils.firstNonNull(cs.getClosedAt(), new Date()));
153                }
154            }
155            return processed;
156        } finally {
157            if (ds != null) {
158                ds.endUpdate();
159                if (readOnly) {
160                    ds.lock();
161                }
162            }
163            monitor.finishTask();
164        }
165    }
166
167    private class Parser extends DefaultHandler {
168        private Locator locator;
169
170        @Override
171        public void setDocumentLocator(Locator locator) {
172            this.locator = locator;
173        }
174
175        protected void throwException(String msg) throws XmlParsingException {
176            throw new XmlParsingException(msg).rememberLocation(locator);
177        }
178
179        @Override
180        public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
181            try {
182                switch (qName) {
183                case "diffResult":
184                    // the root element, ignore
185                    break;
186                case "node":
187                case "way":
188                case "relation":
189                    PrimitiveId id = new SimplePrimitiveId(
190                            Long.parseLong(atts.getValue("old_id")),
191                            OsmPrimitiveType.fromApiTypeName(qName)
192                    );
193                    DiffResultEntry entry = new DiffResultEntry();
194                    if (atts.getValue("new_id") != null) {
195                        entry.newId = Long.parseLong(atts.getValue("new_id"));
196                    }
197                    if (atts.getValue("new_version") != null) {
198                        entry.newVersion = Integer.parseInt(atts.getValue("new_version"));
199                    }
200                    diffResults.put(id, entry);
201                    break;
202                default:
203                    throwException(tr("Unexpected XML element with name ''{0}''", qName));
204                }
205            } catch (NumberFormatException e) {
206                throw new XmlParsingException(e).rememberLocation(locator);
207            }
208        }
209    }
210}
Note: See TracBrowser for help on using the repository browser.