    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}