]> gerrit.simantics Code Review - simantics/r.git/blob - org.simantics.r.scl/src/org/rosuda/REngine/REXP.java
78b4c9c35655fdf592e315bd9c2861bc984fc0bc
[simantics/r.git] / org.simantics.r.scl / src / org / rosuda / REngine / REXP.java
1 package org.rosuda.REngine;
2
3 /** Basic class representing an object of any type in R. Each type in R in represented by a specific subclass.
4  <p>
5  This class defines basic accessor methods (<tt>as</tt><i>XXX</i>), type check methods (<tt>is</tt><i>XXX</i>), gives access to attributes ({@link #getAttribute}, {@link #hasAttribute}) as well as several convenience methods. If a given method is not applicable to a particular type, it will throw the {@link REXPMismatchException} exception.
6  <p>This root class will throw on any accessor call and returns <code>false</code> for all type methods. This allows subclasses to override accessor and type methods selectively.
7  */
8 public class REXP {
9         /** attribute list. This attribute should never be accessed directly. */
10         public REXPList attr;
11
12         /** public root contrsuctor, same as <tt>new REXP(null)</tt> */
13         public REXP() { }
14         /** public root constructor
15          @param attr attribute list object (can be <code>null</code> */
16         public REXP(REXPList attr) { this.attr=attr; }
17
18         // type checks
19         /** check whether the <code>REXP</code> object is a character vector (string)
20          @return <code>true</code> if the receiver is a character vector, <code>false</code> otherwise */
21         public boolean isString() { return false; }
22         /** check whether the <code>REXP</code> object is a numeric vector
23          @return <code>true</code> if the receiver is a numeric vector, <code>false</code> otherwise */
24         public boolean isNumeric() { return false; }
25         /** check whether the <code>REXP</code> object is an integer vector
26          @return <code>true</code> if the receiver is an integer vector, <code>false</code> otherwise */
27         public boolean isInteger() { return false; }
28         /** check whether the <code>REXP</code> object is NULL
29          @return <code>true</code> if the receiver is NULL, <code>false</code> otherwise */
30         public boolean isNull() { return false; }
31         /** check whether the <code>REXP</code> object is a factor
32          @return <code>true</code> if the receiver is a factor, <code>false</code> otherwise */
33         public boolean isFactor() { return false; }
34         /** check whether the <code>REXP</code> object is a list (either generic vector or a pairlist - i.e. {@link #asList()} will succeed)
35          @return <code>true</code> if the receiver is a generic vector or a pair-list, <code>false</code> otherwise */
36         public boolean isList() { return false; }
37         /** check whether the <code>REXP</code> object is a pair-list
38          @return <code>true</code> if the receiver is a pair-list, <code>false</code> otherwise */
39         public boolean isPairList() { return false; }
40         /** check whether the <code>REXP</code> object is a logical vector
41          @return <code>true</code> if the receiver is a logical vector, <code>false</code> otherwise */
42         public boolean isLogical() { return false; }
43         /** check whether the <code>REXP</code> object is an environment
44          @return <code>true</code> if the receiver is an environment, <code>false</code> otherwise */
45         public boolean isEnvironment() { return false; }
46         /** check whether the <code>REXP</code> object is a language object
47          @return <code>true</code> if the receiver is a language object, <code>false</code> otherwise */
48         public boolean isLanguage() { return false; }
49         /** check whether the <code>REXP</code> object is an expression vector
50          @return <code>true</code> if the receiver is an expression vector, <code>false</code> otherwise */
51         public boolean isExpression() { return false; }
52         /** check whether the <code>REXP</code> object is a symbol
53          @return <code>true</code> if the receiver is a symbol, <code>false</code> otherwise */
54         public boolean isSymbol() { return false; }
55         /** check whether the <code>REXP</code> object is a vector
56          @return <code>true</code> if the receiver is a vector, <code>false</code> otherwise */
57         public boolean isVector() { return false; }
58         /** check whether the <code>REXP</code> object is a raw vector
59          @return <code>true</code> if the receiver is a raw vector, <code>false</code> otherwise */
60         public boolean isRaw() { return false; }
61         /** check whether the <code>REXP</code> object is a complex vector
62          @return <code>true</code> if the receiver is a complex vector, <code>false</code> otherwise */
63         public boolean isComplex() { return false; }
64         /** check whether the <code>REXP</code> object is a recursive obejct
65          @return <code>true</code> if the receiver is a recursive object, <code>false</code> otherwise */
66         public boolean isRecursive() { return false; }
67         /** check whether the <code>REXP</code> object is a reference to an R object
68          @return <code>true</code> if the receiver is a reference, <code>false</code> otherwise */
69         public boolean isReference() { return false; }
70
71         // basic accessor methods
72         /** returns the contents as an array of Strings (if supported by the represented object) */
73         public String[] asStrings() throws REXPMismatchException { throw new REXPMismatchException(this, "String"); }
74         /** returns the contents as an array of integers (if supported by the represented object) */
75         public int[] asIntegers() throws REXPMismatchException { throw new REXPMismatchException(this, "int"); }
76         /** returns the contents as an array of doubles (if supported by the represented object) */
77         public double[] asDoubles() throws REXPMismatchException { throw new REXPMismatchException(this, "double"); }
78         /** returns the contents as an array of bytes (if supported by the represented object) */
79         public byte[] asBytes() throws REXPMismatchException { throw new REXPMismatchException(this, "byte"); }
80         /** returns the contents as a (named) list (if supported by the represented object) */
81         public RList asList() throws REXPMismatchException { throw new REXPMismatchException(this, "list"); }
82         /** returns the contents as a factor (if supported by the represented object) */
83         public RFactor asFactor() throws REXPMismatchException { throw new REXPMismatchException(this, "factor"); }
84         /** attempt to represent the REXP by a native Java object and return it. Note that this may lead to loss of information (e.g., factors may be returned as a string array) and attributes are ignored. Not all R types can be converted to native Java objects. Also note that R has no concept of scalars, so vectors of length 1 will always be returned as an arrays (i.e., <code>int[1]</code> and not <code>Integer</code>). */
85         public Object asNativeJavaObject() throws REXPMismatchException { throw new REXPMismatchException(this, "native Java Object"); }
86
87         /** returns the length of a vector object. Note that we use R semantics here, i.e. a matrix will have a length of <i>m * n</i> since it is represented by a single vector (see {@link #dim} for retrieving matrix and multidimentional-array dimensions).
88          * @return length (number of elements) in a vector object
89          * @throws REXPMismatchException if this is not a vector object */
90         public int length() throws REXPMismatchException { throw new REXPMismatchException(this, "vector"); }
91
92         /** returns a boolean vector of the same length as this vector with <code>true</code> for NA values and <code>false</code> for any other values
93          *  @return a boolean vector of the same length as this vector with <code>true</code> for NA values and <code>false</code> for any other values
94          * @throws REXPMismatchException if this is not a vector object */
95         public boolean[] isNA() throws REXPMismatchException { throw new REXPMismatchException(this, "vector"); }
96         
97         // convenience accessor methods
98         /** convenience method corresponding to <code>asIntegers()[0]</code>
99          @return first entry returned by {@link #asInteger} */
100         public int asInteger() throws REXPMismatchException { int[] i = asIntegers(); return i[0]; }
101         /** convenience method corresponding to <code>asDoubles()[0]</code>
102          @return first entry returned by {@link #asDoubles} */
103         public double asDouble() throws REXPMismatchException { double[] d = asDoubles(); return d[0]; }
104         /** convenience method corresponding to <code>asStrings()[0]</code>
105          @return first entry returned by {@link #asStrings} */
106         public String asString() throws REXPMismatchException { String[] s = asStrings(); return s[0]; }
107
108         // methods common to all REXPs
109         
110         /** retrieve an attribute of the given name from this object
111          * @param name attribute name
112          * @return attribute value or <code>null</code> if the attribute does not exist */
113         public REXP getAttribute(String name) {
114                 final REXPList a = _attr();
115                 if (a==null || !a.isList()) return null;
116                 return a.asList().at(name);
117         }
118         
119         /** checks whether this obejct has a given attribute
120          * @param name attribute name
121          * @return <code>true</code> if the attribute exists, <code>false</code> otherwise */
122         public boolean hasAttribute(String name) {
123                 final REXPList a = _attr();
124                 return (a!=null && a.isList() && a.asList().at(name)!=null);
125         }
126         
127         
128         // helper methods common to all REXPs
129         
130         /** returns dimensions of the object (as determined by the "<code>dim</code>" attribute)
131          * @return an array of integers with corresponding dimensions or <code>null</code> if the object has no dimension attribute */
132         public int[] dim() {
133                 try {
134                         return hasAttribute("dim")?_attr().asList().at("dim").asIntegers():null;
135                 } catch (REXPMismatchException me) {
136                 }
137                 return null;
138         }
139         
140         /** determines whether this object inherits from a given class in the same fashion as the <code>inherits()</code> function in R does (i.e. ignoring S4 inheritance)
141          * @param klass class name
142          * @return <code>true</code> if this object is of the class <code>klass</code>, <code>false</code> otherwise */
143         public boolean inherits(String klass) {
144                 if (!hasAttribute("class")) return false;
145                 try {
146                         String c[] = getAttribute("class").asStrings();
147                         if (c != null) {
148                                 int i = 0;
149                                 while (i < c.length) {
150                                         if (c[i]!=null && c[i].equals(klass)) return true;
151                                         i++;
152                                 }
153                         }
154                 } catch (REXPMismatchException me) {
155                 }
156                 return false;
157         }
158
159         /** this method allows a limited access to object's attributes - <b>{@link #getAttribute} should be used instead to access specific attributes</b>!. Note that the {@link #attr} attribute should never be used directly incase the REXP implements a lazy access (e.g. via a reference)
160             @return list of attributes or <code>null</code> if the object has no attributes
161          */
162         public REXPList _attr() { return attr; }
163         
164         /** returns a string description of the object
165          @return string describing the object - it can be of an arbitrary form and used only for debugging (do not confuse with {@link #asString()} for accessing string REXPs) */
166         public String toString() {
167                 return super.toString()+((attr!=null)?"+":"");
168         }
169         
170         /** returns representation that it useful for debugging (e.g. it includes attributes and may include vector values -- see {@link #maxDebugItems})
171          @return extended description of the obejct -- it may include vector values
172          */
173         public String toDebugString() {
174                 return (attr!=null)?(("<"+attr.toDebugString()+">")+super.toString()):super.toString();
175         }
176         
177         //======= complex convenience methods
178         /** 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:<br>
179          <code>Matrix m=new Matrix(c.eval("matrix(c(1,2,3,4,5,6),2,3)").asDoubleMatrix());</code><br>
180          @return 2D array of doubles in the form double[rows][cols] or <code>null</code> if the contents is no 2-dimensional matrix of doubles */
181         public double[][] asDoubleMatrix() throws REXPMismatchException {
182                 double[] ct = asDoubles();
183                 REXP dim = getAttribute("dim");
184                 if (dim == null) throw new REXPMismatchException(this, "matrix (dim attribute missing)");
185                 int[] ds = dim.asIntegers();
186                 if (ds.length != 2) throw new REXPMismatchException(this, "matrix (wrong dimensionality)");
187                 int m = ds[0], n = ds[1];
188                 
189                 double[][] r = new double[m][n];
190                 // R stores matrices as matrix(c(1,2,3,4),2,2) = col1:(1,2), col2:(3,4)
191                 // we need to copy everything, since we create 2d array from 1d array
192                 int k = 0;
193                 for (int i = 0; i < n; i++)
194                         for (int j = 0; j < m; j++) 
195                                 r[j][i] = ct[k++];
196                 return r;
197         }
198         
199         /** creates a REXP that represents a double matrix in R based on matrix of doubles (2D-array: m[rows][cols]). This is the same form as used by popular math packages for Java, such as JAMA. The result of this function can be used in {@link REngine.assign} to store a matrix in R.
200          @param matrix array <code>double[rows][colums]</code> containing the matrix to convert into a REXP. If <code>matrix</code> is <code>null</code> or either of the dimensions is 0 then the resulting matrix will have the dimensions <code>0 x 0</code> (Note: Java cannot represent <code>0 x n</code> matrices for <code>n &gt; 0</code>, so special matrices with one dimension of 0 can only be created by setting dimensions directly).
201          @return <code>REXPDouble</code> with "dim" attribute which constitutes a matrix in R */
202         public static REXP createDoubleMatrix(double[][] matrix) {
203                 int m = 0, n = 0;
204                 double a[];
205                 if (matrix != null && matrix.length != 0 && matrix[0].length != 0) {
206                         m = matrix.length;
207                         n = matrix[0].length;
208                         a = new double[m * n];
209                         int k = 0;
210                         for (int j = 0; j < n; j++)
211                                 for (int i = 0; i < m; i++)
212                                         a[k++] = matrix[i][j];
213                 } else a = new double[0];
214                 return new REXPDouble(a,
215                                       new REXPList(
216                                                    new RList(
217                                                              new REXP[] { new REXPInteger(new int[] { m, n }) },
218                                                              new String[] { "dim" })
219                                                    )
220                                       );
221         }
222         
223         //======= tools
224         /** creates a data frame object from a list object using integer row names
225          *  @param l a (named) list of vectors ({@link REXPVector} subclasses), each element corresponds to a column and all elements must have the same length
226          *  @return a data frame object
227          *  @throws REXPMismatchException if the list is empty or any of the elements is not a vector */
228         public static REXP createDataFrame(RList l) throws REXPMismatchException {
229                 if (l == null || l.size() < 1) throw new REXPMismatchException(new REXPList(l), "data frame (must have dim>0)");
230                 if (!(l.at(0) instanceof REXPVector)) throw new REXPMismatchException(new REXPList(l), "data frame (contents must be vectors)");
231                 REXPVector fe = (REXPVector) l.at(0);
232                 return
233                 new REXPGenericVector(l,
234                                                           new REXPList(
235                                                                         new RList(
236                                                                                    new REXP[] {
237                                                                                            new REXPString("data.frame"),
238                                                                                            new REXPString(l.keys()),
239                                                                                            new REXPInteger(new int[] { REXPInteger.NA, -fe.length() })
240                                                                                    },
241                                                                                    new String[] {
242                                                                                            "class",
243                                                                                            "names",
244                                                                                            "row.names"
245                                                                                    })));
246         }
247         
248         /** specifies how many items of a vector or list will be displayed in {@link #toDebugString} */
249         public static int maxDebugItems = 32;
250 }