001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.relation; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.io.IOException; 007import java.util.ArrayList; 008import java.util.List; 009 010import javax.swing.JOptionPane; 011import javax.swing.SwingUtilities; 012 013import org.openstreetmap.josm.Main; 014import org.openstreetmap.josm.data.DataSource; 015import org.openstreetmap.josm.data.osm.DataSet; 016import org.openstreetmap.josm.data.osm.DataSetMerger; 017import org.openstreetmap.josm.data.osm.Relation; 018import org.openstreetmap.josm.gui.PleaseWaitRunnable; 019import org.openstreetmap.josm.gui.layer.OsmDataLayer; 020import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor; 021import org.openstreetmap.josm.io.OsmApi; 022import org.openstreetmap.josm.io.OsmServerBackreferenceReader; 023import org.openstreetmap.josm.io.OsmTransferException; 024import org.openstreetmap.josm.tools.CheckParameterUtil; 025import 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 */ 053public 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 List<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 * @throws IllegalArgumentException if child is null 072 * @throws IllegalArgumentException if layer is null 073 * @throws IllegalArgumentException 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<>(); 082 this.child = child; 083 this.full = full; 084 } 085 086 /** 087 * Set a continuation which is called upon the job finished. 088 * 089 * @param continuation the continuation 090 */ 091 public void setContinuation(Runnable continuation) { 092 this.continuation = continuation; 093 } 094 095 /** 096 * Replies true if this has been canceled by the user. 097 * 098 * @return true if this has been canceled by the user. 099 */ 100 public boolean isCanceled() { 101 return canceled; 102 } 103 104 /** 105 * Replies true if an exception has been caught during the execution of this task. 106 * 107 * @return true if an exception has been caught during the execution of this task. 108 */ 109 public boolean hasError() { 110 return lastException != null; 111 } 112 113 protected OsmDataLayer getLayer() { 114 return layer; 115 } 116 117 public List<Relation> getParents() { 118 return parents; 119 } 120 121 @Override 122 protected void cancel() { 123 canceled = true; 124 OsmApi.getOsmApi().cancel(); 125 } 126 127 protected void showLastException() { 128 String msg = lastException.getMessage(); 129 if (msg == null) { 130 msg = lastException.toString(); 131 } 132 JOptionPane.showMessageDialog( 133 Main.parent, 134 msg, 135 tr("Error"), 136 JOptionPane.ERROR_MESSAGE 137 ); 138 } 139 140 @Override 141 protected void finish() { 142 if (canceled) return; 143 if (lastException != null) { 144 showLastException(); 145 return; 146 } 147 parents.clear(); 148 for (Relation parent : referrers.getRelations()) { 149 parents.add((Relation) getLayer().data.getPrimitiveById(parent)); 150 } 151 if (continuation != null) { 152 continuation.run(); 153 } 154 } 155 156 @Override 157 protected void realRun() throws SAXException, IOException, OsmTransferException { 158 try { 159 progressMonitor.indeterminateSubTask(null); 160 OsmServerBackreferenceReader reader = new OsmServerBackreferenceReader(child, full); 161 referrers = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 162 if (referrers != null) { 163 final DataSetMerger visitor = new DataSetMerger(getLayer().data, referrers); 164 visitor.merge(); 165 166 // copy the merged layer's data source info 167 for (DataSource src : referrers.dataSources) { 168 getLayer().data.dataSources.add(src); 169 } 170 // FIXME: this is necessary because there are dialogs listening 171 // for DataChangeEvents which manipulate Swing components on this 172 // thread. 173 // 174 SwingUtilities.invokeLater( 175 new Runnable() { 176 @Override 177 public void run() { 178 getLayer().onPostDownloadFromServer(); 179 } 180 } 181 ); 182 183 if (visitor.getConflicts().isEmpty()) 184 return; 185 getLayer().getConflicts().add(visitor.getConflicts()); 186 JOptionPane.showMessageDialog( 187 Main.parent, 188 tr("There were {0} conflicts during import.", 189 visitor.getConflicts().size()), 190 tr("Warning"), 191 JOptionPane.WARNING_MESSAGE 192 ); 193 } 194 } catch (Exception e) { 195 if (canceled) { 196 Main.warn(tr("Ignoring exception because task was canceled. Exception: {0}", e.toString())); 197 return; 198 } 199 lastException = e; 200 } 201 } 202}