All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.math.R.RenjinSession Maven / Gradle / Ivy

There is a newer version: 3.1.8
Show newest version
package org.math.R;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.script.ScriptException;
import org.apache.commons.io.FileUtils;
import static org.math.R.Rsession.HEAD_EXCEPTION;
import org.math.array.DoubleArray;
import org.renjin.aether.AetherPackageLoader;
import org.renjin.eval.Session;
import org.renjin.eval.SessionBuilder;
import org.renjin.primitives.matrix.Matrix;
import org.renjin.primitives.packaging.PackageLoader;
import org.renjin.script.RenjinScriptEngine;
import org.renjin.script.RenjinScriptEngineFactory;
import org.renjin.sexp.DoubleArrayVector;
import org.renjin.sexp.DoubleVector;
import org.renjin.sexp.IntVector;
import org.renjin.sexp.ListVector;
import org.renjin.sexp.Logical;
import static org.renjin.sexp.Logical.TRUE;
import org.renjin.sexp.LogicalVector;
import org.renjin.sexp.Null;
import org.renjin.sexp.SEXP;
import org.renjin.sexp.StringArrayVector;
import org.renjin.sexp.StringVector;

/**
 *
 * @author richet
 */
public class RenjinSession extends Rsession implements RLog {

    protected RenjinScriptEngine R = null;
    File wdir;
    Properties properties;

    public static RenjinSession newInstance(final RLog console, Properties properties) {
        return new RenjinSession(console, properties);
    }

    public RenjinSession(RLog console, Properties properties) {
        super(console);

        Session session = new SessionBuilder().bind(PackageLoader.class, new AetherPackageLoader()).withDefaultPackages().build();

        R = new RenjinScriptEngineFactory().getScriptEngine(session);
        if (R == null) {
            throw new RuntimeException("Renjin Script Engine not found in the classpath.");
        }

        try {
            wdir = new File(new File(FileUtils.getTempDirectory(), ".Renjin"), "" + hashCode());
            if (!wdir.mkdirs()) {
                wdir = new File(new File(FileUtils.getUserDirectory(), ".Renjin"), "" + hashCode());
                if (!wdir.mkdirs()) {
                    throw new IOException("Could not create directory " + new File(new File(FileUtils.getTempDirectory(), ".Renjin"), "" + hashCode()) + "\n or " + new File(new File(FileUtils.getUserDirectory(), ".Renjin"), "" + hashCode()));
                }
            }
            R.eval("setwd('" + fixPathSeparator(wdir.getAbsolutePath()) + "')");
            wdir.deleteOnExit();
        } catch (Exception ex) {
            log("Could not use directory: " + wdir + "\n" + ex.getMessage(), Level.ERROR);
        }

        SINK_FILE = SINK_FILE_BASE + "-renjin" + this.hashCode();

        setenv(properties);

        repos = "org.renjin.cran";
    }

    public RenjinSession(final PrintStream p, Properties properties) {
        this(new RLog() {

            public void log(String string, Level level) {
                PrintStream pp = null;
                if (p != null) {
                    pp = p;
                } else {
                    pp = System.err;
                }

                if (level == Level.WARNING) {
                    p.print("(!) ");
                } else if (level == Level.ERROR) {
                    p.print("(!!) ");
                }
                p.println(string);
            }

            public void close() {
                if (p != null) {
                    p.close();
                }
            }
        }, properties);
    }

    @Override
    boolean isWindows() {
        return RserveDaemon.isWindows();
    }

    @Override
    boolean isMacOSX() {
        return RserveDaemon.isMacOSX();
    }

    @Override
    boolean isLinux() {
        return RserveDaemon.isLinux();
    }

