Coding section is divided into four parts. i. ii. iii. iv.
Download (front) DownloadManager DownloadTableModel ProgressRenderer
1. Download.
import [Link].*; import [Link].*; import [Link].*; // This class downloads a file from a URL. class Download extends Observable implements Runnable { // Max size of download buffer. private static final int MAX_BUFFER_SIZE = 1024; // These are the status names. public static final String STATUSES[] = {"Downloading", "Paused", "Complete", "Cancelled", "Error"}; // These are the status codes. public static final int DOWNLOADING = 0; public static final int PAUSED = 1; public static final int COMPLETE = 2; public static final int CANCELLED = 3; public static final int ERROR = 4; private URL url; // download URL private int size; // size of download in bytes private int downloaded; // number of bytes downloaded private int status; // current status of download // Constructor for Download. public Download(URL url) { [Link] = url; size = -1; downloaded = 0; status = DOWNLOADING; // Begin the download. download(); } // Get this download's URL. public String getUrl() { return [Link](); } // Get this download's size. public int getSize() { return size; }
// Get this download's progress. public float getProgress() { return ((float) downloaded / size) * 100; } // Get this download's status. public int getStatus() { return status; } // Pause this download. public void pause() { status = PAUSED; stateChanged(); } // Resume this download. public void resume() { status = DOWNLOADING; stateChanged(); download(); } // Cancel this download. public void cancel() { status = CANCELLED; stateChanged(); } // Mark this download as having an error. private void error() { status = ERROR; stateChanged(); } // Start or resume downloading. private void download() { Thread thread = new Thread(this); [Link](); } // Get file name portion of URL. private String getFileName(URL url) { String fileName = [Link](); return [Link]([Link]('/') + 1); } // Download file. public void run() { RandomAccessFile file = null; InputStream stream = null; try { // Open connection to URL.
HttpURLConnection connection = (HttpURLConnection) [Link](); // Specify what portion of file to download. [Link]("Range", "bytes=" + downloaded + "-"); // Connect to server. [Link](); // Make sure response code is in the 200 range. if ([Link]() / 100 != 2) { error(); } // Check for valid content length. int contentLength = [Link](); if (contentLength < 1) { error(); } /* Set the size for this download if it hasn't been already set. */ if (size == -1) { size = contentLength; stateChanged(); } // Open file and seek to the end of it. file = new RandomAccessFile(getFileName(url), "rw"); [Link](downloaded); stream = [Link](); while (status == DOWNLOADING) { /* Size buffer according to how much of the file is left to download. */ byte buffer[]; if (size - downloaded > MAX_BUFFER_SIZE) { buffer = new byte[MAX_BUFFER_SIZE]; } else { buffer = new byte[size - downloaded]; } // Read from server into buffer. int read = [Link](buffer); if (read == -1) break; // Write buffer to file. [Link](buffer, 0, read); downloaded += read; stateChanged(); } /* Change status to complete if this point was
reached because downloading has finished. */ if (status == DOWNLOADING) { status = COMPLETE; stateChanged(); } } catch (Exception e) { error(); } finally { // Close file. if (file != null) { try { [Link](); } catch (Exception e) {} } // Close connection to server. if (stream != null) { try { [Link](); } catch (Exception e) {} } } } // Notify observers that this download's status has changed. private void stateChanged() { setChanged(); notifyObservers(); } }
2. DownloadManager.
import [Link].*; import [Link].*; import [Link].*; import [Link].*; import [Link].*; import [Link].*;
// The Download Manager. public class DownloadManager extends JFrame
implements Observer { // Add download text field. private JTextField addTextField;
// Download table's data model. private DownloadsTableModel tableModel;
// Table showing downloads. private JTable table;
// These are the buttons for managing the selected download. private JButton pauseButton, resumeButton; private JButton cancelButton, clearButton;
// Currently selected download. private Download selectedDownload;
// Flag for whether or not table selection is being cleared. private boolean clearing;
// Constructor for Download Manager. public DownloadManager() { // Set application title. setTitle("Download Manager");
// Set window size.
setSize(640, 480);
// Handle window closing events. addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { actionExit(); } });
// Set up file menu. JMenuBar menuBar = new JMenuBar(); JMenu fileMenu = new JMenu("File"); [Link](KeyEvent.VK_F); JMenuItem fileExitMenuItem = new JMenuItem("Exit", KeyEvent.VK_X); [Link](new ActionListener() { public void actionPerformed(ActionEvent e) { actionExit(); } }); [Link](fileExitMenuItem); [Link](fileMenu); setJMenuBar(menuBar);
// Set up add panel. JPanel addPanel = new JPanel(); addTextField = new JTextField(30); [Link](addTextField);
JButton addButton = new JButton("Add Download"); [Link](new ActionListener() { public void actionPerformed(ActionEvent e) { actionAdd(); } }); [Link](addButton);
// Set up Downloads table. tableModel = new DownloadsTableModel(); table = new JTable(tableModel); [Link]().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { tableSelectionChanged(); } }); // Allow only one row at a time to be selected. [Link](ListSelectionModel.SINGLE_SELECTION);
// Set up ProgressBar as renderer for progress column. ProgressRenderer renderer = new ProgressRenderer(0, 100); [Link](true); // show progress text [Link]([Link], renderer);
// Set table's row height large enough to fit JProgressBar. [Link]( (int) [Link]().getHeight());
// Set up downloads panel. JPanel downloadsPanel = new JPanel(); [Link]( [Link]("Downloads")); [Link](new BorderLayout()); [Link](new JScrollPane(table), [Link]);
// Set up buttons panel. JPanel buttonsPanel = new JPanel(); pauseButton = new JButton("Pause"); [Link](new ActionListener() { public void actionPerformed(ActionEvent e) { actionPause(); } }); [Link](false); [Link](pauseButton); resumeButton = new JButton("Resume"); [Link](new ActionListener() { public void actionPerformed(ActionEvent e) { actionResume(); } }); [Link](false); [Link](resumeButton); cancelButton = new JButton("Cancel");
[Link](new ActionListener() { public void actionPerformed(ActionEvent e) { actionCancel(); } }); [Link](false); [Link](cancelButton); clearButton = new JButton("Clear"); [Link](new ActionListener() { public void actionPerformed(ActionEvent e) { actionClear(); } }); [Link](false); [Link](clearButton);
// Add panels to display. getContentPane().setLayout(new BorderLayout()); getContentPane().add(addPanel, [Link]); getContentPane().add(downloadsPanel, [Link]); getContentPane().add(buttonsPanel, [Link]); }
// Exit this program. private void actionExit() { [Link](0); }
// Add a new download. private void actionAdd() { URL verifiedUrl = verifyUrl([Link]()); if (verifiedUrl != null) { [Link](new Download(verifiedUrl)); [Link](""); // reset add text field } else { [Link](this, "Invalid Download URL", "Error", JOptionPane.ERROR_MESSAGE); } }
// Verify download URL. private URL verifyUrl(String url) { // Only allow HTTP URLs. if (![Link]().startsWith("[Link] return null;
// Verify format of URL. URL verifiedUrl = null; try { verifiedUrl = new URL(url); } catch (Exception e) { return null; }
// Make sure URL specifies a file.
if ([Link]().length() < 2) return null;
return verifiedUrl; }
// Called when table row selection changes. private void tableSelectionChanged() { /* Unregister from receiving notifications from the last selected download. */ if (selectedDownload != null) [Link]([Link]);
/* If not in the middle of clearing a download, set the selected download and register to receive notifications from it. */ if (!clearing && [Link]() > -1) { selectedDownload = [Link]([Link]()); [Link]([Link]); updateButtons(); } }
// Pause the selected download. private void actionPause() { [Link](); updateButtons();
// Resume the selected download. private void actionResume() { [Link](); updateButtons(); }
// Cancel the selected download. private void actionCancel() { [Link](); updateButtons(); }
// Clear the selected download. private void actionClear() { clearing = true; [Link]([Link]()); clearing = false; selectedDownload = null; updateButtons(); }
/* Update each button's state based off of the currently selected download's status. */ private void updateButtons() { if (selectedDownload != null) { int status = [Link]();
switch (status) { case [Link]: [Link](true); [Link](false); [Link](true); [Link](false); break; case [Link]: [Link](false); [Link](true); [Link](true); [Link](false); break; case [Link]: [Link](false); [Link](true); [Link](false); [Link](true); break; default: // COMPLETE or CANCELLED [Link](false); [Link](false); [Link](false); [Link](true); } } else { // No download is selected in table. [Link](false);
[Link](false); [Link](false); [Link](false); } }
/* Update is called when a Download notifies its observers of any changes. */ public void update(Observable o, Object arg) { // Update buttons if the selected download has changed. if (selectedDownload != null && [Link](o)) updateButtons(); }
// Run the Download Manager. public static void main(String[] args) { DownloadManager manager = new DownloadManager(); [Link](true); } }
3. DownloadTableModel.
import [Link].*; import [Link].*; import [Link].*;
// This class manages the download table's data.
class DownloadsTableModel extends AbstractTableModel implements Observer { // These are the names for the table's columns. private static final String[] columnNames = {"URL", "Size", "Progress", "Status"};
// These are the classes for each column's values. private static final Class[] columnClasses = {[Link], [Link], [Link], [Link]};
// The table's list of downloads. private ArrayList<Download> downloadList = new ArrayList<Download>();
// Add a new download to the table. public void addDownload(Download download) { // Register to be notified when the download changes. [Link](this);
[Link](download);
// Fire table row insertion notification to table. fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1); }
// Get a download for the specified row. public Download getDownload(int row) {
return (Download) [Link](row); }
// Remove a download from the list. public void clearDownload(int row) { [Link](row);
// Fire table row deletion notification to table. fireTableRowsDeleted(row, row); }
// Get table's column count. public int getColumnCount() { return [Link]; }
// Get a column's name. public String getColumnName(int col) { return columnNames[col]; }
// Get a column's class. public Class getColumnClass(int col) { return columnClasses[col]; }
// Get table's row count. public int getRowCount() {
return [Link](); }
// Get value for a specific row and column combination. public Object getValueAt(int row, int col) { Download download = [Link](row); switch (col) { case 0: // URL return [Link](); case 1: // Size int size = [Link](); return (size == -1) ? "" : [Link](size); case 2: // Progress return new Float([Link]()); case 3: // Status return [Link][[Link]()]; } return ""; }
/* Update is called when a Download notifies its observers of any changes */ public void update(Observable o, Object arg) { int index = [Link](o);
// Fire table row update notification to table. fireTableRowsUpdated(index, index); }}
4. ProgressRenderer. import [Link].*; import [Link].*; import [Link].*; // This class renders a JProgressBar in a table cell. class ProgressRenderer extends JProgressBar implements TableCellRenderer { // Constructor for ProgressRenderer. public ProgressRenderer(int min, int max) { super(min, max); } /* Returns this JProgressBar as the renderer for the given table cell. */ public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { // Set JProgressBar's percent complete value. setValue((int) ((Float) value).floatValue()); return this; }