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}