1 package org.rosuda.REngine;
3 /** Basic class representing an object of any type in R. Each type in R in represented by a specific subclass.
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.
9 /** attribute list. This attribute should never be accessed directly. */
12 /** public root contrsuctor, same as <tt>new REXP(null)</tt> */
14 /** public root constructor
15 @param attr attribute list object (can be <code>null</code> */
16 public REXP(REXPList attr) { this.attr=attr; }
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; }
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"); }
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"); }
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"); }
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]; }
108 // methods common to all REXPs
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);
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);
128 // helper methods common to all REXPs
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 */
134 return hasAttribute("dim")?_attr().asList().at("dim").asIntegers():null;
135 } catch (REXPMismatchException me) {
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;
146 String c[] = getAttribute("class").asStrings();
149 while (i < c.length) {
150 if (c[i]!=null && c[i].equals(klass)) return true;
154 } catch (REXPMismatchException me) {
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
162 public REXPList _attr() { return attr; }
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)?"+":"");
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
173 public String toDebugString() {
174 return (attr!=null)?(("<"+attr.toDebugString()+">")+super.toString()):super.toString();
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];
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
193 for (int i = 0; i < n; i++)
194 for (int j = 0; j < m; j++)
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 > 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) {
205 if (matrix != null && matrix.length != 0 && matrix[0].length != 0) {
207 n = matrix[0].length;
208 a = new double[m * n];
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,
217 new REXP[] { new REXPInteger(new int[] { m, n }) },
218 new String[] { "dim" })
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);
233 new REXPGenericVector(l,
237 new REXPString("data.frame"),
238 new REXPString(l.keys()),
239 new REXPInteger(new int[] { REXPInteger.NA, -fe.length() })
248 /** specifies how many items of a vector or list will be displayed in {@link #toDebugString} */
249 public static int maxDebugItems = 32;