/*
* RapidMiner
*
* Copyright (C) 2001-2011 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.gui;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.logging.Level;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import com.rapid_i.deployment.update.client.UpdateManager;
import com.rapidminer.FileProcessLocation;
import com.rapidminer.NoOpUserError;
import com.rapidminer.Process;
import com.rapidminer.ProcessLocation;
import com.rapidminer.RapidMiner;
import com.rapidminer.RepositoryProcessLocation;
import com.rapidminer.gui.actions.OpenAction;
import com.rapidminer.gui.dialog.ResultHistory;
import com.rapidminer.gui.docking.RapidDockableContainerFactory;
import com.rapidminer.gui.look.RapidLookAndFeel;
import com.rapidminer.gui.look.fc.BookmarkIO;
import com.rapidminer.gui.look.ui.RapidDockingUISettings;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.gui.tools.dialogs.DecisionRememberingConfirmDialog;
import com.rapidminer.gui.viewer.MetaDataViewerTableModel;
import com.rapidminer.operator.io.DatabaseDataReader;
import com.rapidminer.parameter.ParameterTypeBoolean;
import com.rapidminer.parameter.ParameterTypeCategory;
import com.rapidminer.parameter.ParameterTypeInt;
import com.rapidminer.parameter.ParameterTypeString;
import com.rapidminer.repository.MalformedRepositoryLocationException;
import com.rapidminer.repository.RepositoryLocation;
import com.rapidminer.repository.RepositoryManager;
import com.rapidminer.tools.FileSystemService;
import com.rapidminer.tools.LaunchListener;
import com.rapidminer.tools.LaunchListener.RemoteControlHandler;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.ParameterService;
import com.rapidminer.tools.Tools;
import com.rapidminer.tools.jdbc.connection.DatabaseConnectionService;
import com.rapidminer.tools.plugin.Plugin;
import com.rapidminer.tools.usagestats.UsageStatistics;
import com.rapidminer.tools.usagestats.UsageStatsTransmissionDialog;
import com.vlsolutions.swing.docking.DockableContainerFactory;
import com.vlsolutions.swing.docking.ui.DockingUISettings;
/**
* The main class if RapidMiner is started in GUI mode. This class keeps a reference to the
* {@link MainFrame} and some other GUI relevant information and methods.
*
* @author Ingo Mierswa, Simon Fischer
*/
public class RapidMinerGUI extends RapidMiner {
public static final String PROPERTY_GEOMETRY_X = "rapidminer.gui.geometry.x";
public static final String PROPERTY_GEOMETRY_Y = "rapidminer.gui.geometry.y";
public static final String PROPERTY_GEOMETRY_EXTENDED_STATE = "rapidminer.gui.geometry.extendedstate";
public static final String PROPERTY_GEOMETRY_WIDTH = "rapidminer.gui.geometry.width";
public static final String PROPERTY_GEOMETRY_HEIGHT = "rapidminer.gui.geometry.height";
public static final String PROPERTY_GEOMETRY_DIVIDER_MAIN = "rapidminer.gui.geometry.divider.main";
public static final String PROPERTY_GEOMETRY_DIVIDER_EDITOR = "rapidminer.gui.geometry.divider.editor";;
public static final String PROPERTY_GEOMETRY_DIVIDER_LOGGING = "rapidminer.gui.geometry.divider.logging";
public static final String PROPERTY_GEOMETRY_DIVIDER_GROUPSELECTION = "rapidminer.gui.geometry.divider.groupselection";
public static final String PROPERTY_EXPERT_MODE = "rapidminer.gui.expertmode";
public static final String PROPERTY_DISCONNECT_ON_DISABLE = "rapidminer.gui.disconnect_on_disable";
// --- Properties ---
public static final String PROPERTY_RAPIDMINER_GUI_UPDATE_CHECK = "rapidminer.update.check";
public static final String PROPERTY_RAPIDMINER_GUI_MAX_STATISTICS_ROWS = "rapidminer.gui.max_statistics_rows";
public static final String PROPERTY_RAPIDMINER_GUI_MAX_SORTABLE_ROWS = "rapidminer.gui.max_sortable_rows";
public static final String PROPERTY_RAPIDMINER_GUI_MAX_DISPLAYED_VALUES = "rapidminer.gui.max_displayed_values";
public static final String PROPERTY_RAPIDMINER_GUI_SNAP_TO_GRID = "rapidminer.gui.snap_to_grid";
public static final String PROPERTY_AUTOWIRE_INPUT = "rapidminer.gui.autowire_input";
public static final String PROPERTY_AUTOWIRE_OUTPUT = "rapidminer.gui.autowire_output";
public static final String PROPERTY_RESOLVE_RELATIVE_REPOSITORY_LOCATIONS = "rapidminer.gui.resolve_relative_repository_locations";
public static final String PROPERTY_CLOSE_RESULTS_BEFORE_RUN = "rapidminer.gui.close_results_before_run";
public static final String PROPERTY_TRANSFER_USAGESTATS = "rapidminer.gui.transfer_usagestats";
public static final String[] PROPERTY_TRANSFER_USAGESTATS_ANSWERS = { "ask", "always", "never" };
public static final String PROPERTY_ADD_BREAKPOINT_RESULTS_TO_HISTORY = "rapidminer.gui.add_breakpoint_results_to_history";
public static final String PROPERTY_CONFIRM_EXIT = "rapidminer.gui.confirm_exit";
public static final String PROPERTY_FETCH_DATA_BASE_TABLES_NAMES = "rapidminer.gui.fetch_data_base_table_names";
public static final String PROPERTY_EVALUATE_MD_FOR_SQL_QUERIES = DatabaseDataReader.PROPERTY_EVALUATE_MD_FOR_SQL_QUERIES;
static {
ParameterService.registerParameter(new ParameterTypeBoolean(PROPERTY_RAPIDMINER_GUI_UPDATE_CHECK, "Check for new RapidMiner versions at start up time?", true));
ParameterService.registerParameter(new ParameterTypeInt(PROPERTY_RAPIDMINER_GUI_MAX_STATISTICS_ROWS, "Indicates the maximum number of rows for the automatic calculation of statistics and other time intensive data viewing actions.", 1, Integer.MAX_VALUE, 100000));
ParameterService.registerParameter(new ParameterTypeInt(PROPERTY_RAPIDMINER_GUI_MAX_SORTABLE_ROWS, "Indicates the maximum number of rows for sortable tables.", 1, Integer.MAX_VALUE, 100000));
ParameterService.registerParameter(new ParameterTypeInt(PROPERTY_RAPIDMINER_GUI_MAX_DISPLAYED_VALUES, "Indicates the maximum number of different values which will for example be displayed in the meta data view.", 1, Integer.MAX_VALUE, MetaDataViewerTableModel.DEFAULT_MAX_DISPLAYED_VALUES));
ParameterService.registerParameter(new ParameterTypeBoolean(PROPERTY_RESOLVE_RELATIVE_REPOSITORY_LOCATIONS, "If checked, the repository browser dialog will resolve repository locations relative to the current process by default. Can be disabled within the dialog.", true));
ParameterService.registerParameter(new ParameterTypeBoolean(PROPERTY_RAPIDMINER_GUI_SNAP_TO_GRID, "If checked, operators snap to the grid.", true));
ParameterService.registerParameter(new ParameterTypeBoolean(PROPERTY_AUTOWIRE_INPUT, "If checked, operator's inputs are wired automatically when added. Can be checked also in the \"Operators\" tree.", true));
ParameterService.registerParameter(new ParameterTypeBoolean(PROPERTY_AUTOWIRE_OUTPUT, "If checked, operator's outputs are wired automatically when added. Can be checked also in the \"Operators\" tree.", true));
ParameterService.registerParameter(new ParameterTypeCategory(PROPERTY_CLOSE_RESULTS_BEFORE_RUN, "Close active result tabs when new process starts?", DecisionRememberingConfirmDialog.PROPERTY_VALUES, DecisionRememberingConfirmDialog.ASK));
ParameterService.registerParameter(new ParameterTypeBoolean(PROPERTY_FETCH_DATA_BASE_TABLES_NAMES, "Fetch the data base tables names in the SQL query dialog of all SQL operators.", true));
ParameterService.registerParameter(new ParameterTypeBoolean(PROPERTY_EVALUATE_MD_FOR_SQL_QUERIES, "If selected, the SQL meta data will be fetched during meta data evaluation in RapidMiner. For some databases, this may be slow.", true));
ParameterService.registerParameter(new ParameterTypeCategory(RapidMinerGUI.PROPERTY_TRANSFER_USAGESTATS, "Allow RapidMiner to transfer RapidMiner operator usage statistics?", RapidMinerGUI.PROPERTY_TRANSFER_USAGESTATS_ANSWERS, UsageStatsTransmissionDialog.ASK));
ParameterService.registerParameter(new ParameterTypeBoolean(RapidMinerGUI.PROPERTY_ADD_BREAKPOINT_RESULTS_TO_HISTORY, "Should results produced at breakpoints be added to the result history?", false));
ParameterService.registerParameter(new ParameterTypeBoolean(RapidMinerGUI.PROPERTY_DISCONNECT_ON_DISABLE, "Should operators be disconnected upon disabling them?", true));
ParameterService.registerParameter(new ParameterTypeBoolean(PROPERTY_CONFIRM_EXIT, "Should RapidMiner ask if you are sure each time you exit?", true));
// UPDATE
ParameterService.registerParameter(new ParameterTypeBoolean(com.rapid_i.deployment.update.client.UpdateManager.PARAMETER_UPDATE_INCREMENTALLY, "Download (small) patches rather than complete installation archives?", true));
ParameterService.registerParameter(new ParameterTypeString(com.rapid_i.deployment.update.client.UpdateManager.PARAMETER_UPDATE_URL, "URL of the RapidMiner update server.", com.rapid_i.deployment.update.client.UpdateManager.UPDATESERVICE_URL));
ParameterService.registerParameter(new ParameterTypeBoolean(com.rapid_i.deployment.update.client.UpdateManager.PARAMETER_INSTALL_TO_HOME, "If checked, all upgrades will be installed to the users home directory. Otherwise, administrator privileges are required.", true));
}
private static final int NUMBER_OF_RECENT_FILES = 8;
private static MainFrame mainFrame;
private static LinkedList<ProcessLocation> recentFiles = new LinkedList<ProcessLocation>();
private static ResultHistory resultHistory = new ResultHistory();
/**
* This thread listens for System shutdown and cleans up after shutdown.
* This included saving the recent file list and other GUI properties.
*/
private static class ShutdownHook extends Thread {
@Override
public void run() {
LogService.getRoot().info("Running shutdown sequence.");
RapidMinerGUI.saveRecentFileList();
RapidMinerGUI.saveGUIProperties();
UsageStatistics.getInstance().save();
RepositoryManager.shutdown();
}
}
//private static UpdateManager updateManager = new CommunityUpdateManager();
public void run(final String openLocation) throws Exception {
// check if resources were copied
URL logoURL = Tools.getResource("rapidminer_logo.png");
if (logoURL == null) {
LogService.getRoot().severe("Cannot find resources. Probably the ant target 'copy-resources' must be performed!");
RapidMiner.quit(RapidMiner.ExitMode.ERROR);
}
// Initialize Docking UI -- important must be done as early as possible!
DockingUISettings.setInstance(new RapidDockingUISettings());
DockableContainerFactory.setFactory(new RapidDockableContainerFactory());
RapidMiner.showSplash();
// set locale fix to US
Locale.setDefault(Locale.US);
JComponent.setDefaultLocale(Locale.US);
RapidMiner.splashMessage("basic");
RapidMiner.init();
RapidMiner.splashMessage("workspace");
RapidMiner.splashMessage("plaf");
setupToolTipManager();
setupGUI();
RapidMiner.splashMessage("history");
loadRecentFileList();
RapidMiner.splashMessage("icons");
SwingTools.loadIcons();
RepositoryManager.getInstance(null).createRepositoryIfNoneIsDefined();
RapidMiner.splashMessage("create_frame");
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
setMainFrame(new MainFrame(openLocation != null ? "design" : "welcome"));
}
});
RapidMiner.splashMessage("gui_properties");
loadGUIProperties(mainFrame);
RapidMiner.splashMessage("plugin_gui");
Plugin.initPluginGuis(mainFrame);
RapidMiner.splashMessage("show_frame");
mainFrame.setVisible(true);
UsageStatsTransmissionDialog.init();
RapidMiner.splashMessage("checks");
Plugin.initFinalChecks();
RapidMiner.splashMessage("ready");
RapidMiner.hideSplash();
// file from command line or Welcome Dialog
// if (file != null) {
// ImportProcessAction.open(file);
// }
if (openLocation != null) {
OpenAction.open(openLocation, false);
}
// check for updates
Plugin.initPluginUpdateManager();
UpdateManager.checkForUpdates();
}
private void setupToolTipManager() {
// setup tool tip text manager
ToolTipManager manager = ToolTipManager.sharedInstance();
manager.setDismissDelay(25000); // original: 4000
manager.setInitialDelay(1500); // original: 750
manager.setReshowDelay(50); // original: 500
}
/** This default implementation only setup the tool tip durations. Subclasses might
* override this method. */
protected void setupGUI() throws NoOpUserError {
System.setProperty(BookmarkIO.PROPERTY_BOOKMARKS_DIR, FileSystemService.getUserRapidMinerDir().getAbsolutePath());
System.setProperty(BookmarkIO.PROPERTY_BOOKMARKS_FILE, ".bookmarks");
System.setProperty(DatabaseConnectionService.PROPERTY_CONNECTIONS_FILE, "connections");
try {
UIManager.setLookAndFeel(new RapidLookAndFeel());
//OperatorService.reloadIcons();
} catch (Exception e) {
LogService.getRoot().log(Level.WARNING, "Cannot setup modern look and feel, using default.", e);
}
}
public static void setMainFrame(MainFrame mf) {
mainFrame = mf;
}
public static MainFrame getMainFrame() {
return mainFrame;
}
public static void useProcessFile(Process process) {
ProcessLocation location = process.getProcessLocation();
if (location != null) {
while (recentFiles.contains(location)) {
recentFiles.remove(location);
}
recentFiles.addFirst(location);
while (recentFiles.size() > NUMBER_OF_RECENT_FILES)
recentFiles.removeLast();
}
saveRecentFileList();
}
public static ResultHistory getResultHistory() {
return resultHistory;
}
public static List<ProcessLocation> getRecentFiles() {
return recentFiles;
}
private static void loadRecentFileList() {
File file = FileSystemService.getUserConfigFile("history");
if (!file.exists())
return;
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(file));
recentFiles.clear();
String line = null;
while ((line = in.readLine()) != null) {
if (line.startsWith("file ")) {
recentFiles.add(new FileProcessLocation(new File(line.substring(5))));
} else if (line.startsWith("repository ")) {
try {
recentFiles.add(new RepositoryProcessLocation(new RepositoryLocation(line.substring(11))));
} catch (MalformedRepositoryLocationException e) {
LogService.getRoot().log(Level.WARNING, "Unparseable line in history file: "+line);
}
} else {
LogService.getRoot().log(Level.WARNING, "Unparseable line in history file: "+line);
}
}
} catch (IOException e) {
LogService.getRoot().log(Level.WARNING, "Cannot read history file", e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
LogService.getRoot().log(Level.WARNING, "Cannot read history file", e);
}
}
}
}
private static void saveRecentFileList() {
File file = FileSystemService.getUserConfigFile("history");
PrintWriter out = null;
try {
out = new PrintWriter(new FileWriter(file));
for (ProcessLocation loc : recentFiles) {
out.println(loc.toHistoryFileString());
}
} catch (IOException e) {
SwingTools.showSimpleErrorMessage("cannot_write_history_file", e);
} finally {
if (out != null) {
out.close();
}
}
}
private static void saveGUIProperties() {
Properties properties = new Properties();
MainFrame mainFrame = getMainFrame();
if (mainFrame != null) {
properties.setProperty(PROPERTY_GEOMETRY_X, "" + (int) mainFrame.getLocation().getX());
properties.setProperty(PROPERTY_GEOMETRY_Y, "" + (int) mainFrame.getLocation().getY());
properties.setProperty(PROPERTY_GEOMETRY_WIDTH, "" + mainFrame.getWidth());
properties.setProperty(PROPERTY_GEOMETRY_HEIGHT, "" + mainFrame.getHeight());
properties.setProperty(PROPERTY_GEOMETRY_EXTENDED_STATE, "" + mainFrame.getExtendedState());
//properties.setProperty(PROPERTY_GEOMETRY_DIVIDER_MAIN, "" + mainFrame.getMainDividerLocation());
//properties.setProperty(PROPERTY_GEOMETRY_DIVIDER_EDITOR, "" + mainFrame.getEditorDividerLocation());
//properties.setProperty(PROPERTY_GEOMETRY_DIVIDER_LOGGING, "" + mainFrame.getLoggingDividerLocation());
//properties.setProperty(PROPERTY_GEOMETRY_DIVIDER_GROUPSELECTION, "" + mainFrame.getGroupSelectionDividerLocation());
properties.setProperty(PROPERTY_EXPERT_MODE, "" + mainFrame.getPropertyPanel().isExpertMode());
File file = FileSystemService.getUserConfigFile("gui.properties");
OutputStream out = null;
try {
out = new FileOutputStream(file);
properties.store(out, "RapidMiner GUI properties");
} catch (IOException e) {
LogService.getRoot().log(Level.WARNING, "Cannot write GUI properties: " + e.getMessage(), e);
} finally {
try {
if (out != null)
out.close();
} catch (IOException e) { }
}
mainFrame.getResultDisplay().clearAll();
mainFrame.getPerspectives().saveAll();
}
}
private static void loadGUIProperties(MainFrame mainFrame) {
Properties properties = new Properties();
File file = FileSystemService.getUserConfigFile("gui.properties");
if (file.exists()) {
InputStream in = null;
try {
in = new FileInputStream(file);
properties.load(in);
} catch (IOException e) {
setDefaultGUIProperties();
} finally {
try {
if (in != null)
in.close();
} catch (IOException e) {
throw new Error(e); // should not occur
}
}
try {
mainFrame.setLocation(Integer.parseInt(properties.getProperty(PROPERTY_GEOMETRY_X)), Integer.parseInt(properties.getProperty(PROPERTY_GEOMETRY_Y)));
mainFrame.setSize(new Dimension(Integer.parseInt(properties.getProperty(PROPERTY_GEOMETRY_WIDTH)), Integer.parseInt(properties.getProperty(PROPERTY_GEOMETRY_HEIGHT))));
int extendedState;
if (properties.getProperty(PROPERTY_GEOMETRY_EXTENDED_STATE) == null) {
extendedState = JFrame.NORMAL;
} else {
extendedState = Integer.parseInt(properties.getProperty(PROPERTY_GEOMETRY_EXTENDED_STATE));
}
mainFrame.setExtendedState(extendedState);
mainFrame.setExpertMode(Boolean.valueOf(properties.getProperty(PROPERTY_EXPERT_MODE)).booleanValue());
} catch (NumberFormatException e) {
setDefaultGUIProperties();
}
} else {
setDefaultGUIProperties();
}
mainFrame.getPerspectives().loadAll();
}
/** This method sets some default GUI properties. This method can be invoked if the properties
* file was not found or produced any error messages (which might happen after version changes).
*/
private static void setDefaultGUIProperties() {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
mainFrame.setLocation((int)(0.05d * screenSize.getWidth()), (int)(0.05d * screenSize.getHeight()));
mainFrame.setSize((int)(0.9d * screenSize.getWidth()), (int)(0.9d * screenSize.getHeight()));
//mainFrame.setDividerLocations((int)(0.6d * screenSize.getHeight()), (int)(0.2d * screenSize.getWidth()), (int)(0.75d * screenSize.getWidth()), (int)(0.4d * screenSize.getWidth()));
mainFrame.setExpertMode(false);
}
public static void main(String[] args) throws Exception {
System.setSecurityManager(null);
RapidMiner.addShutdownHook(new ShutdownHook());
setExecutionMode(
System.getProperty(PROPERTY_HOME_REPOSITORY_URL) == null ?
ExecutionMode.UI : ExecutionMode.WEBSTART);
boolean shouldLaunch = true;
if (args.length > 0) {
if (!LaunchListener.defaultLaunchWithArguments(args, new RemoteControlHandler() {
@Override
public boolean handleArguments(String[] args) {
LogService.getRoot().info("Received message from second launching client: "+Arrays.toString(args));
mainFrame.requestFocus();
if (args.length >= 1) {
OpenAction.open(args[0], false);
}
return true;
}
})) {
shouldLaunch = false;
}
}
if (shouldLaunch) {
launch(args);
} else {
LogService.getRoot().config("Other RapidMiner instance already up. Exiting.");
}
}
private static void launch(String[] args) throws Exception {
String openLocation = null;
if (args.length > 0) {
if (args.length != 1) {
System.out.println("java " + RapidMinerGUI.class.getName() + " [processfile]");
return;
}
openLocation = args[0];
}
RapidMiner.setInputHandler(new GUIInputHandler());
new RapidMinerGUI().run(openLocation);
}
}