/* * DDataSet.java * * Created on April 24, 2007, 11:08 PM * * To change this template, choose Tools | Template Manager * and open the template in the editor. */ package org.autoplot.cdf; import java.util.HashMap; import java.util.Map; import org.das2.qds.AbstractDataSet; import org.das2.qds.DataSetUtil; import org.das2.qds.QDataSet; import org.das2.qds.RankZeroDataSet; import org.das2.qds.Slice0DataSet; import org.das2.qds.WritableDataSet; /** * hacked DDataSet implementation does transpose for column major files. * rank 1,2,or 3 dataset backed by double array. Note this is not * simply a transpose of DDataSet, as the name implies. The zeroth index is * the same, and the remaining index are reversed. * * Mutable datasets warning: No dataset should be mutable once it is accessible to the * rest of the system. This would require clients make defensive copies which would * seriously degrade performance. * * @author jbf */ public final class TrFDataSet extends TrArrayDataSet implements WritableDataSet, RankZeroDataSet { float[] back; int rank; int len0; int len1; int len2; int len3; private static final boolean RANGE_CHECK = false; public static final String version = "20090605"; public static TrFDataSet createRank1(int len0) { return new TrFDataSet(1, len0, 1, 1, 1); } public static TrFDataSet createRank2(int len0, int len1) { return new TrFDataSet(2, len0, len1, 1, 1); } public static TrFDataSet createRank3(int len0, int len1, int len2) { return new TrFDataSet(3, len0, len1, len2, 1); } public static TrFDataSet createRank4(int len0, int len1, int len2, int len3) { return new TrFDataSet(4, len0, len1, len2, len3); } /** * Makes an array from array of dimension sizes. The result will have * rank qube.length(). * @param qube array specifying the rank and size of each dimension * @return DDataSet */ public static TrFDataSet create(int[] qube) { if ( qube.length==0 ) { return new TrFDataSet( 0, 1, 1, 1, 1 ); } else if (qube.length == 1) { return TrFDataSet.createRank1(qube[0]); } else if (qube.length == 2) { return TrFDataSet.createRank2(qube[0], qube[1]); } else if (qube.length == 3) { return TrFDataSet.createRank3(qube[0], qube[1], qube[2]); } else if (qube.length == 4) { return TrFDataSet.createRank4(qube[0], qube[1], qube[2], qube[3]); } else { throw new IllegalArgumentException("bad qube"); } } /** * Wraps an array from array of dimension sizes. The result will have * rank qube.length(). * @param data array containing the data, with the last dimension contiguous in memory. * @param qube array specifying the rank and size of each dimension * @return DDataSet */ public static TrFDataSet wrap( float[] data, int[] qube ) { if (qube.length == 0 ) { return new TrFDataSet( 1, 1, 1, 1, 1, data ); } else if (qube.length == 1) { return new TrFDataSet( 1, qube[0], 1, 1, 1, data ); } else if (qube.length == 2) { return new TrFDataSet( 2, qube[0], qube[1], 1, 1, data ); } else if (qube.length == 3) { return new TrFDataSet( 3, qube[0], qube[1], qube[2], 1, data ); } else if (qube.length == 4) { return new TrFDataSet( 4, qube[0], qube[1], qube[2], qube[3], data); } else { throw new IllegalArgumentException("bad qube"); } } /** Creates a new instance of DDataSet */ private TrFDataSet(int rank, int len0, int len1, int len2, int len3) { this(rank, len0, len1, len2, len3, new float[len0 * len1 * len2 * len3]); } private TrFDataSet(int rank, int len0, int len1, int len2, int len3, float[] back) { if ( back==null ) throw new NullPointerException("back was null"); this.back = back; this.rank = rank; this.len0 = len0; this.len1 = len1; this.len2 = len2; this.len3 = len3; DataSetUtil.addQube(this); } protected Object getBack() { return this.back; } public int rank() { return rank; } @Override public int length() { return len0; } @Override public int length(int i) { return len1; } @Override public int length(int i0, int i1) { return len2; } @Override public int length(int i0, int i1, int i2) { return len3; } @Override public double value() { float v= back[0]; return v==fill ? dfill : v; } @Override public double value(int i0) { if (RANGE_CHECK) { if (i0 < 0 || i0 >= len0) { throw new IndexOutOfBoundsException("i0=" + i0 + " " + this); } } float v= back[i0]; return v==fill ? dfill : v; } @Override public double value(int i0, int i1) { if (RANGE_CHECK) { if (i0 < 0 || i0 >= len0) { throw new IndexOutOfBoundsException("i0=" + i0 + " " + this); } if (i1 < 0 || i1 >= len1) { throw new IndexOutOfBoundsException("i1=" + i1 + " " + this); } } float v= back[i0 * len1 + i1]; return v==fill ? dfill : v; } @Override public double value(int i0, int i1, int i2) { if (RANGE_CHECK) { if (i0 < 0 || i0 >= len0) { throw new IndexOutOfBoundsException("i0=" + i0 + " " + this); } if (i1 < 0 || i1 >= len1) { throw new IndexOutOfBoundsException("i1=" + i1 + " " + this); } if (i2 < 0 || i2 >= len2) { throw new IndexOutOfBoundsException("i2=" + i2 + " " + this); } } float v= back[i0 * len1 * len2 + i2 * len1 + i1]; return v==fill ? dfill : v; } @Override public double value(int i0, int i1, int i2, int i3) { if (RANGE_CHECK) { if (i0 < 0 || i0 >= len0) { throw new IndexOutOfBoundsException("i0=" + i0 + " " + this); } if (i1 < 0 || i1 >= len1) { throw new IndexOutOfBoundsException("i1=" + i1 + " " + this); } if (i2 < 0 || i2 >= len2) { throw new IndexOutOfBoundsException("i2=" + i2 + " " + this); } if (i3 < 0 || i3 >= len3) { throw new IndexOutOfBoundsException("i3=" + i3 + " " + this); } } float v= back[i0*len1*len2*len3 + i3*len2*len1 + i2*len1 +i1]; return v==fill ? dfill : v; } public void putValue(double value) { back[0]= (float)value; } public void putValue(int i0, double value) { if (RANGE_CHECK) { if (i0 < 0 || i0 >= len0) { throw new IndexOutOfBoundsException("i0=" + i0 + " " + this); } } back[i0] = (float)value; } public void putValue(int i0, int i1, double value) { if (RANGE_CHECK) { if (i0 < 0 || i0 >= len0) { throw new IndexOutOfBoundsException("i0=" + i0 + " " + this); } if (i1 < 0 || i1 >= len1) { throw new IndexOutOfBoundsException("i1=" + i1 + " " + this); } } back[i0 * len1 + i1] = (float)value; } public void putValue(int i0, int i1, int i2, double value) { if (RANGE_CHECK) { if (i0 < 0 || i0 >= len0) { throw new IndexOutOfBoundsException("i0=" + i0 + " " + this); } if (i1 < 0 || i1 >= len1) { throw new IndexOutOfBoundsException("i1=" + i1 + " " + this); } if (i2 < 0 || i2 >= len2) { throw new IndexOutOfBoundsException("i2=" + i2 + " " + this); } } back[i0 * len1 * len2 + i2 * len1 + i1] = (float)value; } public void putValue(int i0, int i1, int i2, int i3, double value) { if (RANGE_CHECK) { if (i0 < 0 || i0 >= len0) { throw new IndexOutOfBoundsException("i0=" + i0 + " " + this); } if (i1 < 0 || i1 >= len1) { throw new IndexOutOfBoundsException("i1=" + i1 + " " + this); } if (i2 < 0 || i2 >= len2) { throw new IndexOutOfBoundsException("i2=" + i2 + " " + this); } if (i3 < 0 || i3 >= len3) { throw new IndexOutOfBoundsException("i3=" + i3 + " " + this); } } back[i0 *len1 *len2 *len3 + i3 *len1 *len2 + i2*len1 +i1]= (float)value; } /** * Shorten the dataset by changing it's dim 0 length parameter. The same backing array is used, * so the element that remain ill be the same. * can only shorten! */ public void putLength(int len) { if (len > len0) { throw new IllegalArgumentException("dataset cannot be lengthened"); } len0 = len; } @Override public String toString() { return DataSetUtil.toString(this); } @Override public void putProperty(String name, Object value) { super.putProperty(name, value); if ( name.equals(QDataSet.FILL_VALUE) ) checkFill(); // because of rounding errors } /** * copies the properties, copying depend datasets as well. * @see DataSetUtil.copyProperties, which is a shallow copy. */ protected static Map copyProperties(QDataSet ds) { Map result = new HashMap(); Map srcProps= DataSetUtil.getProperties(ds); result.putAll(srcProps); for ( int i=0; i < ds.rank(); i++) { QDataSet dep = (QDataSet) ds.property("DEPEND_" + i); if (dep == ds) { throw new IllegalArgumentException("dataset is dependent on itsself!"); } if (dep != null) { result.put("DEPEND_" + i, copy(dep)); } } for (int i = 0; i < QDataSet.MAX_PLANE_COUNT; i++) { QDataSet plane0 = (QDataSet) ds.property("PLANE_" + i); if (plane0 != null) { result.put("PLANE_" + i, copy(plane0)); } else { break; } } return result; } private static TrFDataSet ddcopy(TrFDataSet ds) { int dsLength = ds.len0 * ds.len1 * ds.len2 * ds.len3; float[] newback = new float[dsLength]; System.arraycopy(ds.back, 0, newback, 0, dsLength); TrFDataSet result = new TrFDataSet(ds.rank, ds.len0, ds.len1, ds.len2, ds.len3, newback); result.properties.putAll(copyProperties(ds)); // TODO: problems... if ( result.properties.containsKey(QDataSet.FILL_VALUE) ) { result.checkFill(); } return result; } /** * Copy the dataset to a DDataSet only if the dataset is not already a DDataSet. * @param ds * @return */ public static TrFDataSet maybeCopy( QDataSet ds ) { if ( ds instanceof TrFDataSet ) { return (TrFDataSet)ds; } else { return copy(ds); } } /** * copies the dataset into a writeable dataset, and all of its depend datasets as well. * An optimized copy is used when the argument is a DDataSet. */ public static TrFDataSet copy(QDataSet ds) { if (ds instanceof TrFDataSet) { return ddcopy((TrFDataSet) ds); } int rank = ds.rank(); TrFDataSet result; int len1,len2,len3; if ( !DataSetUtil.isQube(ds) ) { //throw new IllegalArgumentException("copy non-qube"); logger.fine("copy of non-qube to DDataSet, which must be qube"); } switch (rank) { case 0: result= createRank1(1); result.rank= 0; result.putValue(ds.value()); break; case 1: result = createRank1(ds.length()); for (int i = 0; i < ds.length(); i++) { result.putValue(i, ds.value(i)); } break; case 2: len1= ds.length() == 0 ? 0 : ds.length(0); result = createRank2(ds.length(), len1 ); for (int i = 0; i < ds.length(); i++) { for (int j = 0; j < ds.length(i); j++) { result.putValue(i, j, ds.value(i, j)); } } break; case 3: len1= ds.length() == 0 ? 0 : ds.length(0) ; len2= len1 == 0 ? 0 : ds.length(0,0); result = createRank3(ds.length(), len1, len2 ); for (int i = 0; i < ds.length(); i++) { for (int j = 0; j < ds.length(i); j++) { for (int k = 0; k < ds.length(i, j); k++) { result.putValue(i, j, k, ds.value(i, j, k)); } } } break; case 4: len1 = (ds.length()==0) ? 0 : ds.length(0); len2 = (len1==0) ? 0 : ds.length(0,0); len3 = (len2==0) ? 0 : ds.length(0,0,0); result = createRank4(ds.length(), len1, len2, len3); for (int i=0; i< ds.length(); i++) for (int j=0; j props= DataSetUtil.getProperties(this); Map depProps= DataSetUtil.trimProperties( this, start, end ); props.putAll(depProps); DataSetUtil.putProperties( props, result ); return result; } /** * TODO: this is untested, but is left in to demonstrate how the capability * method should be implemented. Clients should use this instead of * casting the class to the capability class. * @param * @param clazz * @return */ @Override public T capability(Class clazz) { if ( clazz==WritableDataSet.class ) { return (T) this; } else { return super.capability(clazz); } } }