    @Override
    public boolean silentlyVoidEval(String expression, boolean tryEval) {
        if (R == null) {
            log(HEAD_EXCEPTION + "R environment not initialized.", Level.ERROR);
            return false;
        }
        if (expression == null) {
            return false;
        }
        if (expression.trim().length() == 0) {
            return true;
        }
        for (EvalListener b : eval) {
            b.eval(expression);
        }
        SEXP e = null;
        synchronized (R) {
            try {
                if (SINK_OUTPUT) {
                    R.eval(".f <- file('" + fixPathSeparator(SINK_FILE) + "',open='wt')");
                    R.eval("sink(.f,type='output')");
                }
                if (SINK_MESSAGE) {
                    R.eval(".fm <- file('" + fixPathSeparator(SINK_FILE) + ".m',open='wt')");
                    R.eval("sink(.fm,type='message')");
                }
                if (tryEval) {
                    e = ((SEXP) R.eval("try(eval(parse(text='" + expression.replace("'", "\\'") + "')),silent=FALSE)"));
                } else {
                    e = ((SEXP) R.eval(expression));
                }
            } catch (Exception ex) {
                log(HEAD_EXCEPTION + ex.getMessage() + "\n  " + expression, Level.ERROR);
            } finally {
                if (SINK_OUTPUT) {
                    try {
                        R.eval("sink(type='output')");
                        lastOuput = asString(R.eval("paste(collapse='\n',readLines('" + fixPathSeparator(SINK_FILE) + "'))"));
                        log(lastOuput, Level.OUTPUT);
                    } catch (Exception ex) {
                        lastOuput = ex.getMessage();
                        log(lastOuput, Level.WARNING);
                    } finally {
                        try {
                            R.eval("close(.f)");
                            R.eval("unlink('" + fixPathSeparator(SINK_FILE) + "')");
                        } catch (Exception ex) {
                            log(HEAD_EXCEPTION + ex.getMessage(), Level.ERROR);
                        }
                    }
                }
                if (SINK_MESSAGE) {
                    try {
                        R.eval("sink(type='message')");
                        lastMessage = asString(R.eval("paste(collapse='\n',readLines('" + fixPathSeparator(SINK_FILE) + ".m'))"));
                        log(lastMessage, Level.INFO);
                    } catch (Exception ex) {
                        lastMessage = ex.getMessage();
                        log(lastMessage, Level.WARNING);
                    } finally {
                        try {
                            R.eval("close(.fm)");
                            R.eval("unlink('" + fixPathSeparator(SINK_FILE) + ".m')");
                        } catch (Exception ex) {
                            log(HEAD_EXCEPTION + ex.getMessage(), Level.ERROR);
                        }
                    }
                }
            }
        }

        if (tryEval && e != null) {
            try {
                if (e.inherits("try-error")/*e.isString() && e.asStrings().length > 0 && e.asString().toLowerCase().startsWith("error")*/) {
                    log(HEAD_EXCEPTION + e.asString() + "\n  " + expression, Level.WARNING);
                    return false;
                }
            } catch (Exception ex) {
                log(HEAD_ERROR + ex.getMessage() + "\n  " + expression, Level.ERROR);
                return false;
            }
        }
        return true;
    }

