Back to index

wims  3.65+svn20090927
DataSheet.java
Go to the documentation of this file.
00001 /*
00002     WIMSchem Elements: Chemistry molecular diagram drawing tool.
00003     
00004     (c) 2008 Dr. Alex M. Clark
00005     
00006     Released as GNUware, under the Gnu Public License (GPL)
00007     
00008     See www.gnu.org for details.
00009 */
00010 
00011 package WIMSchem.ds;
00012 
00013 import WIMSchem.*;
00014 
00015 import java.io.*;
00016 import java.util.*;
00017 
00018 /*
00019     DataSheet is a container class for storing a row/column format collection of molecular data. 
00020     This implementation stores the entirety of
00021     the collection in memory, and is not intended to be used for large datasets. 
00022     Its file format is expected to be an XML format, or 
00023     imported/exported from various others, such as SD files.
00024 */
00025 
00026 public class DataSheet
00027 {
00028     public static final int COLTYPE_MOLECULE=1;
00029     public static final int COLTYPE_STRING=2;
00030     public static final int COLTYPE_INTEGER=3;
00031     public static final int COLTYPE_REAL=4;
00032     public static final int COLTYPE_BOOLEAN=5;
00033     
00034     class Column
00035     {
00036        String Name;
00037        int Type;
00038        String Descr;
00039     }
00040     private ArrayList<Column> cols=new ArrayList<Column>();
00041     private ArrayList<Object[]> rows=new ArrayList<Object[]>();
00042 
00043     private boolean isDirty=false;
00044 
00045     public DataSheet()
00046     {
00047     }
00048     
00049     public int NumCols() {return cols.size();}
00050     public int NumRows() {return rows.size();}
00051     
00052     public static String TypeName(int Type) 
00053     {
00054        return Type==DataSheet.COLTYPE_MOLECULE ? "molecule" :
00055               Type==DataSheet.COLTYPE_STRING ? "string" :
00056               Type==DataSheet.COLTYPE_REAL ? "real" :
00057               Type==DataSheet.COLTYPE_INTEGER ? "integer" :
00058               Type==DataSheet.COLTYPE_BOOLEAN ? "boolean" : "?";
00059     }
00060     
00061     public boolean IsDirty() {return isDirty;}
00062     public void SetDirty() {isDirty=true;}
00063     public void ClearDirty() {isDirty=false;}
00064     
00065     // reading column info
00066     public String ColName(int N) {return cols.get(N).Name;}
00067     public int ColType(int N) {return cols.get(N).Type;}
00068     public String ColDescr(int N) {return cols.get(N).Descr;}
00069     
00070     // returns whether a cell is null; should always be checked for primitive types
00071     public boolean IsNull(int RN,int CN) {return (rows.get(RN))[CN]==null;}
00072 
00073     // fetching row data; note that the correct type must be use, else exception
00074     public Molecule GetMolecule(int RN,int CN) {return (Molecule)(rows.get(RN))[CN];}
00075     public String GetString(int RN,int CN) {return (String)(rows.get(RN))[CN];}
00076     public int GetInteger(int RN,int CN) {return ((Integer)(rows.get(RN))[CN]).intValue();}
00077     public double GetReal(int RN,int CN) {return ((Double)(rows.get(RN))[CN]).doubleValue();}
00078     public boolean GetBoolean(int RN,int CN) {return ((Boolean)(rows.get(RN))[CN]).booleanValue();}
00079 
00080     // gets the untyped object for a cell; use with care
00081     public Object GetObject(int RN,int CN) {return (rows.get(RN))[CN];}
00082 
00083     // sets a cell to null, which is valid for all types
00084     public void SetToNull(int RN,int CN) {(rows.get(RN))[CN]=null;}
00085 
00086     // setting row data; fails silently if the type is wrong
00087     public void SetMolecule(int RN,int CN,Molecule V) {if (ColType(CN)==COLTYPE_MOLECULE) (rows.get(RN))[CN]=V==null ? V : V.Clone();}
00088     public void SetString(int RN,int CN,String V) {if (ColType(CN)==COLTYPE_STRING) (rows.get(RN))[CN]=V;}
00089     public void SetInteger(int RN,int CN,int V) {if (ColType(CN)==COLTYPE_INTEGER) (rows.get(RN))[CN]=new Integer(V);}
00090     public void SetReal(int RN,int CN,double V) {if (ColType(CN)==COLTYPE_REAL) (rows.get(RN))[CN]=new Double(V);}
00091     public void SetBoolean(int RN,int CN,boolean V) {if (ColType(CN)==COLTYPE_BOOLEAN) (rows.get(RN))[CN]=new Boolean(V);}
00092 
00093     // sets the object for a cell, without any type checking; use with care
00094     public void SetObject(int RN,int CN,Object V) {(rows.get(RN))[CN]=V;}
00095     
00096     // appends a new column to the end of the list, and updates the underlying data accordingly
00097     public int AppendColumn(String Name,int Type,String Descr)
00098     {
00099        Column c=new Column();
00100        c.Name=Name;
00101        c.Type=Type;
00102        c.Descr=Descr;
00103        cols.add(c);
00104        for (int n=0;n<rows.size();n++)
00105        {
00106            Object[] d1=rows.get(n);
00107            Object[] d2=new Object[d1.length+1];
00108            for (int i=0;i<d1.length;i++) d2[i]=d1[i];
00109            d2[d1.length]=null;
00110            rows.set(n,d2);
00111        }
00112        return cols.size()-1;
00113     }
00114     
00115     // appends a row containing all-nulls to the end of the list, and returns the new index position
00116     public int AppendRow()
00117     {
00118        rows.add(new Object[cols.size()]);
00119        return rows.size()-1;
00120     }
00121     
00122     public void DeleteRow(int RN) {rows.remove(RN);}
00123     
00124     // removes a column, and adjusts all the data accordingly
00125     public void DeleteColumn(int CN) 
00126     {
00127        cols.remove(CN);
00128        for (int n=0;n<rows.size();n++)
00129        {
00130            Object[] prev=rows.get(n),cur=new Object[cols.size()-1];
00131            for (int i=0,j=0;i<prev.length;i++) if (i!=CN) cur[j++]=prev[i];
00132            rows.set(n,cur);
00133        }
00134     }
00135     
00136     // modifies name and/or description (null=do nothing)
00137     public void ChangeColumnName(int CN,String Name,String Descr)
00138     {
00139        Column c=cols.get(CN);
00140        if (Name!=null) c.Name=Name;
00141        if (Descr!=null) c.Descr=Descr;
00142     }
00143     
00144     // dynamically modifies the column type, correcting the existing data and reformulating; returns true if the conversion was
00145     // successful (for example, can't switch between molecule & other); if Force is set, then incompatible conversions will result
00146     // in null, otherwise the operation will fail
00147     public boolean ChangeColumnType(int CN,int NewType,boolean Force)
00148     {
00149        if (CN<0 || CN>=NumCols()) return false;
00150        if (ColType(CN)==NewType) return true;
00151        
00152        boolean incompatible=ColType(CN)==COLTYPE_MOLECULE || NewType==COLTYPE_MOLECULE;
00153        if (incompatible && !Force) return false;
00154        
00155        Column col=cols.get(CN);
00156        int prevType=col.Type;
00157        col.Type=NewType;
00158        
00159        for (int n=0,nrows=rows.size();n<nrows;n++)
00160        {
00161            Object[] row=rows.get(n);
00162            
00163            if (row[CN]==null) continue;
00164            if (incompatible) {row[CN]=null; continue;}
00165            
00166            String val="";
00167            if (prevType==COLTYPE_STRING) val=(String)row[CN];
00168            else if (prevType==COLTYPE_INTEGER) val=String.valueOf(((Integer)row[CN]).intValue());
00169            else if (prevType==COLTYPE_REAL) val=String.valueOf(((Double)row[CN]).intValue());
00170            else if (prevType==COLTYPE_BOOLEAN) val=((Boolean)row[CN]).booleanValue() ? "true" : "false";
00171            
00172            row[CN]=null;
00173            
00174            try
00175            {
00176               if (NewType==COLTYPE_STRING) row[CN]=val;
00177               else if (NewType==COLTYPE_INTEGER) row[CN]=new Integer(val);
00178               else if (NewType==COLTYPE_REAL) row[CN]=new Double(val);
00179               else if (NewType==COLTYPE_BOOLEAN) row[CN]=val.toLowerCase().compareTo("true")==0 ? Boolean.TRUE : Boolean.FALSE;
00180            }
00181            catch (NumberFormatException e) {} // stays null
00182        }
00183        
00184        return true;
00185     }
00186     
00187     // reorders the columns; each value of Order[n] defines the index into the original list which this should now be
00188     public void ReorderColumns(int[] Order)
00189     {
00190        boolean identity=true;
00191        for (int n=0;n<Order.length-1;n++) if (Order[n]!=Order[n+1]+1) {identity=false; break;}
00192        if (identity) return; // nothing to do
00193        
00194        ArrayList<Column> newcols=new ArrayList<Column>();
00195        for (int n=0;n<cols.size();n++) newcols.add(cols.get(Order[n]));
00196        cols=newcols;
00197        
00198        for (int n=0;n<rows.size();n++)
00199        {
00200            Object[] row=rows.get(n),newrow=new Object[row.length];
00201            for (int i=0;i<row.length;i++) newrow[i]=row[Order[i]];
00202            rows.set(n,newrow);
00203        }
00204     }
00205 }
00206 
00207