package net.sf.openrocket.gui.util; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.net.URL; import javax.swing.SwingWorker; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.file.GeneralRocketLoader; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.MathUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A SwingWorker thread that opens a rocket design file. * * @author Sampo Niskanen <sampo.niskanen@iki.fi> */ public class OpenFileWorker extends SwingWorker<OpenRocketDocument, Void> { private static final Logger log = LoggerFactory.getLogger(OpenFileWorker.class); private final File file; private final URL jarURL; private final GeneralRocketLoader loader; public OpenFileWorker(File file) { this.file = file; this.jarURL = null; loader = new GeneralRocketLoader(file); } public OpenFileWorker(URL fileURL) { this.jarURL = fileURL; this.file = null; loader = new GeneralRocketLoader(fileURL); } public GeneralRocketLoader getRocketLoader() { return loader; } @Override protected OpenRocketDocument doInBackground() throws Exception { InputStream is; // Get the correct input stream if (file != null) { is = new FileInputStream(file); } else { is = jarURL.openStream(); } // Buffer stream unless already buffered if (!(is instanceof BufferedInputStream)) { is = new BufferedInputStream(is); } // Encapsulate in a ProgressInputStream is = new ProgressInputStream(is); try { OpenRocketDocument document = loader.load(is); // Set document state document.setFile(file); document.setSaved(true); return document; } finally { try { is.close(); } catch (Exception e) { Application.getExceptionHandler().handleErrorCondition("Error closing file", e); } } } private class ProgressInputStream extends FilterInputStream { private final int size; private int readBytes = 0; private int progress = -1; protected ProgressInputStream(InputStream in) { super(in); int s; try { s = in.available(); } catch (IOException e) { log.info("Exception while estimating available bytes!", e); s = 0; } size = Math.max(s, 1); } @Override public int read() throws IOException { int c = in.read(); if (c >= 0) { readBytes++; setProgress(); } if (isCancelled()) { throw new InterruptedIOException("OpenFileWorker was cancelled"); } return c; } @Override public int read(byte[] b, int off, int len) throws IOException { int n = in.read(b, off, len); if (n > 0) { readBytes += n; setProgress(); } if (isCancelled()) { throw new InterruptedIOException("OpenFileWorker was cancelled"); } return n; } @Override public int read(byte[] b) throws IOException { int n = in.read(b); if (n > 0) { readBytes += n; setProgress(); } if (isCancelled()) { throw new InterruptedIOException("OpenFileWorker was cancelled"); } return n; } @Override public long skip(long n) throws IOException { long nr = in.skip(n); if (nr > 0) { readBytes += nr; setProgress(); } if (isCancelled()) { throw new InterruptedIOException("OpenFileWorker was cancelled"); } return nr; } @Override public synchronized void reset() throws IOException { in.reset(); readBytes = size - in.available(); setProgress(); if (isCancelled()) { throw new InterruptedIOException("OpenFileWorker was cancelled"); } } private void setProgress() { int p = MathUtil.clamp(readBytes * 100 / size, 0, 100); if (progress != p) { progress = p; OpenFileWorker.this.setProgress(progress); } } } }