Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
The Waikato Environment for Knowledge Analysis (WEKA), a machine
learning workbench. This version represents the developer version, the
"bleeding edge" of development, you could say. New functionality gets added
to this version.
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*
* PackageManager.java
* Copyright (C) 2009-2012 University of Waikato, Hamilton, New Zealand
*/
package weka.gui;
import weka.core.Environment;
import weka.core.Utils;
import weka.core.Version;
import weka.core.WekaPackageManager;
import weka.core.packageManagement.Dependency;
import weka.core.packageManagement.Package;
import weka.core.packageManagement.PackageConstraint;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumnModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import static weka.core.WekaPackageManager.DISABLED_KEY;
import static weka.core.WekaPackageManager.DISABLE_KEY;
/**
* A GUI interface the the package management system.
*
* @author Mark Hall (mhall{[at]}pentaho{[dot]}com)
* @version $Revision: 14546 $
*/
public class PackageManager extends JPanel {
/** For serialization */
private static final long serialVersionUID = -7463821313750352385L;
protected static final String PACKAGE_COLUMN = "Package";
protected static final String CATEGORY_COLUMN = "Category";
protected static final String INSTALLED_COLUMN = "Installed version";
protected static final String REPOSITORY_COLUMN = "Repository version";
protected static final String LOADED_COLUMN = "Loaded";
/** The JTable for displaying the package names and version numbers */
protected JTable m_table = new ETable();
protected JSplitPane m_splitP;
// protected JTextArea m_packageDescription;
/** An editor pane to display package information */
protected JEditorPane m_infoPane;
/** Installed radio button */
protected JRadioButton m_installedBut = new JRadioButton("Installed");
/** Available radio button */
protected JRadioButton m_availableBut = new JRadioButton("Available");
/** All radio button */
protected JRadioButton m_allBut = new JRadioButton("All");
/** Button for installing the selected package */
protected JButton m_installBut = new JButton("Install");
protected JCheckBox m_forceBut = new JCheckBox(
"Ignore dependencies/conflicts");
/** Button for uninstalling the selected package */
protected JButton m_uninstallBut = new JButton("Uninstall");
/** Button for refreshing the package meta data cache */
protected JButton m_refreshCacheBut = new JButton("Refresh repository cache");
/** Button for toggling the load status of an installed package */
protected JButton m_toggleLoad = new JButton("Toggle load");
protected JProgressBar m_progress = new JProgressBar(0, 100);
protected JLabel m_detailLabel = new JLabel();
protected JButton m_backB;
protected LinkedList m_browserHistory = new LinkedList();
protected static final String BROWSER_HOME =
"http://www.cs.waikato.ac.nz/ml/weka/index_home_pm.html";
protected JButton m_homeB;
protected JToolBar m_browserTools;
protected JLabel m_newPackagesAvailableL;
protected DefaultTableModel m_model;
protected Map> m_packageLookupInfo;
protected List m_allPackages;
protected List m_installedPackages;
protected List m_availablePackages;
protected Map m_packageDescriptions =
new HashMap();
protected List m_searchResults = new ArrayList();
protected JTextField m_searchField = new JTextField(15);
protected JLabel m_searchHitsLab = new JLabel("");
/** The column in the table to sort the entries by */
protected int m_sortColumn = 0;
/** Reverse the sort order if the user clicks the same column header twice */
protected boolean m_reverseSort = false;
/** Button to pop up the file environment field widget */
protected JButton m_unofficialBut = new JButton("File/URL");
/** Widget for specifying a URL or path to an unofficial package to install */
protected FileEnvironmentField m_unofficialChooser =
new FileEnvironmentField("File/URL", Environment.getSystemWide());
protected JFrame m_unofficialFrame = null;
public static boolean s_atLeastOnePackageUpgradeHasOccurredInThisSession =
false;
protected Comparator m_packageComparator =
new Comparator() {
@Override
public int compare(Package o1, Package o2) {
String meta1 = "";
String meta2 = "";
if (m_sortColumn == 0) {
meta1 = o1.getName();
meta2 = o2.getName();
} else {
if (o1.getPackageMetaDataElement("Category") != null) {
meta1 = o1.getPackageMetaDataElement("Category").toString();
}
if (o2.getPackageMetaDataElement("Category") != null) {
meta2 = o2.getPackageMetaDataElement("Category").toString();
}
}
int result = meta1.compareTo(meta2);
if (m_reverseSort) {
result = -result;
}
return result;
}
};
protected boolean m_installing = false;
class ProgressPrintStream extends PrintStream {
private final Progressable m_listener;
public ProgressPrintStream(Progressable listener) {
// have to invoke a super class constructor
super(System.out);
m_listener = listener;
}
@Override
public void println(String string) {
boolean messageOnly = false;
if (string.startsWith("%%")) {
string = string.substring(2);
messageOnly = true;
}
if (!messageOnly) {
System.out.println(string); // make sure the log picks it up
m_listener.makeProgress(string);
} else {
m_listener.makeProgressMessageOnly(string);
}
}
@Override
public void println(Object obj) {
println(obj.toString());
}
@Override
public void print(String string) {
boolean messageOnly = false;
if (string.startsWith("%%")) {
string = string.substring(2);
messageOnly = true;
}
if (!messageOnly) {
System.out.print(string); // make sure the log picks it up
m_listener.makeProgress(string);
} else {
m_listener.makeProgressMessageOnly(string);
}
}
@Override
public void print(Object obj) {
print(obj.toString());
}
}
interface Progressable {
void makeProgress(String progressMessage);
void makeProgressMessageOnly(String progressMessage);
}
class EstablishCache extends SwingWorker implements Progressable {
private int m_progressCount = 0;
private Exception m_error = null;
private javax.swing.ProgressMonitor m_progress;
@Override
public void makeProgress(String progressMessage) {
m_progress.setNote(progressMessage);
m_progressCount++;
m_progress.setProgress(m_progressCount);
}
@Override
public void makeProgressMessageOnly(String progressMessage) {
m_progress.setNote(progressMessage);
}
@Override
public Void doInBackground() {
int numPackages = WekaPackageManager.numRepositoryPackages();
if (numPackages < 0) {
// there was some problem getting the file that holds this
// information from the repository server - try to continue
// anyway with a max value of 100 for the number of packages
// (since all we use this for is setting the upper bound on
// the progress bar).
numPackages = 100;
}
m_progress =
new javax.swing.ProgressMonitor(PackageManager.this,
"Establising cache...", "", 0, numPackages);
ProgressPrintStream pps = new ProgressPrintStream(this);
m_error = WekaPackageManager.establishCacheIfNeeded(pps);
m_cacheEstablished = true;
return null;
}
@Override
public void done() {
m_progress.close();
if (m_error != null) {
displayErrorDialog("There was a problem establishing the package\n"
+ "meta data cache. We'll try to use the repository" + "directly.",
m_error);
}
}
}
class CheckForNewPackages extends SwingWorker {
@Override
public Void doInBackground() {
Map localPackageNameList =
WekaPackageManager.getPackageList(true);
if (localPackageNameList == null) {
// quietly return and see if we can continue anyway
return null;
}
Map repositoryPackageNameList =
WekaPackageManager.getPackageList(false);
if (repositoryPackageNameList == null) {
// quietly return and see if we can continue anyway
return null;
}
if (repositoryPackageNameList.keySet().size() < localPackageNameList
.keySet().size()) {
// package(s) have disappeared from the repository.
// Force a cache refresh...
RefreshCache r = new RefreshCache(true);
r.execute();
return null;
}
StringBuffer newPackagesBuff = new StringBuffer();
StringBuffer updatedPackagesBuff = new StringBuffer();
for (String s : repositoryPackageNameList.keySet()) {
if (!localPackageNameList.containsKey(s)) {
newPackagesBuff.append(s + " ");
}
}
for (String localPackage : localPackageNameList.keySet()) {
String localVersion = localPackageNameList.get(localPackage);
String repoVersion = repositoryPackageNameList.get(localPackage);
if (repoVersion == null) {
continue;
}
// a difference here indicates a newer version on the server
if (!localVersion.equals(repoVersion)) {
updatedPackagesBuff.append(localPackage + " (" + repoVersion
+ ") ");
}
}
if (newPackagesBuff.length() > 0 || updatedPackagesBuff.length() > 0) {
String information =
"New and/or updated packages: ";
if (newPackagesBuff.length() > 0) {
information += "
New: " + newPackagesBuff.toString();
}
if (updatedPackagesBuff.length() > 0) {
information +=
"
Updated: " + updatedPackagesBuff.toString()
+ "
";
}
information += "";
m_newPackagesAvailableL.setToolTipText(information);
m_browserTools.add(m_newPackagesAvailableL);
// force a cache refresh (to match command line package manager client
// behaviour)
RefreshCache r = new RefreshCache(false);
r.execute();
m_browserTools.revalidate();
}
return null;
}
}
class RefreshCache extends SwingWorker implements Progressable {
private int m_progressCount = 0;
private Exception m_error = null;
private boolean m_removeUpdateIcon;
public RefreshCache(boolean removeUpdateIcon) {
m_removeUpdateIcon = removeUpdateIcon;
}
@Override
public void makeProgress(String progressMessage) {
m_detailLabel.setText(progressMessage);
if (progressMessage.startsWith("[Default")) {
// We're using the new refresh mechanism - extract the number
// of KB read from the message
String kbs =
progressMessage.replace("[DefaultPackageManager] downloaded ", "");
kbs = kbs.replace(" KB\r", "");
m_progressCount = Integer.parseInt(kbs);
} else {
m_progressCount++;
}
m_progress.setValue(m_progressCount);
}
@Override
public void makeProgressMessageOnly(String progressMessage) {
m_detailLabel.setText(progressMessage);
}
@Override
public Void doInBackground() {
m_cacheRefreshInProgress = true;
int progressUpper = WekaPackageManager.repoZipArchiveSize();
if (progressUpper == -1) {
// revert to legacy approach
progressUpper = WekaPackageManager.numRepositoryPackages();
}
if (progressUpper < 0) {
// there was some problem getting the file that holds this
// information from the repository server - try to continue
// anyway with a max value of 100 for the number of packages
// (since all we use this for is setting the upper bound on
// the progress bar).
progressUpper = 100;
}
// number of KBs for the archive is approx 6 x # packages
m_progress.setMaximum(progressUpper);
m_refreshCacheBut.setEnabled(false);
m_installBut.setEnabled(false);
m_unofficialBut.setEnabled(false);
m_installedBut.setEnabled(false);
m_availableBut.setEnabled(false);
m_allBut.setEnabled(false);
ProgressPrintStream pps = new ProgressPrintStream(this);
m_error = WekaPackageManager.refreshCache(pps);
getAllPackages();
return null;
}
@Override
public void done() {
m_progress.setValue(m_progress.getMinimum());
if (m_error != null) {
displayErrorDialog("There was a problem refreshing the package\n"
+ "meta data cache. We'll try to use the repository" + "directly.",
m_error);
m_detailLabel.setText("");
} else {
m_detailLabel.setText("Cache refresh completed");
}
m_installBut.setEnabled(true && !WekaPackageManager.m_offline);
m_unofficialBut.setEnabled(true);
m_refreshCacheBut.setEnabled(true && !WekaPackageManager.m_offline);
m_installedBut.setEnabled(true);
m_availableBut.setEnabled(true);
m_allBut.setEnabled(true);
m_availablePackages = null;
updateTable();
try {
if (m_removeUpdateIcon) {
m_browserTools.remove(m_newPackagesAvailableL);
m_browserTools.revalidate();
}
} catch (Exception ex) {
}
m_cacheRefreshInProgress = false;
}
}
private void pleaseCloseAppWindowsPopUp() {
if (!Utils
.getDontShowDialog("weka.gui.PackageManager.PleaseCloseApplicationWindows")) {
JCheckBox dontShow = new JCheckBox("Do not show this message again");
Object[] stuff = new Object[2];
stuff[0] =
"Please close any open Weka application windows\n"
+ "(Explorer, Experimenter, KnowledgeFlow, SimpleCLI)\n"
+ "before proceeding.\n";
stuff[1] = dontShow;
JOptionPane.showMessageDialog(PackageManager.this, stuff,
"Weka Package Manager", JOptionPane.OK_OPTION);
if (dontShow.isSelected()) {
try {
Utils
.setDontShowDialog("weka.gui.PackageManager.PleaseCloseApplicationWindows");
} catch (Exception ex) {
// quietly ignore
}
}
}
}
private void toggleLoadStatusRequiresRestartPopUp() {
if (!Utils
.getDontShowDialog("weka.gui.PackageManager.ToggleLoadStatusRequiresRestart")) {
JCheckBox dontShow = new JCheckBox("Do not show this message again");
Object[] stuff = new Object[2];
stuff[0] =
"Changing a package's load status will require a restart for the change to take affect\n";
stuff[1] = dontShow;
JOptionPane.showMessageDialog(PackageManager.this, stuff,
"Weka Package Manager", JOptionPane.OK_OPTION);
if (dontShow.isSelected()) {
try {
Utils
.setDontShowDialog("weka.gui.PackageManager.ToggleLoadStatusRequiresRestart");
} catch (Exception ex) {
// quietly ignore
}
}
}
}
class UninstallTask extends SwingWorker implements Progressable {
private List m_packageNamesToUninstall;
// private String m_packageName;
// private boolean m_successfulUninstall = false;
private final List m_unsuccessfulUninstalls =
new ArrayList();
private int m_progressCount = 0;
public void setPackages(List packageNames) {
m_packageNamesToUninstall = packageNames;
}
@Override
public void makeProgress(String progressMessage) {
m_detailLabel.setText(progressMessage);
m_progressCount++;
m_progress.setValue(m_progressCount);
if (m_progressCount == m_progress.getMaximum()) {
m_progress.setMaximum(m_progressCount + 5);
}
}
@Override
public void makeProgressMessageOnly(String progressMessage) {
m_detailLabel.setText(progressMessage);
}
@Override
public Void doInBackground() {
m_installing = true;
m_installBut.setEnabled(false);
m_unofficialBut.setEnabled(false);
m_uninstallBut.setEnabled(false);
m_refreshCacheBut.setEnabled(false);
m_toggleLoad.setEnabled(false);
m_availableBut.setEnabled(false);
m_allBut.setEnabled(false);
m_installedBut.setEnabled(false);
ProgressPrintStream pps = new ProgressPrintStream(this);
m_progress.setMaximum(m_packageNamesToUninstall.size() * 5);
for (int zz = 0; zz < m_packageNamesToUninstall.size(); zz++) {
String packageName = m_packageNamesToUninstall.get(zz);
boolean explorerPropertiesExist =
WekaPackageManager.installedPackageResourceExists(packageName,
"Explorer.props");
if (!m_forceBut.isSelected()) {
List compromised = new ArrayList();
// Now check to see which other installed packages depend on this one
List installedPackages;
try {
installedPackages = WekaPackageManager.getInstalledPackages();
} catch (Exception e) {
e.printStackTrace();
displayErrorDialog("Can't determine which packages are installed!",
e);
// return null; // can't proceed
m_unsuccessfulUninstalls.add(packageName);
continue;
}
for (Package p : installedPackages) {
List tempDeps;
try {
tempDeps = p.getDependencies();
} catch (Exception e) {
e.printStackTrace();
displayErrorDialog(
"Problem determining dependencies for package : " + p.getName(),
e);
// return null; // can't proceed
m_unsuccessfulUninstalls.add(packageName);
continue;
}
for (Dependency d : tempDeps) {
if (d.getTarget().getPackage().getName().equals(packageName)) {
// add this installed package to the list
compromised.add(p);
break;
}
}
}
if (compromised.size() > 0) {
StringBuffer message = new StringBuffer();
message.append("The following installed packages depend on "
+ packageName + " :\n\n");
for (Package p : compromised) {
message.append("\t" + p.getName() + "\n");
}
message.append("\nDo you wish to proceed?");
int result =
JOptionPane.showConfirmDialog(PackageManager.this,
message.toString(), "Weka Package Manager",
JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.NO_OPTION) {
// bail out here
// return null;
continue;
}
}
}
// m_progress.setMaximum(10);
try {
if (explorerPropertiesExist) {
// need to remove any set Explorer properties first
WekaPackageManager.removeExplorerProps(packageName);
}
WekaPackageManager.uninstallPackage(packageName, true, pps);
} catch (Exception e) {
e.printStackTrace();
displayErrorDialog("Unable to uninstall package: " + packageName, e);
// return null;
m_unsuccessfulUninstalls.add(packageName);
continue;
}
}
WekaPackageManager.refreshGOEProperties();
// m_successfulUninstall = true;
return null;
}
@Override
public void done() {
m_progress.setValue(m_progress.getMinimum());
if (m_unsuccessfulUninstalls.size() == 0) {
m_detailLabel.setText("Packages removed successfully.");
if (!Utils
.getDontShowDialog("weka.gui.PackageManager.RestartAfterUninstall")) {
JCheckBox dontShow = new JCheckBox("Do not show this message again");
Object[] stuff = new Object[2];
stuff[0] =
"Weka might need to be restarted for\n"
+ "the changes to come into effect.\n";
stuff[1] = dontShow;
JOptionPane.showMessageDialog(PackageManager.this, stuff,
"Weka Package Manager", JOptionPane.OK_OPTION);
if (dontShow.isSelected()) {
try {
Utils
.setDontShowDialog("weka.gui.PackageManager.RestartAfterUninstall");
} catch (Exception ex) {
// quietly ignore
}
}
}
} else {
StringBuffer failedPackageNames = new StringBuffer();
for (String p : m_unsuccessfulUninstalls) {
failedPackageNames.append(p + "\n");
}
displayErrorDialog(
"The following package(s) could not be uninstalled\n"
+ "for some reason (check the log)\n"
+ failedPackageNames.toString(), "");
m_detailLabel.setText("Finished uninstalling.");
}
m_unofficialBut.setEnabled(true);
m_refreshCacheBut.setEnabled(true);
m_availableBut.setEnabled(true);
m_allBut.setEnabled(true);
m_installedBut.setEnabled(true);
// force refresh of installed and available packages
m_installedPackages = null;
m_availablePackages = null;
// m_installBut.setEnabled(true);
m_installing = false;
updateTable();
if (m_table.getSelectedRow() >= 0) {
// mainly to update the install/uninstall button status
// displayPackageInfo(m_table.getSelectedRow());
updateInstallUninstallButtonEnablement();
}
}
}
class UnofficialInstallTask extends SwingWorker implements
Progressable {
private String m_target;
private int m_progressCount = 0;
private boolean m_errorOccurred = false;
public void setTargetToInstall(String target) {
m_target = target;
}
@Override
public void makeProgress(String progressMessage) {
m_detailLabel.setText(progressMessage);
m_progressCount++;
m_progress.setValue(m_progressCount);
if (m_progressCount == m_progress.getMaximum()) {
m_progress.setMaximum(m_progressCount + 5);
}
}
@Override
public void makeProgressMessageOnly(String progressMessage) {
m_detailLabel.setText(progressMessage);
}
@Override
public Void doInBackground() {
m_installing = true;
m_installBut.setEnabled(false);
m_uninstallBut.setEnabled(false);
m_refreshCacheBut.setEnabled(false);
m_toggleLoad.setEnabled(false);
m_unofficialBut.setEnabled(false);
m_availableBut.setEnabled(false);
m_allBut.setEnabled(false);
m_installedBut.setEnabled(false);
ProgressPrintStream pps = new ProgressPrintStream(this);
m_progress.setMaximum(30);
Package installedPackage = null;
String toInstall = m_target;
try {
toInstall = Environment.getSystemWide().substitute(m_target);
} catch (Exception ex) {
}
try {
if (toInstall.toLowerCase().startsWith("http://")
|| toInstall.toLowerCase().startsWith("https://")) {
String packageName =
WekaPackageManager.installPackageFromURL(new URL(toInstall), pps);
installedPackage =
WekaPackageManager.getInstalledPackageInfo(packageName);
} else if (toInstall.toLowerCase().endsWith(".zip")) {
String packageName =
WekaPackageManager.installPackageFromArchive(toInstall, pps);
installedPackage =
WekaPackageManager.getInstalledPackageInfo(packageName);
} else {
displayErrorDialog("Unable to install package " + "\nfrom "
+ toInstall + ". Unrecognized as a URL or zip archive.",
(String) null);
m_errorOccurred = true;
pps.close();
return null;
}
} catch (Exception ex) {
displayErrorDialog("Unable to install package " + "\nfrom " + m_target
+ ". Check the log for error messages.", ex);
m_errorOccurred = true;
return null;
}
if (installedPackage != null) {
if (!Utils
.getDontShowDialog("weka.gui.PackageManager.RestartAfterUpgrade")) {
JCheckBox dontShow = new JCheckBox("Do not show this message again");
Object[] stuff = new Object[2];
stuff[0] =
"Weka will need to be restared after installation for\n"
+ "the changes to come into effect.\n";
stuff[1] = dontShow;
JOptionPane.showMessageDialog(PackageManager.this, stuff,
"Weka Package Manager", JOptionPane.OK_OPTION);
if (dontShow.isSelected()) {
try {
Utils
.setDontShowDialog("weka.gui.PackageManager.RestartAfterUpgrade");
} catch (Exception ex) {
// quietly ignore
}
}
}
try {
File packageRoot =
new File(WekaPackageManager.getPackageHome() + File.separator
+ installedPackage.getName());
boolean loadCheck =
// WekaPackageManager.loadCheck(installedPackage, packageRoot, pps);
WekaPackageManager.hasBeenLoaded(installedPackage);
if (!loadCheck) {
displayErrorDialog("Package was installed correctly but could not "
+ "be loaded. Check log for details", (String) null);
}
} catch (Exception ex) {
displayErrorDialog("Unable to install package " + "\nfrom "
+ m_target + ".", ex);
m_errorOccurred = true;
}
// since we can't determine whether an unofficial package is installed
// already before performing the install/upgrade (due to the fact that
// the package name isn't known until the archive is unpacked) we will
// not refresh the GOE properties and make the user restart Weka in
// order
// to be safe and avoid any conflicts between old and new versions of
// classes
// for this package
// WekaPackageManager.refreshGOEProperties();
}
return null;
}
@Override
public void done() {
m_progress.setValue(m_progress.getMinimum());
if (m_errorOccurred) {
m_detailLabel.setText("Problem installing - check log.");
} else {
m_detailLabel.setText("Package installed successfully.");
}
m_unofficialBut.setEnabled(true);
m_refreshCacheBut.setEnabled(true && !WekaPackageManager.m_offline);
m_availableBut.setEnabled(true);
m_allBut.setEnabled(true);
m_installedBut.setEnabled(true);
// force refresh of installed and available packages
m_installedPackages = null;
m_availablePackages = null;
// m_installBut.setEnabled(true);
m_installing = false;
updateTable();
if (m_table.getSelectedRow() >= 0) {
// mainly to update the install/uninstall button status
// displayPackageInfo(m_table.getSelectedRow());
updateInstallUninstallButtonEnablement();
}
}
}
class InstallTask extends SwingWorker implements Progressable {
private List m_packageNamesToInstall;
private List