Skip to content

Instantly share code, notes, and snippets.

@yushijinhun
Last active February 24, 2016 13:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yushijinhun/31199262cf01fa9abae3 to your computer and use it in GitHub Desktop.
Save yushijinhun/31199262cf01fa9abae3 to your computer and use it in GitHub Desktop.
Gui for jmccc-mcdownloader
package org.to2mbn.jmccc.mcdownloader.gui;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import org.to2mbn.jmccc.mcdownloader.MinecraftDownloader;
import org.to2mbn.jmccc.mcdownloader.MinecraftDownloaderBuilder;
import org.to2mbn.jmccc.mcdownloader.download.combine.AbstractCombinedDownloadCallback;
import org.to2mbn.jmccc.option.MinecraftDirectory;
import org.to2mbn.jmccc.version.Version;
public class DownloaderExample {
public static void main(String[] args) throws Exception {
DownloadTable downloadTable = new DownloadTable();
downloadTable.setRemoveDownloadAfterDone(true);
JFrame window = new JFrame("Downioad");
window.add(new JScrollPane(downloadTable));
window.setSize(640, 480);
window.setVisible(true);
MinecraftDirectory mcdir = new MinecraftDirectory("/home/yushijinhun/.minecraft");
MinecraftDownloader downloader = MinecraftDownloaderBuilder.create().setMaxConnections(8000).setMaxConnectionsPerRouter(2000).build();
downloader.downloadIncrementally(mcdir, "16w06a", downloadTable.display(new AbstractCombinedDownloadCallback<Version>() {
@Override
public void failed(Throwable e) {
e.printStackTrace();
cleanup();
}
@Override
public void done(Version result) {
cleanup();
}
@Override
public void cancelled() {
System.err.println("cancelled");
cleanup();
}
private void cleanup() {
window.dispose();
downloader.shutdown();
}
}));
}
}
package org.to2mbn.jmccc.mcdownloader.gui;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URI;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;
import org.to2mbn.jmccc.mcdownloader.download.DownloadCallback;
import org.to2mbn.jmccc.mcdownloader.download.DownloadCallbackGroup;
import org.to2mbn.jmccc.mcdownloader.download.DownloadTask;
import org.to2mbn.jmccc.mcdownloader.download.combine.CombinedDownloadCallback;
public class DownloadTable extends JTable {
private static class DownloadInfoElement {
private volatile int index;
private URI uri;
private volatile String status;
}
private static final long serialVersionUID = 1L;
/**
* Stores the download tasks The index is the row number
*/
private Vector<DownloadInfoElement> tasks = new Vector<>();
/**
* The table model listeners
*/
private Vector<TableModelListener> listeners = new Vector<>();
private TableModel model;
private boolean removeDownloadAfterDone = true;
private AtomicLong downloadedCount = new AtomicLong();
private AtomicInteger tasksCount = new AtomicInteger();
private Timer speedTimer;
private volatile long currentSpeed;
public DownloadTable() {
model = new DownloadTableModel();
setModel(model);
speedTimer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentSpeed = downloadedCount.getAndSet(0);
updateTotalRow();
}
});
speedTimer.setInitialDelay(0);
}
public <T> CombinedDownloadCallback<T> display() {
return display(null);
}
public <T> CombinedDownloadCallback<T> display(CombinedDownloadCallback<T> callback) {
return new DownloadListener<T>(callback);
}
public boolean isRemoveDownloadAfterDone() {
return removeDownloadAfterDone;
}
public void setRemoveDownloadAfterDone(boolean removeDownloadAfterDone) {
this.removeDownloadAfterDone = removeDownloadAfterDone;
}
private class DownloadTableModel implements TableModel {
private static final int COLUMN_URL = 0;
private static final int COLUMN_STATUS = 1;
@Override
public int getRowCount() {
return tasks.size() + 1;
}
@Override
public int getColumnCount() {
// two columns - URL, Status
return 2;
}
@Override
public String getColumnName(int columnIndex) {
switch (columnIndex) {
case COLUMN_URL:
return "URL";
case COLUMN_STATUS:
return "Status";
default:
throw new IndexOutOfBoundsException(String.valueOf(columnIndex));
}
}
@Override
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case COLUMN_URL:
return URI.class;
case COLUMN_STATUS:
return String.class;
default:
throw new IndexOutOfBoundsException(String.valueOf(columnIndex));
}
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex == tasks.size()) {
switch (columnIndex) {
case 0:
return "Total:";
case 1:
return getCurrentSpeed() / 1024 + "kb/s";
default:
throw new IndexOutOfBoundsException(String.valueOf(columnIndex));
}
}
DownloadInfoElement element = tasks.get(rowIndex);
switch (columnIndex) {
case COLUMN_URL:
return element.uri;
case COLUMN_STATUS:
return element.status;
default:
throw new IndexOutOfBoundsException(String.valueOf(columnIndex));
}
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
throw new UnsupportedOperationException("Modifying this table is not allowed.");
}
@Override
public void addTableModelListener(TableModelListener l) {
listeners.add(l);
}
@Override
public void removeTableModelListener(TableModelListener l) {
listeners.remove(l);
}
}
private class DownloadListener<T> implements CombinedDownloadCallback<T> {
private CombinedDownloadCallback<T> proxied;
public DownloadListener(CombinedDownloadCallback<T> proxied) {
this.proxied = proxied;
}
@Override
public void done(T result) {
if (proxied != null) {
proxied.done(result);
}
}
@Override
public void failed(Throwable e) {
if (proxied != null) {
proxied.failed(e);
}
}
@Override
public void cancelled() {
if (proxied != null) {
proxied.cancelled();
}
}
@Override
public <R> DownloadCallback<R> taskStart(DownloadTask<R> task) {
tasksCountup();
DownloadInfoElement element = new DownloadInfoElement();
element.uri = task.getURI();
element.status = "Waiting";
SwingUtilities.invokeLater(() -> {
int id = tasks.size();
element.index = id;
tasks.add(element);
listeners.forEach(l -> l.tableChanged(new TableModelEvent(model, element.index, element.index, TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT)));
});
DownloadCallback<R> monitor = new DownloadCallback<R>() {
private AtomicLong lastProgress = new AtomicLong();
@Override
public void done(R result) {
tasksCountdown();
element.status = "Done";
elementChanged();
if (removeDownloadAfterDone) {
SwingUtilities.invokeLater(() -> {
int index = element.index;
tasks.remove(index);
for (int i = index; i < tasks.size(); i++) {
tasks.get(i).index--;
}
listeners.forEach(l -> l.tableChanged(new TableModelEvent(model, element.index, element.index, TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE)));
});
}
}
@Override
public void failed(Throwable e) {
tasksCountdown();
element.status = "Failure: " + e;
elementChanged();
}
@Override
public void cancelled() {
tasksCountdown();
element.status = "Cancelled";
elementChanged();
}
@Override
public void updateProgress(long done, long total) {
long downloaded = done - lastProgress.getAndSet(done);
if (downloaded > 0) {
downloadedCount.getAndAdd(downloaded);
}
element.status = String.format("%d/%dkb", done / 1024, total / 1024);
elementChanged();
}
@Override
public void retry(Throwable e, int current, int max) {
lastProgress.set(0);
element.status = String.format("Retry: %d/%d: %s", current, max, e);
elementChanged();
}
private void elementChanged() {
SwingUtilities.invokeLater(() -> listeners.forEach(l -> l.tableChanged(new TableModelEvent(model, element.index, element.index, DownloadTableModel.COLUMN_STATUS, TableModelEvent.UPDATE))));
}
};
DownloadCallback<R> extra = proxied == null ? null : proxied.taskStart(task);
if (extra == null) {
return monitor;
} else {
return DownloadCallbackGroup.group(monitor, extra);
}
}
}
private void tasksCountup() {
int currentCount = tasksCount.getAndIncrement();
if (currentCount < 0) {
throw new IllegalStateException("tasksCount = " + currentCount);
} else if (currentCount == 0) {
speedTimer.start();
}
}
private void tasksCountdown() {
int currentCount = tasksCount.getAndDecrement();
if (currentCount < 1) {
throw new IllegalStateException("tasksCount = " + currentCount);
} else if (currentCount == 1) {
speedTimer.stop();
updateTotalRow();
}
}
private long getCurrentSpeed() {
if (speedTimer.isRunning()) {
return currentSpeed;
} else {
return 0;
}
}
private void updateTotalRow() {
SwingUtilities.invokeLater(() -> {
int elementIndex = tasks.size();
listeners.forEach(l -> l.tableChanged(new TableModelEvent(model, elementIndex, elementIndex, 1, TableModelEvent.UPDATE)));
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment