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 }