001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.gui.dialogs.changeset;
003    
004    import static org.openstreetmap.josm.tools.I18n.tr;
005    
006    import java.awt.Component;
007    import java.io.IOException;
008    import java.lang.reflect.InvocationTargetException;
009    import java.util.Collection;
010    import java.util.Collections;
011    import java.util.HashSet;
012    import java.util.Set;
013    
014    import javax.swing.SwingUtilities;
015    
016    import org.openstreetmap.josm.Main;
017    import org.openstreetmap.josm.data.osm.Changeset;
018    import org.openstreetmap.josm.data.osm.ChangesetCache;
019    import org.openstreetmap.josm.gui.ExceptionDialogUtil;
020    import org.openstreetmap.josm.gui.PleaseWaitRunnable;
021    import org.openstreetmap.josm.io.OsmServerChangesetReader;
022    import org.openstreetmap.josm.io.OsmTransferException;
023    import org.openstreetmap.josm.tools.BugReportExceptionHandler;
024    import org.openstreetmap.josm.tools.CheckParameterUtil;
025    import org.openstreetmap.josm.tools.ExceptionUtil;
026    import org.xml.sax.SAXException;
027    
028    /**
029     * This is an asynchronous task for downloading a collection of changests from the OSM
030     * server.
031     *
032     * The  task only downloads the changeset properties without the changeset content. It
033     * updates the global {@link ChangesetCache}.
034     *
035     */
036    public class ChangesetHeaderDownloadTask extends PleaseWaitRunnable implements ChangesetDownloadTask{
037    
038        /**
039         * Builds a download task from for a collection of changesets.
040         *
041         * Ignores null values and changesets with {@link Changeset#isNew()} == true.
042         *
043         * @param changesets the collection of changesets. Assumes an empty collection if null.
044         * @return the download task
045         */
046        static public ChangesetHeaderDownloadTask buildTaskForChangesets(Collection<Changeset> changesets) {
047            return buildTaskForChangesets(Main.parent, changesets);
048        }
049    
050        /**
051         * Builds a download task from for a collection of changesets.
052         *
053         * Ignores null values and changesets with {@link Changeset#isNew()} == true.
054         *
055         * @param parent the parent component relative to which the {@link PleaseWaitDialog} is displayed.
056         * Must not be null.
057         * @param changesets the collection of changesets. Assumes an empty collection if null.
058         * @return the download task
059         * @throws IllegalArgumentException thrown if parent is null
060         */
061        static public ChangesetHeaderDownloadTask buildTaskForChangesets(Component parent, Collection<Changeset> changesets) {
062            CheckParameterUtil.ensureParameterNotNull(parent, "parent");
063            if (changesets == null) {
064                changesets = Collections.emptyList();
065            }
066    
067            HashSet<Integer> ids = new HashSet<Integer>();
068            for (Changeset cs: changesets) {
069                if (cs == null || cs.isNew()) {
070                    continue;
071                }
072                ids.add(cs.getId());
073            }
074            if (parent == null)
075                return new ChangesetHeaderDownloadTask(ids);
076            else
077                return new ChangesetHeaderDownloadTask(parent, ids);
078    
079        }
080    
081        private Set<Integer> idsToDownload;
082        private OsmServerChangesetReader reader;
083        private boolean canceled;
084        private Exception lastException;
085        private Set<Changeset> downloadedChangesets;
086    
087        protected void init(Collection<Integer> ids) {
088            if (ids == null) {
089                ids = Collections.emptyList();
090            }
091            idsToDownload = new HashSet<Integer>();
092            if (ids == null ||  ids.isEmpty())
093                return;
094            for (int id: ids) {
095                if (id <= 0) {
096                    continue;
097                }
098                idsToDownload.add(id);
099            }
100        }
101    
102        /**
103         * Creates the download task for a collection of changeset ids. Uses a {@link PleaseWaitDialog}
104         * whose parent is {@link Main#parent}.
105         *
106         * Null ids or or ids <= 0 in the id collection are ignored.
107         *
108         * @param ids the collection of ids. Empty collection assumed if null.
109         */
110        public ChangesetHeaderDownloadTask(Collection<Integer> ids) {
111            // parent for dialog is Main.parent
112            super(tr("Download changesets"), false /* don't ignore exceptions */);
113            init(ids);
114        }
115    
116        /**
117         * Creates the download task for a collection of changeset ids. Uses a {@link PleaseWaitDialog}
118         * whose parent is the parent window of <code>dialogParent</code>.
119         *
120         * Null ids or or ids <= 0 in the id collection are ignored.
121         *
122         * @param dialogParent the parent reference component for the {@link PleaseWaitDialog}. Must not be null.
123         * @param ids the collection of ids. Empty collection assumed if null.
124         * @throws IllegalArgumentException thrown if dialogParent is null
125         */
126        public ChangesetHeaderDownloadTask(Component dialogParent, Collection<Integer> ids) throws IllegalArgumentException{
127            super(dialogParent,tr("Download changesets"), false /* don't ignore exceptions */);
128            init(ids);
129        }
130    
131        @Override
132        protected void cancel() {
133            canceled = true;
134            synchronized (this) {
135                if (reader != null) {
136                    reader.cancel();
137                }
138            }
139        }
140    
141        @Override
142        protected void finish() {
143            if (canceled)
144                return;
145            if (lastException != null) {
146                ExceptionDialogUtil.explainException(lastException);
147            }
148            Runnable r = new Runnable() {
149                public void run() {
150                    ChangesetCache.getInstance().update(downloadedChangesets);
151                }
152            };
153    
154            if (SwingUtilities.isEventDispatchThread()) {
155                r.run();
156            } else {
157                try {
158                    SwingUtilities.invokeAndWait(r);
159                } catch(InterruptedException e) {
160                    e.printStackTrace();
161                } catch(InvocationTargetException e) {
162                    Throwable t = e.getTargetException();
163                    if (t instanceof RuntimeException) {
164                        BugReportExceptionHandler.handleException(t);
165                    } else if (t instanceof Exception){
166                        ExceptionUtil.explainException(e);
167                    } else {
168                        BugReportExceptionHandler.handleException(t);
169                    }
170                }
171            }
172        }
173    
174        @Override
175        protected void realRun() throws SAXException, IOException, OsmTransferException {
176            try {
177                synchronized (this) {
178                    reader = new OsmServerChangesetReader();
179                }
180                downloadedChangesets = new HashSet<Changeset>();
181                downloadedChangesets.addAll(reader.readChangesets(idsToDownload, getProgressMonitor().createSubTaskMonitor(0, false)));
182            } catch(OsmTransferException e) {
183                if (canceled)
184                    // ignore exception if canceled
185                    return;
186                // remember other exceptions
187                lastException = e;
188            }
189        }
190    
191        /* ------------------------------------------------------------------------------- */
192        /* interface ChangesetDownloadTask                                                 */
193        /* ------------------------------------------------------------------------------- */
194        public Set<Changeset> getDownloadedChangesets() {
195            return downloadedChangesets;
196        }
197    
198        public boolean isCanceled() {
199            return canceled;
200        }
201    
202        public boolean isFailed() {
203            return lastException != null;
204        }
205    }