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.apache.log4j.Logger;
016import org.openstreetmap.josm.gui.PleaseWaitRunnable;
017import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor;
018import org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage;
019import org.openstreetmap.josm.plugins.streetside.StreetsideImage;
020import org.openstreetmap.josm.plugins.streetside.StreetsideImportedImage;
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  final static Logger logger = Logger.getLogger(StreetsideExportManager.class);
037
038  private final ArrayBlockingQueue<BufferedImage> queue = new ArrayBlockingQueue<>(10);
039  private final ArrayBlockingQueue<StreetsideAbstractImage> queueImages = new ArrayBlockingQueue<>(10);
040
041  private int amount;
042  private Set<StreetsideAbstractImage> images;
043  private String path;
044
045  private Thread writer;
046  private ThreadPoolExecutor ex;
047
048  /**
049   * Main constructor.
050   *
051   * @param images Set of {@link StreetsideAbstractImage} objects to be exported.
052   * @param path Export path.
053   */
054  public StreetsideExportManager(Set<StreetsideAbstractImage> images, String path) {
055    super(
056      tr("Downloading") + "…",
057      new PleaseWaitProgressMonitor(tr("Exporting Streetside Images")),
058      true
059    );
060    this.images = images == null ? new HashSet<>() : images;
061    this.path = path;
062    amount = this.images.size();
063  }
064
065  /**
066   * Constructor used to rewrite imported images.
067   *
068   * @param images
069   *          The set of {@link StreetsideImportedImage} object that is going to
070   *          be rewritten.
071   * @throws IOException
072   *           If the file of one of the {@link StreetsideImportedImage} objects
073   *           doesn't contain a picture.
074   */
075  public StreetsideExportManager(List<StreetsideImportedImage> images) throws IOException {
076    this(null, null);
077    for (StreetsideImportedImage image : images) {
078      queue.add(image.getImage());
079      queueImages.add(image);
080    }
081    amount = images.size();
082  }
083
084  @Override
085  protected void cancel() {
086    writer.interrupt();
087    ex.shutdown();
088  }
089
090  @Override
091  protected void realRun() throws IOException {
092    // Starts a writer thread in order to write the pictures on the disk.
093    writer = new StreetsideExportWriterThread(path, queue,
094        queueImages, amount, getProgressMonitor());
095    writer.start();
096    if (path == null) {
097      try {
098        writer.join();
099      } catch (InterruptedException e) {
100        logger.error(e);
101      }
102      return;
103    }
104    ex = new ThreadPoolExecutor(20, 35, 25, TimeUnit.SECONDS,
105      new ArrayBlockingQueue<>(10));
106    for (StreetsideAbstractImage image : images) {
107      if (image instanceof StreetsideImage) {
108        try {
109          ex.execute(new StreetsideExportDownloadThread(
110              (StreetsideImage) image, queue, queueImages));
111        } catch (Exception e) {
112          logger.error(e);
113        }
114      } else if (image instanceof StreetsideImportedImage) {
115        try {
116          queue.put(((StreetsideImportedImage) image).getImage());
117          queueImages.put(image);
118        } catch (InterruptedException e) {
119          logger.error(e);
120        }
121      }
122      try {
123        // If the queue is full, waits for it to have more space
124        // available before executing anything else.
125        while (ex.getQueue().remainingCapacity() == 0) {
126          Thread.sleep(100);
127        }
128      } catch (Exception e) {
129        logger.error(e);
130      }
131    }
132    try {
133      writer.join();
134    } catch (InterruptedException e) {
135      logger.error(e);
136    }
137  }
138
139  @Override
140  protected void finish() {
141  }
142}