source: josm/trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java @ 5241

Revision 4645, 10.8 KB checked in by framm, 5 months ago (diff)

Added three new hooks for plugins, allowing plugins to mess with JOSM's server I/O. Plugins can now add postprocessors to server reading (so they may change what JOSM receives from the OSM server), and they can add postprocessors to server writing (so they can react to an upload action *after* it happened and after IDs for new objects have been assigned - this is in addition to the existing UploadHook which is a server writing preprocessor). Thirdly, plugins can now substitute their own version of OsmWriter. Taken together, these changes make it possible for a plugin to introduce extra information from a non-OSM source into the JOSM editing process, and upon upload split away that extra information and send it to another destination. - Also made minor changes to CredentialDialog to allow plugins to subclass the dialog in case they need their own authentication.

  • Property svn:eol-style set to native
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.io;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
8import java.util.ArrayList;
9import java.util.Collection;
10import java.util.Iterator;
11import java.util.LinkedList;
12import java.util.List;
13
14import org.openstreetmap.josm.data.osm.Changeset;
15import org.openstreetmap.josm.data.osm.IPrimitive;
16import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
17import org.openstreetmap.josm.gui.io.UploadStrategySpecification;
18import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
19import org.openstreetmap.josm.gui.progress.ProgressMonitor;
20import org.openstreetmap.josm.tools.CheckParameterUtil;
21
22/**
23 * Class that uploads all changes to the osm server.
24 *
25 * This is done like this: - All objects with id = 0 are uploaded as new, except
26 * those in deleted, which are ignored - All objects in deleted list are
27 * deleted. - All remaining objects with modified flag set are updated.
28 */
29public class OsmServerWriter {
30    /**
31     * This list contains all successfully processed objects. The caller of
32     * upload* has to check this after the call and update its dataset.
33     *
34     * If a server connection error occurs, this may contain fewer entries
35     * than where passed in the list to upload*.
36     */
37    private Collection<IPrimitive> processed;
38
39    private static ArrayList<OsmServerWritePostprocessor> postprocessors;
40    public static void registerPostprocessor(OsmServerWritePostprocessor pp) {
41        if (postprocessors == null) {
42            postprocessors = new ArrayList<OsmServerWritePostprocessor>();
43        }
44        postprocessors.add(pp);
45    }
46    public static void unregisterPostprocessor(OsmServerWritePostprocessor pp) {
47        if (postprocessors != null) {
48            postprocessors.remove(pp);
49        }
50    }
51
52    private OsmApi api = OsmApi.getOsmApi();
53    private boolean canceled = false;
54
55    private static final int MSECS_PER_SECOND = 1000;
56    private static final int SECONDS_PER_MINUTE = 60;
57    private static final int MSECS_PER_MINUTE = MSECS_PER_SECOND * SECONDS_PER_MINUTE;
58
59    long uploadStartTime;
60
61    public String timeLeft(int progress, int list_size) {
62        long now = System.currentTimeMillis();
63        long elapsed = now - uploadStartTime;
64        if (elapsed == 0) {
65            elapsed = 1;
66        }
67        float uploads_per_ms = (float)progress / elapsed;
68        float uploads_left = list_size - progress;
69        int ms_left = (int)(uploads_left / uploads_per_ms);
70        int minutes_left = ms_left / MSECS_PER_MINUTE;
71        int seconds_left = (ms_left / MSECS_PER_SECOND) % SECONDS_PER_MINUTE ;
72        String time_left_str = Integer.toString(minutes_left) + ":";
73        if (seconds_left < 10) {
74            time_left_str += "0";
75        }
76        time_left_str += Integer.toString(seconds_left);
77        return time_left_str;
78    }
79
80    /**
81     * Uploads the changes individually. Invokes one API call per uploaded primitmive.
82     *
83     * @param primitives the collection of primitives to upload
84     * @param progressMonitor the progress monitor
85     * @throws OsmTransferException thrown if an exception occurs
86     */
87    protected void uploadChangesIndividually(Collection<? extends IPrimitive> primitives, ProgressMonitor progressMonitor) throws OsmTransferException {
88        try {
89            progressMonitor.beginTask(tr("Starting to upload with one request per primitive ..."));
90            progressMonitor.setTicksCount(primitives.size());
91            uploadStartTime = System.currentTimeMillis();
92            for (IPrimitive osm : primitives) {
93                int progress = progressMonitor.getTicks();
94                String time_left_str = timeLeft(progress, primitives.size());
95                String msg = "";
96                switch(OsmPrimitiveType.from(osm)) {
97                case NODE: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading node ''{4}'' (id: {5})"); break;
98                case WAY: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading way ''{4}'' (id: {5})"); break;
99                case RELATION: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading relation ''{4}'' (id: {5})"); break;
100                }
101                progressMonitor.subTask(
102                        tr(msg,
103                                Math.round(100.0*progress/primitives.size()),
104                                progress,
105                                primitives.size(),
106                                time_left_str,
107                                osm.getName() == null ? osm.getId() : osm.getName(),
108                                        osm.getId()));
109                makeApiRequest(osm,progressMonitor);
110                processed.add(osm);
111                progressMonitor.worked(1);
112            }
113        } catch(OsmTransferException e) {
114            throw e;
115        } catch(Exception e) {
116            throw new OsmTransferException(e);
117        } finally {
118            progressMonitor.finishTask();
119        }
120    }
121
122    /**
123     * Upload all changes in one diff upload
124     *
125     * @param primitives the collection of primitives to upload
126     * @param progressMonitor  the progress monitor
127     * @throws OsmTransferException thrown if an exception occurs
128     */
129    protected void uploadChangesAsDiffUpload(Collection<? extends IPrimitive> primitives, ProgressMonitor progressMonitor) throws OsmTransferException {
130        try {
131            progressMonitor.beginTask(tr("Starting to upload in one request ..."));
132            processed.addAll(api.uploadDiff(primitives, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)));
133        } catch(OsmTransferException e) {
134            throw e;
135        } finally {
136            progressMonitor.finishTask();
137        }
138    }
139
140    /**
141     * Upload all changes in one diff upload
142     *
143     * @param primitives the collection of primitives to upload
144     * @param progressMonitor  the progress monitor
145     * @param chunkSize the size of the individual upload chunks. > 0 required.
146     * @throws IllegalArgumentException thrown if chunkSize <= 0
147     * @throws OsmTransferException thrown if an exception occurs
148     */
149    protected void uploadChangesInChunks(Collection<? extends IPrimitive> primitives, ProgressMonitor progressMonitor, int chunkSize) throws OsmTransferException, IllegalArgumentException {
150        if (chunkSize <=0)
151            throw new IllegalArgumentException(tr("Value >0 expected for parameter ''{0}'', got {1}", "chunkSize", chunkSize));
152        try {
153            progressMonitor.beginTask(tr("Starting to upload in chunks..."));
154            List<IPrimitive> chunk = new ArrayList<IPrimitive>(chunkSize);
155            Iterator<? extends IPrimitive> it = primitives.iterator();
156            int numChunks = (int)Math.ceil((double)primitives.size() / (double)chunkSize);
157            int i= 0;
158            while(it.hasNext()) {
159                i++;
160                if (canceled) return;
161                int j = 0;
162                chunk.clear();
163                while(it.hasNext() && j < chunkSize) {
164                    if (canceled) return;
165                    j++;
166                    chunk.add(it.next());
167                }
168                progressMonitor.setCustomText(
169                        trn("({0}/{1}) Uploading {2} object...",
170                                "({0}/{1}) Uploading {2} objects...",
171                                chunk.size(), i, numChunks, chunk.size()));
172                processed.addAll(api.uploadDiff(chunk, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)));
173            }
174        } catch(OsmTransferException e) {
175            throw e;
176        } finally {
177            progressMonitor.finishTask();
178        }
179    }
180
181    /**
182     * Send the dataset to the server.
183     *
184     * @param strategy the upload strategy. Must not be null.
185     * @param primitives list of objects to send
186     * @param changeset the changeset the data is uploaded to. Must not be null.
187     * @param monitor the progress monitor. If null, assumes {@see NullProgressMonitor#INSTANCE}
188     * @throws IllegalArgumentException thrown if changeset is null
189     * @throws IllegalArgumentException thrown if strategy is null
190     * @throws OsmTransferException thrown if something goes wrong
191     */
192    public void uploadOsm(UploadStrategySpecification strategy, Collection<? extends IPrimitive> primitives, Changeset changeset, ProgressMonitor monitor) throws OsmTransferException {
193        CheckParameterUtil.ensureParameterNotNull(changeset, "changeset");
194        processed = new LinkedList<IPrimitive>();
195        monitor = monitor == null ? NullProgressMonitor.INSTANCE : monitor;
196        monitor.beginTask(tr("Uploading data ..."));
197        try {
198            api.initialize(monitor);
199            // check whether we can use diff upload
200            if (changeset.getId() == 0) {
201                api.openChangeset(changeset,monitor.createSubTaskMonitor(0, false));
202            } else {
203                api.updateChangeset(changeset,monitor.createSubTaskMonitor(0, false));
204            }
205            api.setChangeset(changeset);
206            switch(strategy.getStrategy()) {
207            case SINGLE_REQUEST_STRATEGY:
208                uploadChangesAsDiffUpload(primitives,monitor.createSubTaskMonitor(0,false));
209                break;
210            case INDIVIDUAL_OBJECTS_STRATEGY:
211                uploadChangesIndividually(primitives,monitor.createSubTaskMonitor(0,false));
212                break;
213            case CHUNKED_DATASET_STRATEGY:
214                uploadChangesInChunks(primitives,monitor.createSubTaskMonitor(0,false), strategy.getChunkSize());
215                break;
216            }
217        } catch(OsmTransferException e) {
218            throw e;
219        } finally {
220            executePostprocessors(monitor);
221            monitor.finishTask();
222            api.setChangeset(null);
223        }
224    }
225
226    void makeApiRequest(IPrimitive osm, ProgressMonitor progressMonitor) throws OsmTransferException {
227        if (osm.isDeleted()) {
228            api.deletePrimitive(osm, progressMonitor);
229        } else if (osm.isNew()) {
230            api.createPrimitive(osm, progressMonitor);
231        } else {
232            api.modifyPrimitive(osm, progressMonitor);
233        }
234    }
235
236    public void cancel() {
237        this.canceled = true;
238        if (api != null) {
239            api.cancel();
240        }
241    }
242
243    /**
244     * Replies the collection of successfully processed primitives
245     *
246     * @return the collection of successfully processed primitives
247     */
248    public Collection<IPrimitive> getProcessedPrimitives() {
249        return processed;
250    }
251
252    /**
253     * Calls all registered upload postprocessors.
254     */
255    public void executePostprocessors(ProgressMonitor pm) {
256        if (postprocessors != null) {
257            for (OsmServerWritePostprocessor pp : postprocessors) {
258                pp.postprocessUploadedPrimitives(processed, pm);
259            }
260        }
261    }
262}
Note: See TracBrowser for help on using the repository browser.