    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}