    @Override
    public Object silentlyRawEval(String expression, boolean tryEval) {
        if (R == null) {
            log(HEAD_EXCEPTION + "R environment not initialized.", Level.ERROR);
            return null;
        }
        if (expression == null) {
            return null;
        }
        if (expression.trim().length() == 0) {
            return null;
        }
        for (EvalListener b : eval) {
            b.eval(expression);
        }
        SEXP e = null;
        synchronized (R) {
            try {
                if (SINK_OUTPUT) {
                    R.eval(".f <- file('" + fixPathSeparator(SINK_FILE) + "',open='wt')");
                    R.eval("sink(.f,type='output')");
                }
                if (SINK_MESSAGE) {
                    R.eval(".fm <- file('" + fixPathSeparator(SINK_FILE) + ".m',open='wt')");
                    R.eval("sink(.fm,type='message')");
                }
                if (tryEval) {
                    e = (SEXP) (R.eval("try(eval(parse(text='" + expression.replace("'", "\\'") + "')),silent=FALSE)"));
                } else {
                    e = (SEXP) (R.eval(expression));
                }
            } catch (Exception ex) {
                log(HEAD_EXCEPTION + ex.getMessage() + "\n  " + expression, Level.ERROR);
            } finally {
                if (SINK_OUTPUT) {
                    try {
                        R.eval("sink(type='output')");
                        lastOuput = asString(R.eval("paste(collapse='\n',readLines('" + fixPathSeparator(SINK_FILE) + "'))"));
                        log(lastOuput, Level.OUTPUT);
                    } catch (Exception ex) {
                        lastOuput = ex.getMessage();
                        log(lastOuput, Level.WARNING);
                    } finally {
                        try {
                            R.eval("close(.f)"); // because Renjin.sink() do not properly close connection, so calling it explicitely
                            R.eval("unlink('" + fixPathSeparator(SINK_FILE) + "')");
                        } catch (Exception ex) {
                            log(HEAD_EXCEPTION + ex.getMessage(), Level.ERROR);
                        }
                    }
                }
                if (SINK_MESSAGE) {
                    try {
                        R.eval("sink(type='message')");
                        lastMessage = asString(R.eval("paste(collapse='\n',readLines('" + fixPathSeparator(SINK_FILE) + ".m'))"));
                        log(lastMessage, Level.INFO);
                    } catch (Exception ex) {
                        lastMessage = ex.getMessage();
                        log(lastMessage, Level.WARNING);
                    } finally {
                        try {
                            R.eval("close(.fm)");
                            R.eval("unlink('" + fixPathSeparator(SINK_FILE) + ".m')");
                        } catch (Exception ex) {
                            log(HEAD_EXCEPTION + ex.getMessage(), Level.ERROR);
                        }
                    }
                }
            }
        }

        if (tryEval && e != null) {
            try {
                if (e.inherits("try-error")/*e.isString() && e.asStrings().length > 0 && e.asString().toLowerCase().startsWith("error")*/) {
                    log(HEAD_EXCEPTION + e.asString() + "\n  " + expression, Level.WARNING);
                    e = null;
                }
            } catch (Exception ex) {
                log(HEAD_ERROR + ex.getMessage() + "\n  " + expression, Level.ERROR);
                return null;
            }
        }
        return e;
    }

    @Override
    public boolean set(String varname, double[][] data, String... names) {
        if (data == null) {

            if (names == null) {
                return false;
            }
            List nulls = new LinkedList<>();
            for (int i = 0; i < names.length; i++) {
                nulls.add(Null.INSTANCE.clone());
            }

            ListVector l = new ListVector(nulls);
            synchronized (R) {
                R.put(varname, l);
                R.put(varname + ".names", new StringArrayVector(names));
                try {
                    R.eval("names(" + varname + ") <- " + varname + ".names");
                    //R.eval(varname + " <- data.frame(" + varname + ")");
                } catch (ScriptException ex) {
                    ex.printStackTrace();
                    return false;
                }
            }
            return true;

        } else {

            DoubleVector[] d = new DoubleVector[data[0].length];
            for (int i = 0; i < d.length; i++) {
                d[i] = new DoubleArrayVector(DoubleArray.getColumnCopy(data, i));
            }
            ListVector l = new ListVector(d);
            //l.setAttribute(Symbols.NAMES, new StringArrayVector(names)); 
            synchronized (R) {
                R.put(varname, l);
                //R.put("names("+varname+")",new StringArrayVector(names));
                R.put(varname + ".names", new StringArrayVector(names));
                try {
                    R.eval("names(" + varname + ") <- " + varname + ".names");
                    R.eval(varname + " <- data.frame(" + varname + ")");
                } catch (ScriptException ex) {
                    ex.printStackTrace();
                    return false;
                }
            }
            return true;
        }
    }

    @Override
    public boolean set(String varname, Object var) {
        if (var instanceof double[][]) {
            double[][] dd = (double[][]) var;
            double[] d = reshapeAsRow(dd);
            synchronized (R) {
                R.put(varname, d);
                try {
                    R.eval(varname + " <- matrix(" + varname + ",nrow=" + dd.length + ")");
                } catch (ScriptException ex) {
                    ex.printStackTrace();
                    return false;
                }
            }
        } else {
            synchronized (R) {
                R.put(varname, var);
            }
        }
        return true;
    }

    // http://docs.renjin.org/en/latest/library/moving-data-between-java-and-r-code.html
    @Override
    public double asDouble(Object o) throws ClassCastException {
        if (o == null) {
            return (Double) null;
        }
        if (o instanceof Double) {
            return (double) o;
        }
        if (!(o instanceof SEXP)) {
            throw new IllegalArgumentException("[asDouble] Not an SEXP object: " + o);
        }
        try {
            return ((SEXP) o).asReal();
        } catch (Exception ex) {
            throw new ClassCastException("[asDouble] Cannot cast to double " + o);
        }
    }

