package org.rosuda.JRclient; import java.util.*; /** representation of R-eXpressions in Java @version $Id: REXP.java,v 1.17 2004/08/01 20:31:52 urbaneks Exp $ */ public class REXP extends Object { /** xpression type: NULL */ public static final int XT_NULL=0; /** xpression type: integer */ public static final int XT_INT=1; /** xpression type: double */ public static final int XT_DOUBLE=2; /** xpression type: String */ public static final int XT_STR=3; /** xpression type: language construct (currently content is same as list) */ public static final int XT_LANG=4; /** xpression type: symbol (content is symbol name: String) */ public static final int XT_SYM=5; /** xpression type: RBool */ public static final int XT_BOOL=6; /** xpression type: Vector */ public static final int XT_VECTOR=16; /** xpression type: RList */ public static final int XT_LIST=17; /** xpression type: closure (there is no java class for that type (yet?). currently the body of the closure is stored in the content part of the REXP. Please note that this may change in the future!) */ public static final int XT_CLOS=18; /** xpression type: int[] */ public static final int XT_ARRAY_INT=32; /** xpression type: double[] */ public static final int XT_ARRAY_DOUBLE=33; /** xpression type: String[] (currently not used, Vector is used instead) */ public static final int XT_ARRAY_STR=34; /** internal use only! this constant should never appear in a REXP */ public static final int XT_ARRAY_BOOL_UA=35; /** xpression type: RBool[] */ public static final int XT_ARRAY_BOOL=36; /** xpression type: unknown; no assumptions can be made about the content */ public static final int XT_UNKNOWN=48; /** xpression type: RFactor; this XT is internally generated (ergo is does not come from Rsrv.h) to support RFactor class which is built from XT_ARRAY_INT */ public static final int XT_FACTOR=127; /** xpression type */ int Xt; /** attribute xpression or null if none */ REXP attr; /** content of the xpression - its object type is dependent of {@link #Xt} */ Object cont; /** cached binary length; valid only if positive */ long cachedBinaryLength=-1; /** construct a new, empty (NULL) expression w/o attribute */ public REXP() { Xt=0; attr=null; cont=null; } /** construct a new xpression of type t and content o, but no attribute @param t xpression type (XT_...) @param o content */ public REXP(int t, Object o) { Xt=t; cont=o; attr=null; } /** construct a new xpression of type t, content o and attribute at @param t xpression type @param o content @param at attribute */ public REXP(int t, Object o, REXP at) { Xt=t; cont=o; attr=at; } /** construct a new xpression of type XT_ARRAY_DOUBLE and content val @param val array of doubles to store in the REXP */ public REXP(double[] val) { this(XT_ARRAY_DOUBLE,val); } /** construct a new xpression of type XT_ARRAY_INT and content val @param val array of integers to store in the REXP */ public REXP(int[] val) { this(XT_ARRAY_INT,val); } /** construct a new xpression of type XT_ARRAY_INT and content val @param val array of integers to store in the REXP */ public REXP(String[] val) { this(XT_ARRAY_STR,val); } /** get attribute of the REXP. In R every object can have attached attribute xpression. Some more complex structures such as classes are built that way. @return attribute xpression or null if there is none associated */ public REXP getAttribute() { return attr; } /** get raw content. Use as... methods to retrieve contents of known type. @return content of the REXP */ public Object getContent() { return cont; } /** get xpression type (see XT_.. constants) of the content. It defines the type of the content object. @return xpression type */ public int getType() { return Xt; } /** parses byte buffer for binary representation of xpressions - read one xpression slot (descends recursively for aggregated xpressions such as lists, vectors etc.) @param x xpression object to store the parsed xpression in @param buf buffer containing the binary representation @param o offset in the buffer to start at @return position just behind the parsed xpression. Can be use for successive calls to {@link #parseREXP} if more than one expression is stored in the binary array. */ public static int parseREXP(REXP x, byte[] buf, int o) { int xl=Rtalk.getLen(buf,o); boolean hasAtt=((buf[o]&128)!=0); boolean isLong=((buf[o]&64)!=0); int xt=(int)(buf[o]&63); //System.out.println("parseREXP: type="+xt+", len="+xl+", hasAtt="+hasAtt+", isLong="+isLong); if (isLong) o+=4; o+=4; int eox=o+xl; x.Xt=xt; x.attr=null; if (hasAtt) o=parseREXP(x.attr=new REXP(),buf,o); if (xt==XT_NULL) { x.cont=null; return o; }; if (xt==XT_DOUBLE) { long lr=Rtalk.getLong(buf,o); x.cont=new Double(Double.longBitsToDouble(lr)); o+=8; if (o!=eox) { System.out.println("Warning: double SEXP size mismatch\n"); o=eox; }; return o; } if (xt==XT_ARRAY_DOUBLE) { int as=(eox-o)/8,i=0; double[] d=new double[as]; while (oPlease note that currently only XT_[ARRAY_]INT, XT_[ARRAY_]DOUBLE and XT_[ARRAY_]STR are supported! All other types will return 4 which is the size of the header. @return length of the REXP including headers (4 or 8 bytes)*/ public int getBinaryLength() { int l=0; switch (Xt) { case XT_INT: l=4; break; case XT_DOUBLE: l=8; break; case XT_STR: l=(cont==null)?1:((String)cont).length()+1; break; case XT_ARRAY_INT: l=(cont==null)?0:((int[])cont).length*4; break; case XT_ARRAY_DOUBLE: l=(cont==null)?0:((double[])cont).length*8; break; case XT_ARRAY_STR: if (cachedBinaryLength<0) { // if there's no cache, we have to count.. if (cont==null) cachedBinaryLength=4; else { String sa[]=(String[])cont; int i=0, io=0; while (i0xfffff0) cachedBinaryLength+=4; } } return (int)cachedBinaryLength; } // switch if (l>0xfffff0) l+=4; // large data need 4 more bytes return l+4; } /** Stores the REXP in its binary (ready-to-send) representation including header into a buffer and returns the index of the byte behind the REXP.

Please note that currently only XT_[ARRAY_]INT, XT_[ARRAY_]DOUBLE and XT_[ARRAY_]STR are supported! All other types will be stored as SEXP of the length 0 without any contents. @param buf buffer to store the REXP binary into @param off offset of the first byte where to store the REXP @return the offset of the first byte behind the stored REXP */ public int getBinaryRepresentation(byte[] buf, int off) { int myl=getBinaryLength(); boolean isLarge=(myl>0xfffff0); Rtalk.setHdr(Xt,myl-(isLarge?8:4),buf,off); off+=(isLarge?8:4); switch (Xt) { case XT_INT: Rtalk.setInt(asInt(),buf,off); break; case XT_DOUBLE: Rtalk.setLong(Double.doubleToLongBits(asDouble()),buf,off); break; case XT_ARRAY_INT: if (cont!=null) { int ia[]=(int[])cont; int i=0, io=off; while(i"; } /** get content of the REXP as string (if it is one) @return string content or null if the REXP is no string */ public String asString() { return (Xt==XT_STR)?(String)cont:null; } /** get content of the REXP as int (if it is one) @return int content or 0 if the REXP is no integer */ public int asInt() { if (Xt==XT_ARRAY_INT) { int i[]=(int[])cont; if (i!=null && i.length>0) return i[0]; } return (Xt==XT_INT)?((Integer)cont).intValue():0; } /** get content of the REXP as double (if it is one) @return double content or 0.0 if the REXP is no double */ public double asDouble() { if (Xt==XT_ARRAY_DOUBLE) { double d[]=(double[])cont; if (d!=null && d.length>0) return d[0]; } return (Xt==XT_DOUBLE)?((Double)cont).doubleValue():0.0; } /** get content of the REXP as {@link Vector} (if it is one) @return Vector content or null if the REXP is no Vector */ public Vector asVector() { return (Xt==XT_VECTOR)?(Vector)cont:null; } /** get content of the REXP as {@link RFactor} (if it is one) @return {@link RFactor} content or null if the REXP is no factor */ public RFactor asFactor() { return (Xt==XT_FACTOR)?(RFactor)cont:null; } /** get content of the REXP as {@link RList} (if it is one) @return {@link RList} content or null if the REXP is no list */ public RList asList() { return (Xt==XT_LIST)?(RList)cont:null; } /** get content of the REXP as {@link RBool} (if it is one) @return {@link RBool} content or null if the REXP is no logical value */ public RBool asBool() { return (Xt==XT_BOOL)?(RBool)cont:null; } /** get content of the REXP as an array of doubles. Array of integers, single double and single integer are automatically converted into such an array if necessary. @return double[] content or null if the REXP is not a array of doubles or integers */ public double[] asDoubleArray() { if (Xt==XT_ARRAY_DOUBLE) return (double[])cont; if (Xt==XT_DOUBLE) { double[] d=new double[1]; d[0]=asDouble(); return d; } if (Xt==XT_INT) { double[] d=new double[1]; d[0]=((Integer)cont).doubleValue(); return d; } if (Xt==XT_ARRAY_INT) { int[] i=asIntArray(); if (i==null) return null; double[] d=new double[i.length]; int j=0; while (jNO automatic conversion is done if the content is not an array of the correct type, because there is no canonical representation of doubles as integers. A single integer is returned as an array of the length 1. @return double[] content or null if the REXP is not a array of integers */ public int[] asIntArray() { if (Xt==XT_ARRAY_INT) return (int[])cont; if (Xt==XT_INT) { int[] i=new int[1]; i[0]=asInt(); return i; } return null; } /** returns the content of the REXP as a matrix of doubles (2D-array: m[rows][cols]). This is the same form as used by popular math packages for Java, such as JAMA. This means that following leads to desired results:
Matrix m=new Matrix(c.eval("matrix(c(1,2,3,4,5,6),2,3)").asDoubleMatrix()); @return 2D array of doubles in the form double[rows][cols] or null if the contents is no 2-dimensional matrix of doubles */ public double[][] asDoubleMatrix() { if (Xt!=XT_ARRAY_DOUBLE || attr==null || attr.Xt!=XT_LIST) return null; REXP dim=attr.asList().getHead(); if (dim==null || dim.Xt!=XT_ARRAY_INT) return null; // we need dimension attr int[] ds=dim.asIntArray(); if (ds==null || ds.length!=2) return null; // matrix must be 2-dimensional int m=ds[0], n=ds[1]; double[][] r=new double[m][n]; double[] ct=asDoubleArray(); if (ct==null) return null; // R stores matrices as matrix(c(1,2,3,4),2,2) = col1:(1,2), col2:(3,4) // we need to copy everything, since we create 2d array from 1d array int i=0,k=0; while (i "); sb.append(l.body); }; if (Xt==XT_UNKNOWN) sb.append((Integer)cont); sb.append("]"); return sb.toString(); }; }