001 // License: GPL. For details, see LICENSE file.
002 package org.openstreetmap.josm.gui.dialogs.relation;
003
004 import static org.openstreetmap.josm.tools.I18n.tr;
005
006 import java.io.IOException;
007 import java.util.ArrayList;
008 import java.util.List;
009
010 import javax.swing.JOptionPane;
011 import javax.swing.SwingUtilities;
012
013 import org.openstreetmap.josm.Main;
014 import org.openstreetmap.josm.data.osm.DataSet;
015 import org.openstreetmap.josm.data.osm.DataSetMerger;
016 import org.openstreetmap.josm.data.osm.DataSource;
017 import org.openstreetmap.josm.data.osm.Relation;
018 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
019 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
020 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
021 import org.openstreetmap.josm.io.OsmApi;
022 import org.openstreetmap.josm.io.OsmServerBackreferenceReader;
023 import org.openstreetmap.josm.io.OsmTransferException;
024 import org.openstreetmap.josm.tools.CheckParameterUtil;
025 import org.xml.sax.SAXException;
026
027 /**
028 * This is an asynchronous task for loading the parents of a given relation.
029 *
030 * Typical usage:
031 * <pre>
032 * final ParentRelationLoadingTask task = new ParentRelationLoadingTask(
033 * child, // the child relation
034 * Main.main.getEditLayer(), // the edit layer
035 * true, // load fully
036 * new PleaseWaitProgressMonitor() // a progress monitor
037 * );
038 * task.setContinuation(
039 * new Runnable() {
040 * public void run() {
041 * if (task.isCanceled() || task.hasError())
042 * return;
043 * List<Relation> parents = task.getParents();
044 * // do something with the parent relations
045 * }
046 * );
047 *
048 * // start the task
049 * Main.worker.submit(task);
050 * </pre>
051 *
052 */
053 public class ParentRelationLoadingTask extends PleaseWaitRunnable{
054 private boolean canceled;
055 private Exception lastException;
056 private DataSet referrers;
057 private boolean full;
058 private OsmDataLayer layer;
059 private Relation child;
060 private ArrayList<Relation> parents;
061 private Runnable continuation;
062
063 /**
064 * Creates a new task for asynchronously downloading the parents of a child relation.
065 *
066 * @param child the child relation. Must not be null. Must have an id > 0.
067 * @param layer the OSM data layer. Must not be null.
068 * @param full if true, parent relations are fully downloaded (i.e. with their members)
069 * @param monitor the progress monitor to be used
070 *
071 * @exception IllegalArgumentException thrown if child is null
072 * @exception IllegalArgumentException thrown if layer is null
073 * @exception IllegalArgumentException thrown if child.getId() == 0
074 */
075 public ParentRelationLoadingTask(Relation child, OsmDataLayer layer, boolean full, PleaseWaitProgressMonitor monitor ) {
076 super(tr("Download referring relations"), monitor, false /* don't ignore exception */);
077 CheckParameterUtil.ensureValidPrimitiveId(child, "child");
078 CheckParameterUtil.ensureParameterNotNull(layer, "layer");
079 referrers = null;
080 this.layer = layer;
081 parents = new ArrayList<Relation>();
082 this.child = child;
083 }
084
085 /**
086 * Set a continuation which is called upon the job finished.
087 *
088 * @param continuation the continuation
089 */
090 public void setContinuation(Runnable continuation) {
091 this.continuation = continuation;
092 }
093
094 /**
095 * Replies true if this has been canceled by the user.
096 *
097 * @return true if this has been canceled by the user.
098 */
099 public boolean isCanceled() {
100 return canceled;
101 }
102
103 /**
104 * Replies true if an exception has been caught during the execution of this task.
105 *
106 * @return true if an exception has been caught during the execution of this task.
107 */
108 public boolean hasError() {
109 return lastException != null;
110 }
111
112 protected OsmDataLayer getLayer() {
113 return layer;
114 }
115
116 public List<Relation> getParents() {
117 return parents;
118 }
119
120 @Override
121 protected void cancel() {
122 canceled = true;
123 OsmApi.getOsmApi().cancel();
124 }
125
126 protected void showLastException() {
127 String msg = lastException.getMessage();
128 if (msg == null) {
129 msg = lastException.toString();
130 }
131 JOptionPane.showMessageDialog(
132 Main.parent,
133 msg,
134 tr("Error"),
135 JOptionPane.ERROR_MESSAGE
136 );
137 }
138
139 @Override
140 protected void finish() {
141 if (canceled) return;
142 if (lastException != null) {
143 showLastException();
144 return;
145 }
146 parents.clear();
147 for (Relation parent : referrers.getRelations()) {
148 parents.add((Relation) getLayer().data.getPrimitiveById(parent));
149 }
150 if (continuation != null) {
151 continuation.run();
152 }
153 }
154
155 @Override
156 protected void realRun() throws SAXException, IOException, OsmTransferException {
157 try {
158 progressMonitor.indeterminateSubTask(null);
159 OsmServerBackreferenceReader reader = new OsmServerBackreferenceReader(child, full);
160 referrers = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
161 if (referrers != null) {
162 final DataSetMerger visitor = new DataSetMerger(getLayer().data, referrers);
163 visitor.merge();
164
165 // copy the merged layer's data source info
166 for (DataSource src : referrers.dataSources) {
167 getLayer().data.dataSources.add(src);
168 }
169 // FIXME: this is necessary because there are dialogs listening
170 // for DataChangeEvents which manipulate Swing components on this
171 // thread.
172 //
173 SwingUtilities.invokeLater(
174 new Runnable() {
175 public void run() {
176 getLayer().onPostDownloadFromServer();
177 }
178 }
179 );
180
181 if (visitor.getConflicts().isEmpty())
182 return;
183 getLayer().getConflicts().add(visitor.getConflicts());
184 JOptionPane.showMessageDialog(
185 Main.parent,
186 tr("There were {0} conflicts during import.",
187 visitor.getConflicts().size()),
188 tr("Warning"),
189 JOptionPane.WARNING_MESSAGE
190 );
191 }
192 } catch(Exception e) {
193 if (canceled) {
194 System.out.println(tr("Warning: Ignoring exception because task was canceled. Exception: {0}", e.toString()));
195 return;
196 }
197 lastException = e;
198 }
199 }
200 }