    @Override
    public double[] asArray(Object o) throws ClassCastException {
        if (o == null) {
            return null;
        }
        if (o instanceof double[]) {
            return (double[]) o;
        }
        if (o instanceof Double) {
            return new double[]{(double) o};
        }
        if (!(o instanceof SEXP)) {
            throw new IllegalArgumentException("[asArray] Not an SEXP object: " + o);
        }
        if (!(o instanceof DoubleVector)) {
            throw new IllegalArgumentException("[asArray] Not a DoubleVector object: " + o);
        }
        try {
            return ((DoubleVector) o).toDoubleArray();
        } catch (Exception ex) {
            throw new ClassCastException("[asArray] Cannot cast to double[] " + o);
        }
    }

    @Override
    public double[][] asMatrix(Object o) throws ClassCastException {
        if (o == null) {
            return null;
        }
        if (o instanceof double[][]) {
            return (double[][]) o;
        }
        if (o instanceof double[]) {
            return t(new double[][]{(double[]) o});
        }
        if (o instanceof Double) {
            return new double[][]{{(double) o}};
        }
        if (!(o instanceof SEXP)) {
            throw new IllegalArgumentException("[asMatrix] Not an SEXP object: " + o);
        }
        if (!(o instanceof DoubleVector)) {
            throw new IllegalArgumentException("[asMatrix] Not a DoubleVector object: " + o);
        }
        try {
            Matrix m = new Matrix((DoubleVector) o);
            double[][] mm = new double[m.getNumRows()][m.getNumCols()];
            for (int i = 0; i < mm.length; i++) {
                for (int j = 0; j < mm[i].length; j++) {
                    mm[i][j] = m.getElementAsDouble(i, j);
                }
            }
            return mm;
        } catch (Exception ex) {
            throw new ClassCastException("[asMatrix] Cannot cast to double[][] " + o);
        }
    }

    @Override
    public String asString(Object o) throws ClassCastException {
        if (o == null) {
            return null;
        }
        if (o instanceof String) {
            return (String) o;
        }
        if (!(o instanceof SEXP)) {
            throw new IllegalArgumentException("[asString] Not an SEXP object: " + o);
        }
        try {
            return ((SEXP) o).asString();
        } catch (Exception ex) {
            throw new ClassCastException("[asString] Cannot cast to String " + o);
        }
    }

    @Override
    public String[] asStrings(Object o) throws ClassCastException {
        if (o == null) {
            return null;
        }
        if (o instanceof String[]) {
            return (String[]) o;
        }
        if (o instanceof String) {
            return new String[]{(String) o};
        }
        if (!(o instanceof SEXP)) {
            throw new IllegalArgumentException("[asStrings] Not an SEXP object: " + o);
        }
        if (!(o instanceof StringVector)) {
            throw new IllegalArgumentException("[asStrings] Not a StringVector object: " + o);
        }
        try {
            int n = ((SEXP) o).length();
            String[] s = new String[n];
            for (int i = 0; i < n; i++) {
                s[i] = ((SEXP) o).getElementAsSEXP(i).asString();
            }
            return s;
        } catch (Exception ex) {
            throw new ClassCastException("[asStrings] Cannot cast to String[] " + o);
        }
    }

    @Override
    public int asInteger(Object o) throws ClassCastException {
        if (o == null) {
            return (Integer) null;
        }
        return asIntegers(o)[0];
    }

    @Override
    public int[] asIntegers(Object o) throws ClassCastException {
        if (o == null) {
            return null;
        }
        if (o instanceof int[]) {
            return (int[]) o;
        }
        if (o instanceof Integer) {
            return new int[]{(int) o};
        }
        if (!(o instanceof SEXP)) {
            throw new IllegalArgumentException("[asIntegers] Not an SEXP object: " + o);
        }
        if (!(o instanceof IntVector)) {
            throw new IllegalArgumentException("[asIntegers] Not a IntVector object: " + o);
        }
        try {
            return ((IntVector) o).toIntArray();
        } catch (Exception ex) {
            throw new ClassCastException("[asIntegers] Cannot cast to int[] " + o);
        }
    }

