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.osm.DataSet; 015import org.openstreetmap.josm.data.osm.DataSetMerger; 016import org.openstreetmap.josm.data.osm.Relation; 017import org.openstreetmap.josm.gui.PleaseWaitRunnable; 018import org.openstreetmap.josm.gui.layer.OsmDataLayer; 019import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor; 020import org.openstreetmap.josm.io.OsmApi; 021import org.openstreetmap.josm.io.OsmServerBackreferenceReader; 022import org.openstreetmap.josm.io.OsmTransferException; 023import org.openstreetmap.josm.tools.CheckParameterUtil; 024import org.xml.sax.SAXException; 025 026/** 027 * This is an asynchronous task for loading the parents of a given relation. 028 * 029 * Typical usage: 030 * <pre> 031 * final ParentRelationLoadingTask task = new ParentRelationLoadingTask( 032 * child, // the child relation 033 * Main.getLayerManager().getEditLayer(), // the edit layer 034 * true, // load fully 035 * new PleaseWaitProgressMonitor() // a progress monitor 036 * ); 037 * task.setContinuation( 038 * new Runnable() { 039 * public void run() { 040 * if (task.isCanceled() || task.hasError()) 041 * return; 042 * List<Relation> parents = task.getParents(); 043 * // do something with the parent relations 044 * } 045 * ); 046 * 047 * // start the task 048 * Main.worker.submit(task); 049 * </pre> 050 * 051 */ 052public class ParentRelationLoadingTask extends PleaseWaitRunnable { 053 private boolean canceled; 054 private Exception lastException; 055 private DataSet referrers; 056 private final boolean full; 057 private final OsmDataLayer layer; 058 private final Relation child; 059 private final List<Relation> parents; 060 private Runnable continuation; 061 062 /** 063 * Creates a new task for asynchronously downloading the parents of a child relation. 064 * 065 * @param child the child relation. Must not be null. Must have an id > 0. 066 * @param layer the OSM data layer. Must not be null. 067 * @param full if true, parent relations are fully downloaded (i.e. with their members) 068 * @param monitor the progress monitor to be used 069 * 070 * @throws IllegalArgumentException if child is null 071 * @throws IllegalArgumentException if layer is null 072 * @throws IllegalArgumentException if child.getId() == 0 073 */ 074 public ParentRelationLoadingTask(Relation child, OsmDataLayer layer, boolean full, PleaseWaitProgressMonitor monitor) { 075 super(tr("Download referring relations"), monitor, false /* don't ignore exception */); 076 CheckParameterUtil.ensureValidPrimitiveId(child, "child"); 077 CheckParameterUtil.ensureParameterNotNull(layer, "layer"); 078 referrers = null; 079 this.layer = layer; 080 parents = new ArrayList<>(); 081 this.child = child; 082 this.full = full; 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 getLayer().data.dataSources.addAll(referrers.dataSources); 167 // FIXME: this is necessary because there are dialogs listening 168 // for DataChangeEvents which manipulate Swing components on this thread. 169 // 170 SwingUtilities.invokeLater( 171 new Runnable() { 172 @Override 173 public void run() { 174 getLayer().onPostDownloadFromServer(); 175 } 176 } 177 ); 178 179 if (visitor.getConflicts().isEmpty()) 180 return; 181 getLayer().getConflicts().add(visitor.getConflicts()); 182 JOptionPane.showMessageDialog( 183 Main.parent, 184 tr("There were {0} conflicts during import.", 185 visitor.getConflicts().size()), 186 tr("Warning"), 187 JOptionPane.WARNING_MESSAGE 188 ); 189 } 190 } catch (OsmTransferException e) { 191 if (canceled) { 192 Main.warn(tr("Ignoring exception because task was canceled. Exception: {0}", e.toString())); 193 return; 194 } 195 lastException = e; 196 } 197 } 198}