001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.gui.io;
003    
004    import static org.openstreetmap.josm.tools.CheckParameterUtil.ensureParameterNotNull;
005    import static org.openstreetmap.josm.tools.I18n.tr;
006    
007    import java.io.IOException;
008    import java.lang.reflect.InvocationTargetException;
009    import java.util.Collection;
010    import java.util.Collections;
011    
012    import javax.swing.SwingUtilities;
013    
014    import org.openstreetmap.josm.data.osm.DataSet;
015    import org.openstreetmap.josm.data.osm.DataSetMerger;
016    import org.openstreetmap.josm.data.osm.Node;
017    import org.openstreetmap.josm.data.osm.OsmPrimitive;
018    import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
019    import org.openstreetmap.josm.data.osm.Relation;
020    import org.openstreetmap.josm.data.osm.Way;
021    import org.openstreetmap.josm.gui.ExceptionDialogUtil;
022    import org.openstreetmap.josm.gui.PleaseWaitRunnable;
023    import org.openstreetmap.josm.gui.layer.OsmDataLayer;
024    import org.openstreetmap.josm.gui.progress.ProgressMonitor;
025    import org.openstreetmap.josm.gui.util.GuiHelper;
026    import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
027    import org.openstreetmap.josm.io.OsmServerObjectReader;
028    import org.openstreetmap.josm.io.OsmTransferException;
029    import org.xml.sax.SAXException;
030    
031    /**
032     * The asynchronous task for updating a collection of objects using multi fetch.
033     *
034     */
035    public class UpdatePrimitivesTask extends PleaseWaitRunnable {
036        private DataSet ds;
037        private boolean canceled;
038        private Exception lastException;
039        private Collection<? extends OsmPrimitive> toUpdate;
040        private OsmDataLayer layer;
041        private MultiFetchServerObjectReader multiObjectReader;
042        private OsmServerObjectReader objectReader;
043    
044        /**
045         * Creates the  task
046         *
047         * @param layer the layer in which primitives are updated. Must not be null.
048         * @param toUpdate a collection of primitives to update from the server. Set to
049         * the empty collection if null.
050         * @throws IllegalArgumentException thrown if layer is null.
051         */
052        public UpdatePrimitivesTask(OsmDataLayer layer, Collection<? extends OsmPrimitive> toUpdate) throws IllegalArgumentException{
053            super(tr("Update objects"), false /* don't ignore exception */);
054            ensureParameterNotNull(layer, "layer");
055            if (toUpdate == null) {
056                toUpdate = Collections.emptyList();
057            }
058            this.layer = layer;
059            this.toUpdate = toUpdate;
060        }
061    
062        @Override
063        protected void cancel() {
064            canceled = true;
065            synchronized(this) {
066                if (multiObjectReader != null) {
067                    multiObjectReader.cancel();
068                }
069                if (objectReader != null) {
070                    objectReader.cancel();
071                }
072            }
073        }
074    
075        @Override
076        protected void finish() {
077            if (canceled)
078                return;
079            if (lastException != null) {
080                ExceptionDialogUtil.explainException(lastException);
081                return;
082            }
083            GuiHelper.runInEDTAndWait(new Runnable() {
084                public void run() {
085                    layer.mergeFrom(ds);
086                    layer.onPostDownloadFromServer();
087                }
088            });
089        }
090    
091        protected void initMultiFetchReaderWithNodes(MultiFetchServerObjectReader reader) {
092            getProgressMonitor().indeterminateSubTask(tr("Initializing nodes to update ..."));
093            for (OsmPrimitive primitive : toUpdate) {
094                if (primitive instanceof Node && !primitive.isNew()) {
095                    reader.append((Node)primitive);
096                } else if (primitive instanceof Way) {
097                    Way way = (Way)primitive;
098                    for (Node node: way.getNodes()) {
099                        if (!node.isNew()) {
100                            reader.append(node);
101                        }
102                    }
103                }
104            }
105        }
106    
107        protected void initMultiFetchReaderWithWays(MultiFetchServerObjectReader reader) {
108            getProgressMonitor().indeterminateSubTask(tr("Initializing ways to update ..."));
109            for (OsmPrimitive primitive : toUpdate) {
110                if (primitive instanceof Way && !primitive.isNew()) {
111                    reader.append((Way)primitive);
112                }
113            }
114        }
115    
116        protected void initMultiFetchReaderWithRelations(MultiFetchServerObjectReader reader) {
117            getProgressMonitor().indeterminateSubTask(tr("Initializing relations to update ..."));
118            for (OsmPrimitive primitive : toUpdate) {
119                if (primitive instanceof Relation && !primitive.isNew()) {
120                    reader.append((Relation)primitive);
121                }
122            }
123        }
124    
125        @Override
126        protected void realRun() throws SAXException, IOException, OsmTransferException {
127            this.ds = new DataSet();
128            DataSet theirDataSet;
129            try {
130                synchronized(this) {
131                    if (canceled) return;
132                    multiObjectReader = new MultiFetchServerObjectReader();
133                }
134                initMultiFetchReaderWithNodes(multiObjectReader);
135                initMultiFetchReaderWithWays(multiObjectReader);
136                initMultiFetchReaderWithRelations(multiObjectReader);
137                theirDataSet = multiObjectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
138                synchronized(this) {
139                    multiObjectReader = null;
140                }
141                DataSetMerger merger = new DataSetMerger(ds, theirDataSet);
142                merger.merge();
143                // a way loaded with MultiFetch may have incomplete nodes because at least one of its
144                // nodes isn't present in the local data set. We therefore fully load all
145                // ways with incomplete nodes.
146                //
147                for (Way w : ds.getWays()) {
148                    if (canceled) return;
149                    if (w.hasIncompleteNodes()) {
150                        synchronized(this) {
151                            if (canceled) return;
152                            objectReader = new OsmServerObjectReader(w.getId(), OsmPrimitiveType.WAY, true /* full */);
153                        }
154                        theirDataSet = objectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
155                        synchronized (this) {
156                            objectReader = null;
157                        }
158                        merger = new DataSetMerger(ds, theirDataSet);
159                        merger.merge();
160                    }
161                }
162            } catch(Exception e) {
163                if (canceled)
164                    return;
165                lastException = e;
166            }
167        }
168    }