    @Override
    public boolean asLogical(Object o) throws ClassCastException {
        if (o == null) {
            return (Boolean) null;
        }
        if (o instanceof Boolean) {
            return (boolean) o;
        }
        if (!(o instanceof SEXP)) {
            throw new IllegalArgumentException("[asLogical] Not an SEXP object: " + o);
        }
        try {
            return ((SEXP) o).asLogical() == Logical.TRUE;
        } catch (Exception ex) {
            throw new ClassCastException("[asLogical] Cannot cast to boolean " + o);
        }
    }

    @Override
    public boolean[] asLogicals(Object o) throws ClassCastException {
        if (o == null) {
            return null;
        }
        if (o instanceof boolean[]) {
            return (boolean[]) o;
        }
        if (o instanceof Boolean) {
            return new boolean[]{(boolean) o};
        }
        if (!(o instanceof SEXP)) {
            throw new IllegalArgumentException("[asLogicals] Not an SEXP object: " + o);
        }
        if (!(o instanceof LogicalVector)) {
            throw new IllegalArgumentException("[asLogicals] Not a LogicalVector object: " + o);
        }

        try {
            int n = ((SEXP) o).length();
            boolean[] s = new boolean[n];
            for (int i = 0; i < n; i++) {
                s[i] = ((SEXP) o).getElementAsSEXP(i).asLogical() == Logical.TRUE;
            }
            return s;
        } catch (Exception ex) {
            throw new ClassCastException("[asLogicals] Cannot cast to boolean[] " + o);
        }
    }

    @Override
    public Map asList(Object o) throws ClassCastException {
        if (o == null) {
            return null;
        }
        if (o instanceof Map) {
            return (Map) o;
        }
        if (!(o instanceof SEXP)) {
            throw new IllegalArgumentException("[asList] Not an SEXP object: " + o);
        }
        if (!(o instanceof ListVector)) {
            throw new IllegalArgumentException("[asList] Not a ListVector object: " + o);
        }
        ListVector l = (ListVector) o;
        Map m = new HashMap();
        for (int i = 0; i < l.length(); i++) {
            m.put(l.getName(i), cast(l.get(i)));
        }
        return m;
    }

    @Override
    public boolean isNull(Object o) {
        if (o == null) {
            return true;
        }
        if (!(o instanceof SEXP)) {
            throw new IllegalArgumentException("[isNull] Not an SEXP object: " + o);
        }
        try {
            return o instanceof Null;
        } catch (Exception ex) {
            throw new ClassCastException("[isNull] Cannot cast to Null " + o);
        }
    }

    @Override
    public String toString(Object o) {
        if (o instanceof SEXP) {
            try {
                return ((SEXP) o).toString();
            } catch (Exception ex) {
                throw new ClassCastException("[toString] Cannot toString " + o);
            }
        } else if (o.getClass().isArray()) {
            return Arrays.asList(o).toString();
        } else {
            return o.toString();
        }
    }

    @Override
    public void close() {
        super.close();
        R.getSession().close();
    }

