package org.rosuda.JRI; // JRclient library - client interface to Rserve, see http://www.rosuda.org/Rserve/ // Copyright (C) 2004 Simon Urbanek // --- for licensing information see LICENSE file in the original JRclient distribution --- import java.util.*; /** implementation of R-lists
This is rather preliminary and may change in future since it's not really proper. The point is that the parser tries to interpret lists to be of the form entry=value, where entry is stored in the "head" part, and value is stored in the "body" part. Then using {@link #at(String)} it is possible to fetch "body" for a specific "head". The terminology used is partly from hash fields - "keys" are the elements in "head" and values are in "body" (see {@link #keys}).

On the other hand, R uses lists to store complex internal structures, which are not parsed according to the structure - in that case "head" and "body" have to be evaluated separately according to their meaning in that context. @version $Id: RList.java 2720 2007-03-15 17:35:42Z urbanek $ */ public class RList extends Object { /** xpressions containing head, body and tag. The terminology is a bit misleading (for historical reasons) - head corresponds to CAR, body to CDR and finally tag is TAG. */ public REXP head, body, tag; /** cached keys (from TAG) */ String[] keys = null; /** cached values(from CAR) */ REXP[] values = null; /** flag denoting whether we need to re-fetch the cached values.

the current assumption is that the contents don't change after first retrieval - there is currently no recursive check! */ boolean dirtyCache = true; /** constructs an empty list */ public RList() { head=body=tag=null; } /** fake constructor to keep compatibility with Rserve (for now, will be gone soon) */ public RList(RVector v) { Vector n = v.getNames(); if (n != null) { keys = new String[n.size()]; n.copyInto(keys); } values=new REXP[v.size()]; v.copyInto(values); dirtyCache=false; // head,tail,tag are all invalid! } /** constructs an initialized list @param h head xpression @param b body xpression */ public RList(REXP h, REXP b) { head=h; body=b; tag=null; } /** constructs an initialized list @param h head xpression (CAR) @param t tag xpression (TAG) @param b body/tail xpression (CDR) */ public RList(REXP h, REXP t, REXP b) { head=h; body=b; tag=t; } /** get head xpression (CAR) @return head xpression */ public REXP getHead() { return head; } /** get body xpression (CDR) @return body xpression */ public REXP getBody() { return body; } /** get tag xpression @return tag xpression */ public REXP getTag() { return tag; } /** internal function that updates cached vectors @return true if the conversion was successful */ boolean updateVec() { if (!dirtyCache) return true; // we do NOT run it recursively, because in most cases only once instance is asked RList cur = this; int l = 0; while (cur!=null) { l++; REXP bd = cur.getBody(); cur = (bd==null)?null:bd.asList(); } keys=new String[l]; values=new REXP[l]; cur = this; l=0; while (cur != null) { REXP x = cur.getTag(); if (x!=null) keys[l]=x.asSymbolName(); values[l] = cur.getHead(); REXP bd = cur.getBody(); cur = (bd==null)?null:bd.asList(); l++; } dirtyCache=false; return true; } /** get xpression given a key @param v key @return xpression which corresponds to the given key or null if list is not standartized or key not found */ public REXP at(String v) { if (!updateVec() || keys==null || values==null) return null; int i=0; while (inull if list is not standartized or if index out of bounds */ public REXP at(int i) { return (!updateVec() || values==null || i<0 || i>=values.length)?null:values[i]; } /** returns all keys of the list @return array containing all keys or null if list is not standartized */ public String[] keys() { return (!updateVec())?null:this.keys; } }