package org.rosuda.JGR;
// JGR - Java Gui for R, see http://www.rosuda.org/JGR/
// Copyright (C) 2003 - 2005 Markus Helbig
// --- for licensing information see LICENSE file in the original JGR
// distribution ---
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import org.rosuda.JGR.toolkit.ConsoleSync;
import org.rosuda.JGR.toolkit.JGRListener;
import org.rosuda.JGR.toolkit.JGRPrefs;
import org.rosuda.JGR.util.ErrorMsg;
import org.rosuda.REngine.REXP;
import org.rosuda.REngine.REXPMismatchException;
import org.rosuda.REngine.REXPString;
import org.rosuda.REngine.REngine;
import org.rosuda.REngine.REngineException;
import org.rosuda.REngine.JRI.JRIEngine;
import org.rosuda.ibase.SVar;
import org.rosuda.ibase.toolkit.EzMenuSwing;
import org.rosuda.util.Global;
/**
* JGR, Java Gui for R JGR is just a new Gui for R http://www.r-project.org , written in
* Java.
* Thus it is (should) be platform-indepent. Currently we have several problems
* on *nix machines.
*
* JGR uses JRI and rJava for talking
* with R, and the JavaGD- Device
* all written by Simon Urbanek.
*
* RoSuDa 2003 - 2005
*
* @author Markus Helbig
*/
public class JGR {
/** Version number of JGR */
public static final String VERSION = "1.7-19";
/** Title (used for displaying the splashscreen) */
public static final String TITLE = "JGR";
/** Subtitle (used for displaying the splashscreen) */
public static final String SUBTITLE = "Java Gui for R";
/** Develtime (used for displaying the splashscreen) */
public static final String DEVELTIME = "2003 - 2017";
/** Organization (used for displaying the splashscreen) */
public static final String INSTITUTION = "RoSuDa, Univ. Augsburg";
/** Author JGR (used for displaying the splashscreen) */
public static final String AUTHOR1 = "Markus Helbig";
/** Author JRI, rJava and JavaGD (used for displaying the splashscreen) */
public static final String AUTHOR2 = "Simon Urbanek";
/** Author JGR parts since 2009 */
public static final String AUTHOR3 = "Ian Fellows";
/** Website of organization (used for displaying the splashscreen) */
public static final String WEBSITE = "http://www.rosuda.org";
/** Filename of the splash-image (used for displaying the splashscreen) */
public static final String SPLASH = "jgrsplash.jpg";
/** Main-console window */
public static JGRConsole MAINRCONSOLE = null;
/**
* The history of current session. If there was a .Rhistory file, it will be
* loaded into this vector
*/
public static Vector RHISTORY = null;
/** RHOME path of current used R */
public static String RHOME = null;
/** Rengine {@link org.rosuda.JRI.Rengine}, needed for executing R-commands */
private static REngine rEngine = null;
/** ConsoleSnyc {@link org.rosuda.JRG.toolkit.ConsoleSync} */
public static ConsoleSync rSync = new ConsoleSync();
/** Current data-sets (data.frames, matrix, ...) */
public static Vector DATA = new Vector();
/** Current models */
public static Vector MODELS = new Vector();
/** Current data not in DATA {@link DATA} */
public static Vector OTHERS = new Vector();
/** Current functions */
public static Vector FUNCTIONS = new Vector();
/** Current objects in workspace */
public static Vector OBJECTS = new Vector();
/** Keywords for syntaxhighlighting */
public static Vector KEYWORDS = new Vector();
/** Keywords (objects) for syntaxhighlighting */
public static Vector KEYWORDS_OBJECTS = new Vector();
/** Indicates wether the Rengine is up or not */
public static boolean STARTED = false;
/**
* JGRListener, is uses for listening to java-commands coming from JGR's
* R-process
*/
private static JGRListener jgrlistener = null;
/** Splashscreen */
public static org.rosuda.JGR.toolkit.SplashScreen splash;
/** arguments from command line */
public static String[] arguments = new String[]{};
/** arguments for Rengine */
private static String[] rargs = { "--save" };
/**
* Set to true
when JGR is running as the main program and
* false
if the classes are loaded, but not run via main.
*/
private static boolean JGRmain = false;
private static String tempWD;
/**
* Default packages passed though launcher
*/
private static String launcherPackages = null;
/**
* should splash screen be displayed
*/
private static boolean showSplash = true;
/**
* Starting the JGR Application (javaside)
*/
public JGR() {
SVar.int_NA = -2147483648;
Object dummy = new Object();
JGRPackageManager.neededPackages.put("base", dummy);
JGRPackageManager.neededPackages.put("graphics", dummy);
JGRPackageManager.neededPackages.put("grDevices", dummy);
JGRPackageManager.neededPackages.put("utils", dummy);
JGRPackageManager.neededPackages.put("methods", dummy);
JGRPackageManager.neededPackages.put("stats", dummy);
JGRPackageManager.neededPackages.put("datasets", dummy);
JGRPackageManager.neededPackages.put("JGR", dummy);
JGRPackageManager.neededPackages.put("rJava", dummy);
JGRPackageManager.neededPackages.put("JavaGD", dummy);
JGRPackageManager.neededPackages.put("iplots", dummy);
org.rosuda.util.Platform.initPlatform("org.rosuda.JGR.toolkit.");
JGRPrefs.initialize();
splash = new org.rosuda.JGR.toolkit.SplashScreen();
if(showSplash)
splash.start();
readHistory();
MAINRCONSOLE = new JGRConsole();
MAINRCONSOLE.setWorking(true);
if(showSplash)
splash.toFront();
if (showSplash && System.getProperty("os.name").startsWith("Window")) {
splash.stop();
JGRPrefs.isWindows = true;
}
// let's preemptively load JRI - if we do it here, we can show an error
// message
try {
System.loadLibrary("jri");
} catch (UnsatisfiedLinkError e) {
String errStr = "all environment variables (PATH, LD_LIBRARY_PATH, etc.) are setup properly (see supplied script)";
String libName = "libjri.so";
if (System.getProperty("os.name").startsWith("Window")) {
errStr = "you start JGR by double-clicking the JGR.exe program";
libName = "jri.dll";
}
if (System.getProperty("os.name").startsWith("Mac")) {
errStr = "you start JGR by double-clicking the JGR application";
libName = "libjri.jnilib";
}
JOptionPane.showMessageDialog(null, "Cannot find Java/R Interface (JRI) library (" + libName + ").\nPlease make sure " + errStr + ".",
"Cannot find JRI library", JOptionPane.ERROR_MESSAGE);
System.err.println("Cannot find JRI native library!\n");
e.printStackTrace();
System.exit(1);
}
if (!org.rosuda.JRI.Rengine.versionCheck()) {
JOptionPane.showMessageDialog(null,
"Java/R Interface (JRI) library doesn't match this JGR version.\nPlease update JGR and JRI to the latest version.",
"Version Mismatch", JOptionPane.ERROR_MESSAGE);
System.exit(2);
}
try {
rEngine = new JRIEngine(rargs, MAINRCONSOLE);
} catch (Exception e) {
JOptionPane.showMessageDialog(null,
"Unable to start R: " + e.getMessage(),
"REngine problem", JOptionPane.ERROR_MESSAGE);
System.err.println("Cannot start REngine " + e);
System.exit(1);
}
try {
// to avoid quoting hell we use an assignment
rEngine.assign(".$JGR", new REXPString(JGRPrefs.workingDirectory));
JGR.eval("try({setwd(`.$JGR`); rm(`.$JGR`)},silent=T)");
} catch (REngineException e) {
new ErrorMsg(e);
} catch (REXPMismatchException e) {
new ErrorMsg(e);
}
// load packages requested by the user
// in theory we could have checked for jgr.load.pkgs property, but in
// practice it doesn't
// hurt if we load them despite the fact that R_DEFAULT_PACKAGES has
// been set.
RController.requirePackages("JGR"); // ensure JGR is loaded
JGRPackageManager.defaultPackages = RController.getJgrDefaultPackages();
if(launcherPackages != null)
RController.requirePackages(launcherPackages);
if (JGRPrefs.defaultPackages != null)
RController.requirePackages(JGRPrefs.defaultPackages);
STARTED = true;
if (showSplash && !System.getProperty("os.name").startsWith("Win"))
splash.stop();
// make sure we get a clean prompt after all packages have been loaded
JGR.MAINRCONSOLE.execute("", false);
MAINRCONSOLE.toFront();
MAINRCONSOLE.input.requestFocus();
int w = MAINRCONSOLE.getFontWidth();
if(w>0)
JGR.threadedEval("options(width=" + w + ")");
// redefine Java Output streams to be written to JGR console
System.setOut(MAINRCONSOLE.getStdOutPrintStream());
System.setErr(MAINRCONSOLE.getStdErrPrintStream());
//kludge to fix infinite recursion crash on mac os x
//get rid of this when bug is fixed
//UPDATE: this is fixed as of 1.7-16 provided the -Xss10m opion is used
// to start java. This is default on the mac 2.0 launcher.
//if (System.getProperty("os.name").startsWith("Mac"))
// try{JGR.threadedEval("options(expressions=1000)");}catch(Exception e){}
new Refresher().run();
}
public static REXP idleEval(String cmd) throws REngineException, REXPMismatchException {
if (getREngine() == null)
throw new REngineException(null, "REngine not available");
REXP x = null;
int lock = getREngine().tryLock();
if (lock != 0) {
try {
x = getREngine().parseAndEval(cmd, null, true);
} finally {
getREngine().unlock(lock);
}
}
return x;
}
public static REXP eval(String cmd) throws REngineException, REXPMismatchException {
if (getREngine() == null)
throw new REngineException(null, "REngine not available");
REXP x = getREngine().parseAndEval(cmd, null, true);
return x;
}
public static void threadedEval(String cmd){
final String c = cmd;
new Thread(new Runnable(){
public void run() {
try {
JGR.eval(c);
} catch (Exception e) {}
}
}).start();
}
public static REXP timedEval(String cmd){
return timedEval(cmd,15000,true);
}
public static REXP timedEval(String cmd,boolean ask){
return timedEval(cmd,15000,ask);
}
public static REXP timedEval(String cmd,int interval,boolean ask){
return new MonitoredEval(interval,ask).run(cmd);
}
public static void timedAssign(String symbol, REXP value){
timedAssign(symbol, value,15000,true);
}
public static void timedAssign(String symbol, REXP value,boolean ask){
timedAssign(symbol, value,15000,ask);
}
public static void timedAssign(String symbol, REXP value,int interval,boolean ask){
new MonitoredEval(interval,ask).assign(symbol, value);
}
public static REngine getREngine() {
return rEngine;
}
public static void setREngine(REngine e){
rEngine=e;
}
/**
* Exits JGR, but not before asked the user if he wants to save his
* workspace.
*
* @return users's answer (yes/no/cancel)
*/
public static String exit() {
int exit = 1;
if (JGRPrefs.askForSavingWorkspace) {
exit = JOptionPane
.showConfirmDialog(null, "Save workspace?", "Close JGR", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
}
if (exit == 0) {
writeHistory();
JGRPrefs.writeCurrentPackagesWhenExit();
return "y\n";
} else if (exit == 1) {
JGRPrefs.writeCurrentPackagesWhenExit();
return "n\n";
} else
return "c\n";
}
/**
* Add new Menu at runtime to Console.
*
* @param name
* MenuName
*/
public static void addMenu(String name) {
if (MAINRCONSOLE == null)
return;
EzMenuSwing.addMenu(MAINRCONSOLE, name);
}
/**
* Insert new Menu at runtime to Console.
*
* @param name
* MenuName
* @param pos
* position at which to insert
*/
public static void insertMenu(String name,int pos) {
if (MAINRCONSOLE == null)
return;
insertMenu(MAINRCONSOLE, name,pos);
}
//temporary should be in EzMenuSwing
private static void insertMenu(JFrame f, String name,int index) {
JMenuBar mb = f.getJMenuBar();
JMenu m = EzMenuSwing.getMenu(f,name);
if (m == null && index 0)
new JGRPackageManager(previousPkg);
} catch (Exception e) {
}
}
/**
* Starts JGR
* options:
*
* - --debug: enable debug information
*
* or any other option supported by R.
*/
public static void main(String[] args) {
JGRmain = true;
arguments = args;
if (args.length > 0) {
Vector args2 = new Vector();
for (int i = 0; i < args.length; i++) {
if (args[i].equals("--debug")) {
org.rosuda.util.Global.DEBUG = 1;
org.rosuda.JRI.Rengine.DEBUG = 1;
System.out.println("JGR version " + VERSION);
} else
args2.add(args[i]);
if (args[i].equals("--version")) {
System.out.println("JGR version " + VERSION);
System.exit(0);
}
if (args[i].equals("--help") || args[i].equals("-h")) {
System.out.println("JGR version " + VERSION);
System.out.println("\nOptions:");
System.out.println("\n\t-h, --help\t Print short helpmessage and exit");
System.out.println("\t--version\t Print version end exit");
System.out.println("\t--debug\t Print more information about JGR's process");
System.out.println("\nMost other R options are supported too");
System.exit(0);
}
if (args[i].startsWith("--withPackages=")){
launcherPackages = args[i].substring(15);
}
if (args[i].startsWith("--noSplash")){
showSplash=false;
}
}
Object[] arguments = args2.toArray();
if (arguments.length > 0) {
rargs = new String[arguments.length + 1];
for (int i = 0; i < rargs.length - 1; i++)
rargs[i] = arguments[i].toString();
rargs[rargs.length - 1] = "--save";
}
}
if (Global.DEBUG > 0)
for (int i = 0; i < rargs.length; i++)
System.out.println(rargs[i]);
String nativeLF = UIManager.getSystemLookAndFeelClassName();
// Install the look and feel
try {
UIManager.setLookAndFeel(nativeLF);
} catch (InstantiationException e) {
} catch (ClassNotFoundException e) {
} catch (UnsupportedLookAndFeelException e) {
} catch (IllegalAccessException e) {
}
try {
new JGR();
} catch (Exception e) {
new ErrorMsg(e);
}
}
public static void refreshObjects() {
REXP x;
try {
x = idleEval("try(.refreshObjects(),silent=TRUE)");
String[] r = null;
if (x != null && (r = x.asStrings()) != null)
JGR.setObjects(r);
} catch (REngineException e) {
new ErrorMsg(e);
} catch (REXPMismatchException e) {
new ErrorMsg(e);
}
}
/**
* Refresher, which is looking for new keywords and objects in workspace and
* refreshes highlight and autocompletion information.
*/
class Refresher implements Runnable {
public Refresher() {
checkForMissingPkg();
}
public void run() {
while (true)
try {
Thread.sleep(5000);
REXP x = idleEval("try(.refreshKeyWords(),silent=TRUE)");
String[] r = null;
if (x != null && (r = x.asStrings()) != null)
setKeyWords(r);
x = idleEval("try(.refreshObjects(),silent=TRUE)");
r = null;
if (x != null && (r = x.asStrings()) != null)
setObjects(r);
RController.refreshObjects();
} catch (Exception e) {
new ErrorMsg(e);
}
}
}
}
final class MonitoredEval{
volatile boolean done;
volatile REXP result;
int interval;
int checkInterval;
boolean ask;
public MonitoredEval(int inter,boolean ak){
done = false;
interval = inter;
checkInterval = interval;
ask=ak;
}
protected void startMonitor(){
int t = 0;
while(true){
try {
Thread.sleep(checkInterval);
} catch (InterruptedException e) {
return;
}
if(done)
return;
if(t+checkInterval