    public Object cast(Object o) throws ClassCastException {
        if (o == null) {
            return null;
        }

        if (!(o instanceof SEXP)) {
            throw new ClassCastException("[cast] Not an SEXP object");
        }

        SEXP s = (SEXP) o;

        if (s.length() != 1) {
            switch (s.getTypeName()) {
                case "logical":
                    return asLogicals(s);
                case "integer":
                    return asIntegers(s);
                case "double":
                    if (s.getAttributes().get("dim").length() == 2) {
                        return asMatrix(s);
                    }
                    return asArray(s);
                case "character":
                    return asStrings(s);
                case "list":
                    return asList(s);
                case "NULL":
                    return null;
                default:
                    throw new ClassCastException("Cannot cast " + s + " (class " + s.getImplicitClass() + ", type " + s.getTypeName() + ")");
            }
        } else {
            switch (s.getTypeName()) {
                case "logical":
                    return asLogical(s);
                case "integer":
                    return asInteger(s);
                case "double":
                    return asDouble(s);
                case "character":
                    return asString(s);
                case "list":
                    return asList(s);
                case "closure":
                    String name = "function_" + (int) Math.floor(1000 * Math.random());
                    synchronized (R) {
                        R.put(name, s);
                        try {
                            if (((SEXP) rawEval("is.function(" + name + ")")).asLogical() == TRUE) {
                                return new Function(name);
                            }
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                    }
                case "NULL":
                    return null;
                default:
                    throw new ClassCastException("Cannot cast " + s + " (class " + s.getImplicitClass() + ", type " + s.getTypeName() + ")");

            }
        }
        //throw new ClassCastException("Cannot cast to ? " + s + " (" + s.getTypeName() + ")");
    }

    //protected Context topLevelContext = Context.newTopLevelContext();
    @Override
    public void toGraphic(File f, int width, int height, String fileformat, String... commands) {
        throw new UnsupportedOperationException("Graphics not yet available using Renjin");

        /*BufferedImage image = new BufferedImage(width, height, ColorSpace.TYPE_RGB);

         Graphics2D g2d = (Graphics2D) image.getGraphics();
         g2d.setColor(Color.WHITE);
         g2d.setBackground(Color.WHITE);
         g2d.fill(g2d.getDeviceConfiguration().getBounds());

         AwtGraphicsDevice driver = new AwtGraphicsDevice(g2d);
         topLevelContext.getSingleton(GraphicsDevices.class).setActive(new org.renjin.graphics.GraphicsDevice(driver));

         try {
         StringWriter w = new StringWriter();
         PrintWriter p = R.getSession().getStdErr();
         R.getSession().setStdErr(new PrintWriter(w));

         for (String command : commands) {
         voidEval(command);
         }
         R.getSession().setStdErr(p);
         System.err.println(w.getBuffer());
         } finally {
         try {
         FileOutputStream fos = new FileOutputStream(f);

         ImageIO.write(image, fileformat.toUpperCase(), fos);
         fos.close();
         } catch (IOException ex) {
         ex.printStackTrace();
         }
         }*/
    }

    /**
     * Get R command text output
     *
     * @param command R command returning text
     * @return String
     */
    public String print(String command) {
        StringWriter w = new StringWriter();
        try {
            PrintWriter p = R.getSession().getStdOut();
            R.getSession().setStdOut(new PrintWriter(w));
            silentlyRawEval("print(" + command + ")");
            R.getSession().setStdOut(p);
            return w.toString();//.substring(l);
        } catch (Exception ex) {
            return ex.getMessage();
        }
    }

    /**
     * @param url CRAN repository to use for packages installation (eg
     * http://cran.r-project.org)
     */
    public void setCRANRepository(String url) {
        if (!url.equals(repos)) {
            log("Cannot use another repositroy that " + repos, Level.WARNING);
        }
    }

    @Override
    public String installPackage(String pack, boolean load) {
        if (!load) {
            return "Renjin does not support yet installing package without loading.";
        }
        try {
            return loadPackage(pack);
        } catch (Exception e) {
            return "Renjin cannot install package " + pack + " yet: " + e.getMessage();
        }
    }

    @Override
    public String installPackage(String pack, File dir, boolean load) {
        if (!load) {
            return "Renjin does not support yet installing package without loading.";
        }
        try {
            return "Renjin does not support yet installing local package.";
            //return super.installPackage(RENJIN_REPO_PREFIX + pack, dir, load);
        } catch (Exception e) {
            return "Renjin cannot install package " + pack + " from " + dir + " yet: " + e.getMessage();
        }
    }

    @Override
    public boolean isAvailable() {
        return true;
    }

    public static void main(String[] args) throws Exception {
        //args = new String[]{"install.packages('lhs',repos='\"http://cloud.r-project.org/\"',lib='.')", "1+1"};
        if (args == null || args.length == 0) {
            args = new String[10];
            for (int i = 0; i < args.length; i++) {
                args[i] = Math.random() + "+pi";
            }
        }
        RenjinSession R = new RenjinSession(new RLogSlf4j(), null);

        for (int j = 0; j < args.length; j++) {
            System.out.print(args[j] + ": ");
            System.out.println(R.cast(R.rawEval(args[j])));
        }

        R.close();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy