001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.plugins.streetside.io.export; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.image.BufferedImage; 007import java.io.IOException; 008import java.util.HashSet; 009import java.util.List; 010import java.util.Set; 011import java.util.concurrent.ArrayBlockingQueue; 012import java.util.concurrent.ThreadPoolExecutor; 013import java.util.concurrent.TimeUnit; 014 015import org.openstreetmap.josm.gui.PleaseWaitRunnable; 016import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor; 017import org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage; 018import org.openstreetmap.josm.plugins.streetside.StreetsideImage; 019import org.openstreetmap.josm.plugins.streetside.StreetsideImportedImage; 020import org.openstreetmap.josm.tools.Logging; 021 022/** 023 * Export main thread. Exportation works by creating a 024 * {@link StreetsideExportWriterThread} and several 025 * {@link StreetsideExportDownloadThread}. The second ones download every single 026 * image that is going to be exported and stores them in an 027 * {@link ArrayBlockingQueue}. Then it is picked by the first one and written on 028 * the selected folder. Each image will be named by its key. 029 * 030 * @author nokutu 031 * @see StreetsideExportWriterThread 032 * @see StreetsideExportDownloadThread 033 */ 034public class StreetsideExportManager extends PleaseWaitRunnable { 035 036 private final ArrayBlockingQueue<BufferedImage> queue = new ArrayBlockingQueue<>(10); 037 private final ArrayBlockingQueue<StreetsideAbstractImage> queueImages = new ArrayBlockingQueue<>(10); 038 039 private int amount; 040 private Set<StreetsideAbstractImage> images; 041 private String path; 042 043 private Thread writer; 044 private ThreadPoolExecutor ex; 045 046 /** 047 * Main constructor. 048 * 049 * @param images Set of {@link StreetsideAbstractImage} objects to be exported. 050 * @param path Export path. 051 */ 052 public StreetsideExportManager(Set<StreetsideAbstractImage> images, String path) { 053 super( 054 tr("Downloading") + "…", 055 new PleaseWaitProgressMonitor(tr("Exporting Streetside Images")), 056 true 057 ); 058 this.images = images == null ? new HashSet<>() : images; 059 this.path = path; 060 this.amount = this.images.size(); 061 } 062 063 /** 064 * Constructor used to rewrite imported images. 065 * 066 * @param images 067 * The set of {@link StreetsideImportedImage} object that is going to 068 * be rewritten. 069 * @throws IOException 070 * If the file of one of the {@link StreetsideImportedImage} objects 071 * doesn't contain a picture. 072 */ 073 public StreetsideExportManager(List<StreetsideImportedImage> images) throws IOException { 074 this(null, null); 075 for (StreetsideImportedImage image : images) { 076 this.queue.add(image.getImage()); 077 this.queueImages.add(image); 078 } 079 this.amount = images.size(); 080 } 081 082 @Override 083 protected void cancel() { 084 this.writer.interrupt(); 085 this.ex.shutdown(); 086 } 087 088 @Override 089 protected void realRun() throws IOException { 090 // Starts a writer thread in order to write the pictures on the disk. 091 this.writer = new StreetsideExportWriterThread(this.path, this.queue, 092 this.queueImages, this.amount, this.getProgressMonitor()); 093 this.writer.start(); 094 if (this.path == null) { 095 try { 096 this.writer.join(); 097 } catch (InterruptedException e) { 098 Logging.error(e); 099 } 100 return; 101 } 102 this.ex = new ThreadPoolExecutor(20, 35, 25, TimeUnit.SECONDS, 103 new ArrayBlockingQueue<>(10)); 104 for (StreetsideAbstractImage image : this.images) { 105 if (image instanceof StreetsideImage) { 106 try { 107 this.ex.execute(new StreetsideExportDownloadThread( 108 (StreetsideImage) image, this.queue, this.queueImages)); 109 } catch (Exception e) { 110 Logging.error(e); 111 } 112 } else if (image instanceof StreetsideImportedImage) { 113 try { 114 this.queue.put(((StreetsideImportedImage) image).getImage()); 115 this.queueImages.put(image); 116 } catch (InterruptedException e) { 117 Logging.error(e); 118 } 119 } 120 try { 121 // If the queue is full, waits for it to have more space 122 // available before executing anything else. 123 while (this.ex.getQueue().remainingCapacity() == 0) { 124 Thread.sleep(100); 125 } 126 } catch (Exception e) { 127 Logging.error(e); 128 } 129 } 130 try { 131 this.writer.join(); 132 } catch (InterruptedException e) { 133 Logging.error(e); 134 } 135 } 136 137 @Override 138 protected void finish() { 139 } 140}