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