package gov.nasa.gsfc.voyager.cdf;
import java.io.*;
import java.nio.*;
import java.util.*;
import java.util.zip.*;
import java.lang.reflect.*;
public class Extractor {
    static int MAX_ARRAY = 3;
    static String[] functions = new String[] {"Series" , "Element",
           "Point" , "Range" , "Elements", "RangeForElements",
           "RangeForElement", "TimeSeries", "SampledTimeSeries"};
    static Method[][][] methods =
        new Method[functions.length][MAX_ARRAY + 1][2];
    static Class[][][] args = new Class[functions.length][MAX_ARRAY + 1][];
    static {
        try {
            Class variableClass =
                Class.forName("gov.nasa.gsfc.voyager.cdf.Variable");
            Class cdfClass = Class.forName("gov.nasa.gsfc.voyager.cdf.CDF");
            int[] ia = new int[0];
            double[] da = new double[0];
            args[0][0] = new Class[] {cdfClass, variableClass};
            args[0][1] = args[0][0];
            args[0][2] = args[0][0];
            args[0][3] = args[0][0];
            args[1][0] = null;
            args[1][1] = new Class[] {cdfClass, variableClass, Integer.class};
            args[1][2] = new Class[] {cdfClass, variableClass, Integer.class,
                                      Integer.class};
            args[2][0] = new Class[] {cdfClass, variableClass, Integer.class};
            args[2][1] = args[2][0];
            args[2][2] = args[2][0];
            args[2][3] = args[2][0];

            args[3][0] = new Class[] {cdfClass, variableClass, Integer.class,
                                      Integer.class};
            args[3][1] = args[3][0];
            args[3][2] = args[3][0];
            args[4][0] = null;
            args[4][1] = new Class[] {cdfClass, variableClass, ia.getClass()};
            args[4][2] = null;
            args[5][0] = null;
            args[5][1] = new Class[] {cdfClass, variableClass, Integer.class,
                         Integer.class, ia.getClass()};
            args[5][2] = null;
            args[6][0] = null;
            args[6][1] = new Class[] {cdfClass, variableClass, Integer.class,
                         Integer.class, Integer.class};
            args[6][2] = null;
            args[7][0] = new Class[] {cdfClass, variableClass, Boolean.class,
                         da.getClass()};
            args[7][1] = new Class[] {cdfClass, variableClass, Integer.class,
                         Boolean.class, da.getClass()};
            args[7][2] = null;
            args[8][0] = new Class[] {cdfClass, variableClass, Boolean.class,
                         da.getClass(), ia.getClass()};
            args[8][1] = new Class[] {cdfClass, variableClass, Integer.class,
                         Boolean.class, da.getClass(), ia.getClass()};
            args[8][2] = null;

        } catch (ClassNotFoundException ex) {
        }
    }
    public Extractor() throws NoSuchMethodException {
    }
    static {
        for (int i = 0; i < functions.length; i++) {
            for (int j = 0; j <= MAX_ARRAY; j++) {
                for (int k = 0; k < 2; k++) {
                    methods[i][j][k] = null;
                }
            }
        }
        try {
            Class cl = Class.forName("gov.nasa.gsfc.voyager.cdf.Extractor");
            for (int i = 0; i < functions.length; i++) {
                for (int j = 0; j <= MAX_ARRAY; j++) {
                    if (args[i][j] == null) continue;
                    methods[i][j][0] = cl.getMethod("get" + functions[i] + j,
                                          args[i][j]);
                }
            }
            methods[0][0][1] = cl.getMethod("getStringSeries0", args[0][0]);
            methods[0][1][1] = cl.getMethod("getStringSeries1", args[0][0]);
        } catch (ClassNotFoundException ex) {
        } catch (NoSuchMethodException ex) {
        }
    }
    public static Method getMethod(Variable var, String func) throws 
        IllegalAccessException, InvocationTargetException {
        int rank = var.getEffectiveRank();
        int index = -1;
        for (int i = 0; i < functions.length; i++) {
            if (func.equals(functions[i])) {
                index = i;
                break;
            }
        }
        if (index < 0) return null;
        if (DataTypes.isStringType(var.getType())) {
            return methods[index][rank][1];
        }
        return methods[index][rank][0];
    }

    static int[] getRecordRange(CDF thisCDF,Variable var, double[] timeRange) {
        try {
            double[] times = ((CDFImpl)thisCDF).getTimes(var, false);
            double start = timeRange[0];
            double stop = timeRange[1];
            int i = 0;
            for (; i < times.length; i++) {
                if (start > times[i]) continue;
                break;
            }
            if (i == times.length) return null;
            int low = i;
            for (; i < times.length; i++) {
                if (stop <= times[i]) break;
            }
            if (i == 0) return null;
            return new int[] {low, i - 1};
        } catch (Throwable t) {
        }
        return null;
    }

    public static double [] getSeries0(CDF thisCDF, Variable var) throws 
        IllegalAccessException, InvocationTargetException {
        int numberOfValues = var.getNumberOfValues();
        if (numberOfValues == 0) return null;
        double [] data = new double[numberOfValues];
        int type = var.getType();
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        for (int blk = 0; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            int first = loc[0];
            int last = loc[1];
            ByteBuffer bv = positionBuffer((CDFImpl)thisCDF, var, loc[2],
                (last - first + 1));
            Method method;
            switch (DataTypes.typeCategory[type]) {
            case 0:
                FloatBuffer bvf = bv.asFloatBuffer();
                for (int n = first; n <= last; n++) {
                    data[n] = bvf.get();
                }
                break;
            case 1:
                DoubleBuffer bvd = bv.asDoubleBuffer();
                for (int n = first; n <= last; n++) {
                    data[n] = bvd.get();
                }
                break;
            case 2:
                method = DataTypes.method[type];
                for (int n = first; n <= last; n++) {
                    Number num = (Number)method.invoke(bv, new Object[] {});
                    data[n] = num.doubleValue();
                }
                break;
            case 3:
                method = DataTypes.method[type];
                for (int n = first; n <= last; n++) {
                    Number num = (Number)method.invoke(bv, new Object[] {});
                    int x = num.intValue();
                    data[n] = (x >= 0)?(double)x:(double)(longInt + x);
                }
            }

        }
        if (!var.recordVariance()) {
            for (int i = 1; i < numberOfValues; i++) {
                data[i] = data[0];
            }
        }
        return data;
    }

    public static double [][] getTimeSeries0(CDF thisCDF, Variable var,
        Boolean ignoreFill, double[] timeRange) throws Throwable {
        return getTimeSeries(thisCDF, var, null, ignoreFill, timeRange);
    }
    public static double [][] getTimeSeries1(CDF thisCDF, Variable var,
        Integer which, Boolean ignoreFill, double[] timeRange)
        throws Throwable {
        return getTimeSeries(thisCDF, var, which, ignoreFill, timeRange);
    }
    public static double [][] getTimeSeries(CDF thisCDF, Variable var,
        Integer which, Boolean ignoreFill, double[] timeRange) throws Throwable
        {
        if (var.getNumberOfValues() == 0) return null;
        boolean ignore = ignoreFill.booleanValue();
        double [] vdata;
        int [] recordRange = null;
        double [] times = ((CDFImpl)thisCDF).getTimes(var, false);
        if (times == null) return null;
        double[] stimes;
        if (timeRange == null) {
            vdata = (which == null)?getSeries0(thisCDF, var):
                                    getElement1(thisCDF, var, which);
        } else {
            recordRange = getRecordRange(thisCDF, var, timeRange); 
            if (recordRange == null) return null;
            if (which == null) {
                vdata = getRange0(thisCDF, var, new Integer(recordRange[0]),
                                  new Integer(recordRange[1]));
            } else {
                vdata = getRangeForElement1(thisCDF, var,
                    new Integer(recordRange[0]), new Integer(recordRange[1]),
                    which);
            }
        }
        if (!ignore) {
            if (timeRange == null) return new double [][] {times, vdata};
            stimes = new double[vdata.length];
            System.arraycopy(times, recordRange[0], stimes, 0, vdata.length);
            return new double [][] {stimes, vdata};
        }
        // fill values need to be filtered
        double [] fill = getFillValue(thisCDF, var);
        int first = (timeRange != null)?recordRange[0]:0;
        if (fill[0] != 0) { // there is no fill value
            stimes = new double[vdata.length];
            System.arraycopy(times, first, stimes, 0, vdata.length);
            return new double [][] {stimes, vdata};
        }
        return filterFill(times, vdata, fill[1], first);
    }

    public static double[] getFillValue(CDF thisCDF, Variable var) {
        Vector fill = (Vector)thisCDF.getAttribute(var.getName(), "FILLVAL");
        if (fill.size() != 0) {
            return new double[] {0, ((double [])fill.elementAt(0))[0]};
        } else {
            return new double[] {Double.NEGATIVE_INFINITY, 0};
        }
    }

    public static double [][] filterFill(double[] times, double [] vdata, 
        double fill, int first) {
        double [][] series;
        int count = 0;
        for (int i = 0; i < vdata.length; i++) {
            if (vdata[i] != fill) count++;
        }
        series = new double[2][count];
        int n = 0;
        for (int i = 0; i < vdata.length; i++) {
            if (vdata[i] == fill) continue;
            series[0][n] = times[i + first];
            series[1][n] = vdata[i];
            n++;
        }
        return series;
    }

    public static double [][] getSeries1(CDF thisCDF, Variable var) throws
        IllegalAccessException, InvocationTargetException {
        int nv = var.getNumberOfValues();
        if (nv == 0) return null;
        if (!var.recordVariance()) nv = 1;
        int elements = (((Integer)elementCount(var).elementAt(0))).intValue();
        double [][] data = new double[nv][elements];

        int type = var.getType();
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        for (int blk = 0; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            int first = loc[0];
            int last = loc[1];
            ByteBuffer bv = positionBuffer((CDFImpl)thisCDF, var, loc[2],
                (last - first + 1));
            Method method;
            switch (DataTypes.typeCategory[type]) {
            case 0:
                FloatBuffer bvf = bv.asFloatBuffer();
                for (int n = first; n <= last; n++) {
                    for (int m = 0; m < elements; m++) {
                        data[n][m] = bvf.get();
                    }
                }
                break;
            case 1:
                DoubleBuffer bvd = bv.asDoubleBuffer();
                for (int n = first; n <= last; n++) {
                    for (int m = 0; m < elements; m++) {
                        data[n][m] = bvd.get();
                    }
                }
                break;
            case 2:
                doSignedInteger(bv, type, first, last, elements, data);
                break;
            case 3:
                doUnsignedInteger(bv, type, first, last, elements, data);
                break;
            }

        }
        return data;
    }

    static void doSignedInteger(ByteBuffer bv, int type, int first, 
        int last, int count, double[][] data) throws
        IllegalAccessException, InvocationTargetException {
        Method method = DataTypes.method[type];
        for (int n = first; n <= last; n++) {
            for (int e = 0; e < count; e++) {
                Number num = (Number)method.invoke(bv, new Object[] {});
                data[n][e] = num.doubleValue();
            }
        }
    }

    static void doSignedInteger(ByteBuffer bv, int pos, int type,
        int size, int first, int last, double[] data) throws
        IllegalAccessException, InvocationTargetException {
        Method method = DataTypes.method[type];
        bv.position(pos);
        for (int n = first; n <= last; n++) {
            Number num = (Number)method.invoke(bv, new Object[] {});
            data[n] = num.doubleValue();
            pos += size;
            bv.position(pos);
        }
    }

    static int doSignedInteger(ByteBuffer bv, int pos, int type,
        int size, int first, int last, double[] data, int index) throws
        IllegalAccessException, InvocationTargetException {
        Method method = DataTypes.method[type];
        bv.position(pos);
        for (int n = first; n <= last; n++) {
            Number num = (Number)method.invoke(bv, new Object[] {});
            data[index++] = num.doubleValue();
            pos += size;
            bv.position(pos);
        }
        return index;
    }

    static void doSignedInteger(ByteBuffer bv, int pos, int type,
        int size, int first, int last, int[] offsets, double[][] data) throws
        IllegalAccessException, InvocationTargetException {
        Method method = DataTypes.method[type];
        bv.position(pos);
        int ne = offsets.length;
        for (int n = first; n <= last; n++) {
            for (int e = 0; e < ne; e++) {
                bv.position(pos + offsets[e]);
                Number num = (Number)method.invoke(bv, new Object[] {});
                data[n][e] = num.doubleValue();
            }
            pos += size;
        }
    }

    static int doSignedInteger(ByteBuffer bv, int pos, int type,
        int size, int first, int last, int[] offsets, double[][] data,
        int index) throws IllegalAccessException, InvocationTargetException {
        Method method = DataTypes.method[type];
        bv.position(pos);
        int ne = offsets.length;
        for (int n = first; n <= last; n++) {
            for (int e = 0; e < ne; e++) {
                bv.position(pos + offsets[e]);
                Number num = (Number)method.invoke(bv, new Object[] {});
                data[index][e] = num.doubleValue();
            }
            pos += size;
            index++;
        }
        return index;
    }

    static void doUnsignedInteger(ByteBuffer bv, int type, int first, 
        int last, int count, double[][] data) throws
        IllegalAccessException, InvocationTargetException {
        Method method = DataTypes.method[type];
        for (int n = first; n <= last; n++) {
            for (int e = 0; e < count; e++) {
                Number num = (Number)method.invoke(bv, new Object[] {});
                int x = num.intValue();
                data[n][e] = (x >= 0)?(double)x:(double)(longInt + x);
            }
        }
    }

    static void doUnsignedInteger(ByteBuffer bv, int pos, int type,
        int size, int first, int last, double[] data) throws
        IllegalAccessException, InvocationTargetException {
        Method method = DataTypes.method[type];
        bv.position(pos);
        for (int n = first; n <= last; n++) {
            Number num = (Number)method.invoke(bv, new Object[] {});
            int x = num.intValue();
            data[n] = (x >= 0)?(double)x:(double)(longInt + x);
            pos += size;
            bv.position(pos);
        }
    }

    static int doUnsignedInteger(ByteBuffer bv, int pos, int type,
        int size, int first, int last, double[] data, int index) throws
        IllegalAccessException, InvocationTargetException {
        Method method = DataTypes.method[type];
        bv.position(pos);
        for (int n = first; n <= last; n++) {
            Number num = (Number)method.invoke(bv, new Object[] {});
            int x = num.intValue();
            data[index++] = (x >= 0)?(double)x:(double)(longInt + x);
            pos += size;
            bv.position(pos);
        }
        return index;
    }

    static void doUnsignedInteger(ByteBuffer bv, int pos, int type,
        int size, int first, int last, int[] offsets, double[][] data) throws
        IllegalAccessException, InvocationTargetException {
        Method method = DataTypes.method[type];
        bv.position(pos);
        int ne = offsets.length;
        for (int n = first; n <= last; n++) {
            for (int e = 0; e < ne; e++) {
                bv.position(pos + offsets[e]);
                Number num = (Number)method.invoke(bv, new Object[] {});
                int x = num.intValue();
                data[n][e] = (x >= 0)?(double)x:(double)(longInt + x);
            }
            pos += size;
        }
    }

    static int doUnsignedInteger(ByteBuffer bv, int pos, int type,
        int size, int first, int last, int[] offsets, double[][] data,
        int index) throws IllegalAccessException, InvocationTargetException {
        Method method = DataTypes.method[type];
        bv.position(pos);
        int ne = offsets.length;
        for (int n = first; n <= last; n++) {
            for (int e = 0; e < ne; e++) {
                bv.position(pos + offsets[e]);
                Number num = (Number)method.invoke(bv, new Object[] {});
                int x = num.intValue();
                data[index][e] = (x >= 0)?(double)x:(double)(longInt + x);
            }
            pos += size;
            index++;
        }
        return index;
    }

    public static double [] getElement1(CDF thisCDF, Variable var, Integer idx) 
        throws Throwable {
        int element = idx.intValue();
        int nv = var.getNumberOfValues();
        if (nv == 0) return null;
        if (!var.recordVariance()) nv = 1;
        if (!validElement(var, new int[] {element})) return null;
        double [] data = new double[nv];
        int size = var.getDataItemSize();

        int type = var.getType();
        int loff = element*DataTypes.size[type];
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        for (int blk = 0; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            int first = loc[0];
            int last = loc[1];
            ByteBuffer bv = positionBuffer((CDFImpl)thisCDF, var, loc[2],
                (last - first + 1));
            Method method;
            int pos = bv.position() + loff;
            switch (DataTypes.typeCategory[type]) {
            case 0:
                for (int n = first; n <= last; n++) {
                    data[n] = bv.getFloat(pos);
                    pos += size;
                }
                break;
            case 1:
                for (int n = first; n <= last; n++) {
                    data[n] = bv.getDouble(pos);
                    pos += size;
                }
                break;
            case 2:
                doSignedInteger(bv, pos, type, size, first, last, data);
                break;
            case 3:
                doUnsignedInteger(bv, pos, type, size, first, last, data);
                break;
            }

        }
        return data;
    }

    public static double [][] getElements1(CDF thisCDF, Variable var,
        int[] idx) throws Throwable {
        int nv = var.getNumberOfValues();
        if (nv == 0) return null;
        if (!var.recordVariance()) {
            nv = 1;
        }
        if (!validElement(var, idx)) return null;
        int ne = idx.length;
        double [][] data = new double[nv][ne];
        int size = var.getDataItemSize();

        int type = var.getType();
        int[] loff = new int[ne];
        for (int i = 0; i < ne; i++) {
            loff[i] = idx[i]*DataTypes.size[type];
        }
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        for (int blk = 0; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            int first = loc[0];
            int last = loc[1];
            ByteBuffer bv = positionBuffer((CDFImpl)thisCDF, var, loc[2],
                (last - first + 1));
            Method method;
            int pos = bv.position();
            switch (DataTypes.typeCategory[type]) {
            case 0:
                for (int n = first; n <= last; n++) {
                    for (int e = 0; e < ne; e++) {
                        data[n][e] = bv.getFloat(pos + loff[e]);
                    }
                    pos += size;
                }
                break;
            case 1:
                for (int n = first; n <= last; n++) {
                    for (int e = 0; e < ne; e++) {
                        data[n][e] = bv.getDouble(pos + loff[e]);
                    }
                    pos += size;
                }
                break;
            case 2:
                doSignedInteger(bv, pos, type, size, first, last, loff, data);
                break;
            case 3:
                doUnsignedInteger(bv, pos, type, size, first, last, loff, data);
                break;
            }

        }
        return data;
    }

    public static double [][][] getSeries2(CDF thisCDF, Variable var) {
        int nv = var.getNumberOfValues();
        if (nv == 0) return null;
        if (!var.recordVariance()) nv = 1;
        int n0 = (((Integer)elementCount(var).elementAt(0))).intValue();
        int n1 = (((Integer)elementCount(var).elementAt(1))).intValue();
        double [][][] data = new double[nv][n0][n1];
        int type = var.getType();
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        for (int blk = 0; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            int first = loc[0];
            int last = loc[1];
            ByteBuffer bv = positionBuffer((CDFImpl)thisCDF, var, loc[2],
                (last - first + 1));
            switch (DataTypes.typeCategory[type]) {
            case 0:
                FloatBuffer bvf = bv.asFloatBuffer();
                if (var.rowMajority()) {
                    for (int n = first; n <= last; n++) {
                        for (int m = 0; m < n0; m++) {
                            for (int l = 0; l < n1; l++) {
                                data[n][m][l] = bvf.get();
                            }
                        }
                    }
                } else {
                    for (int n = first; n <= last; n++) {
                        for (int m = 0; m < n1; m++) {
                            for (int l = 0; l < n0; l++) {
                                data[n][l][m] = bvf.get();
                            }
                        }
                    }
                }
                break;
            case 1:
                DoubleBuffer bvd = bv.asDoubleBuffer();
                if (var.rowMajority()) {
                    for (int n = first; n <= last; n++) {
                        for (int m = 0; m < n0; m++) {
                            for (int l = 0; l < n1; l++) {
                                data[n][m][l] = bvd.get();
                            }
                        }
                    }
                } else {
                    for (int n = first; n <= last; n++) {
                        for (int m = 0; m < n1; m++) {
                            for (int l = 0; l < n0; l++) {
                                data[n][l][m] = bvd.get();
                            }
                        }
                    }
                }
                break;
            }
        }
        return data;
    }

    public static Double getPoint0(CDF thisCDF,Variable var, Integer pt) 
        throws Throwable {
        int point = pt.intValue();
        int type = var.getType();
        int itemSize = var.getDataItemSize();
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        for (int blk = 0; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            if (loc[1] < point) continue;
            ByteBuffer bv = positionBuffer((CDFImpl)thisCDF, var, loc[2],
                (loc[1] - loc[0] + 1));
            int pos = bv.position() + (point - loc[0])*itemSize;
            Method method;
            Number num;
            switch (DataTypes.typeCategory[type]) {
            case 0:
                return new Double((double)bv.getFloat(pos));
            case 1:
                return new Double(bv.getDouble(pos));
            case 2:
                method = DataTypes.method[type];
                num = (Number)method.invoke(bv, new Object[] {});
                return new Double(num.doubleValue());
            case 3:
                method = DataTypes.method[type];
                num = (Number)method.invoke(bv, new Object[] {});
                int x = num.intValue();
                double d = (x >= 0)?(double)x:(double)(longInt + x);
                return new Double(d);
            }
        }
        return null;
    }

    public static double[] getPoint1(CDF thisCDF,Variable var, Integer pt) 
        throws Throwable {
        int point = pt.intValue();
        int type = var.getType();
        int itemSize = var.getDataItemSize();
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        for (int blk = 0; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            if (loc[1] < point) continue;
            ByteBuffer bv = positionBuffer((CDFImpl)thisCDF, var, loc[2],
                (loc[1] - loc[0] + 1));
            int pos = bv.position() + (point - loc[0])*itemSize;
            bv.position(pos);
            int n = (((Integer)elementCount(var).elementAt(0))).intValue();
            double [] da = new double[n];
            Method method;
            switch (DataTypes.typeCategory[type]) {
            case 0:
                FloatBuffer bvf = bv.asFloatBuffer();
                for (int i = 0; i < n; i++) {
                    da[i] = bvf.get();
                }
                return da;
            case 1:
                DoubleBuffer bvd = bv.asDoubleBuffer();
                for (int i = 0; i < n; i++) {
                    da[i] = bvd.get();
                }
                return da;
            case 2:
                method = DataTypes.method[type];
                for (int i = 0; i < n; i++) {
                    Number num = (Number)method.invoke(bv, new Object[] {});
                    da[n] = num.doubleValue();
                }
                break;
            case 3:
                method = DataTypes.method[type];
                for (int i = 0; i < n; i++) {
                    Number num = (Number)method.invoke(bv, new Object[] {});
                    int x = num.intValue();
                    da[n] = (x >= 0)?(double)x:(double)(longInt + x);
                }
            }
        }
        return null;
    }

    public static double[][] getPoint2(CDF thisCDF, Variable var,
        Integer pt) throws Throwable {
        int point = pt.intValue();
        int type = var.getType();
        int itemSize = var.getDataItemSize();
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        for (int blk = 0; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            if (loc[1] < point) continue;
            ByteBuffer bv = positionBuffer((CDFImpl)thisCDF, var, loc[2],
                (loc[1] - loc[0] + 1));
            int pos = bv.position() + (point - loc[0])*itemSize;
            bv.position(pos);
            int n0 = (((Integer)elementCount(var).elementAt(0))).intValue();
            int n1 = (((Integer)elementCount(var).elementAt(1))).intValue();
            double [][] da = new double[n0][n1];
            Method method;
            switch (DataTypes.typeCategory[type]) {
            case 0:
                FloatBuffer bvf = bv.asFloatBuffer();
                if (var.rowMajority()) {
                    for (int i = 0; i < n0; i++) {
                        for (int j = 0; j < n1; j++) {
                            da[i][j] = bvf.get();
                        }
                    }
                } else {
                    for (int i = 0; i < n1; i++) {
                        for (int j = 0; j < n0; j++) {
                            da[j][i] = bvf.get();
                        }
                    }
                }
                return da;
            case 1:
                DoubleBuffer bvd = bv.asDoubleBuffer();
                if (var.rowMajority()) {
                    for (int i = 0; i < n0; i++) {
                        for (int j = 0; j < n1; j++) {
                            da[i][j] = bvd.get();
                        }
                    }
                } else {
                    for (int i = 0; i < n1; i++) {
                        for (int j = 0; j < n0; j++) {
                            da[j][i] = bvd.get();
                        }
                    }
                }
                return da;
            case 2:
                method = DataTypes.method[type];
                if (var.rowMajority()) {
                    for (int i = 0; i < n0; i++) {
                        for (int j = 0; j < n1; j++) {
                            Number num = 
                                (Number)method.invoke(bv, new Object[] {});
                            da[i][j] = num.doubleValue();
                        }
                    }
                } else {
                    for (int i = 0; i < n1; i++) {
                        for (int j = 0; j < n0; j++) {
                            Number num = 
                                (Number)method.invoke(bv, new Object[] {});
                            da[j][i] = num.doubleValue();
                        }
                    }
                }
                return da;
            case 3:
                method = DataTypes.method[type];
                if (var.rowMajority()) {
                    for (int i = 0; i < n0; i++) {
                        for (int j = 0; j < n1; j++) {
                            Number num = 
                                (Number)method.invoke(bv, new Object[] {});
                            int x = num.intValue();
                            double d = (x >= 0)?(double)x:(double)(longInt + x);
                            da[i][j] = d;
                        }
                    }
                } else {
                    for (int i = 0; i < n1; i++) {
                        for (int j = 0; j < n0; j++) {
                            Number num = 
                                (Number)method.invoke(bv, new Object[] {});
                            int x = num.intValue();
                            double d = (x >= 0)?(double)x:(double)(longInt + x);
                            da[j][i] = d;
                        }
                    }
                }
                return da;
            }
        }
        return null;
    }

    public static double[] getElement2(CDF thisCDF,Variable var, Integer pt1,
         Integer pt2) {
        return null;
    }

    public static double [] getRange0(CDF thisCDF, Variable var, Integer istart,
        Integer iend) throws Throwable {
        int start = istart.intValue();
        int end = iend.intValue();
        int numberOfValues = var.getNumberOfValues();
        int itemSize = var.getDataItemSize();
        double [] data = new double[end - start + 1];
        int type = var.getType();
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        int [] blks =  
            getBlockRange(locations, var.recordVariance(), start, end);
        int firstBlock = blks[0];
        int lastBlock = blks[1];
        int index = 0;
        for (int blk = firstBlock; blk <= lastBlock; blk++) {
            Object[] oa = positionBuffer((CDFImpl)thisCDF, var, blks, blk,
                start, end);
            ByteBuffer bv = (ByteBuffer)oa[0];
            int first = ((Integer)oa[1]).intValue();
            int last = ((Integer)oa[2]).intValue();
            Method method;
            switch (DataTypes.typeCategory[type]) {
            case 0:
                FloatBuffer bvf = bv.asFloatBuffer();
                for (int n = first; n <= last; n++) {
                    data[index++] = bvf.get();
                }
                break;
            case 1:
                DoubleBuffer bvd = bv.asDoubleBuffer();
                for (int n = first; n <= last; n++) {
                    data[index++] = bvd.get();
                }
                break;
            case 2:
                method = DataTypes.method[type];
                for (int n = first; n <= last; n++) {
                    Number num = (Number)method.invoke(bv, new Object[] {});
                    data[index++] = num.doubleValue();
                }
                break;
            case 3:
                method = DataTypes.method[type];
                for (int n = first; n <= last; n++) {
                    Number num = (Number)method.invoke(bv, new Object[] {});
                    int x = num.intValue();
                    data[index++] = (x >= 0)?(double)x:(double)(longInt + x);
                }
            }
        }
        if (!var.recordVariance()) {
            for (int i = start; i <= end; i++) {
                data[i - start] = data[0];
            }
        }
        return data;
    }

    public static double [][] getRange1(CDF thisCDF, Variable var,
        Integer istart, Integer iend) throws Throwable {
        int start = istart.intValue();
        int end = iend.intValue();
        int numberOfValues = var.getNumberOfValues();
        int itemSize = var.getDataItemSize();
        int elements = (((Integer)elementCount(var).elementAt(0))).intValue();
        double [][] data = new double[end - start + 1][elements];

        int type = var.getType();
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        int [] blks =  
            getBlockRange(locations, var.recordVariance(), start, end);
        int firstBlock = blks[0];
        int lastBlock = blks[1];

        int index = 0;
        for (int blk = firstBlock; blk <= lastBlock; blk++) {
            Object[] oa = positionBuffer((CDFImpl)thisCDF, var, blks, blk,
                start, end);
            ByteBuffer bv = (ByteBuffer)oa[0];
            int first = ((Integer)oa[1]).intValue();
            int last = ((Integer)oa[2]).intValue();
            switch (DataTypes.typeCategory[type]) {
            case 0:
                FloatBuffer bvf = bv.asFloatBuffer();
                for (int n = first; n <= last; n++) {
                    for (int m = 0; m < elements; m++) {
                        data[index][m] = bvf.get();
                    }
                    index++;
                }
                break;
            case 1:
                DoubleBuffer bvd = bv.asDoubleBuffer();
                for (int n = first; n <= last; n++) {
                    for (int m = 0; m < elements; m++) {
                        data[index][m] = bvd.get();
                    }
                    index++;
                }
                break;
            case 2:
                doSignedInteger(bv, type, first, last, elements, data);
                break;
            case 3:
                doUnsignedInteger(bv, type, first, last, elements, data);
                break;
            }
        }
        if (!var.recordVariance()) {
            for (int i = start; i <= end; i++) {
                for (int m = 0; m < elements; m++) {
                    data[i - start][m] = data[0][m];
                }
            }
        }
        return data;
    }


    static Vector elementCount(Variable var) {
        int [] dimensions = var.getDimensions();
        Vector ecount = new Vector();
        for (int i = 0; i < dimensions.length; i++) {
                if (var.getVarys()[i]) ecount.add(new Integer(dimensions[i]));
        }
        return ecount;
    }
    /** good for rank 1
     */
    static boolean validElement(Variable var, int[] idx) {
        int elements = (((Integer)elementCount(var).elementAt(0))).intValue();
        for (int i = 0; i < idx.length; i++) {
            if ((idx[i] >= 0) && (idx[i] < elements)) continue;
            return false;
        } 
        return true;
    }

    public static double [] getRangeForElement1(CDF thisCDF, Variable var,
        Integer istart, Integer iend, Integer ielement) throws Throwable {
        int element = ielement.intValue();
        if (!validElement(var, new int[] {element})) return null;
        int start = istart.intValue();
        int end = iend.intValue();
        int numberOfValues = var.getNumberOfValues();
        int size = var.getDataItemSize();
        int type = var.getType();
        double [] data = new double[end - start + 1];
        int loff = element*DataTypes.size[type];
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        int [] blks =  
            getBlockRange(locations, var.recordVariance(), start, end);
        int firstBlock = blks[0];
        int lastBlock = blks[1];
        int index = 0;
        for (int blk = firstBlock; blk <= lastBlock; blk++) {
            Object[] oa = positionBuffer((CDFImpl)thisCDF, var, blks, blk,
                start, end);
            ByteBuffer bv = (ByteBuffer)oa[0];
            int first = ((Integer)oa[1]).intValue();
            int last = ((Integer)oa[2]).intValue();
            int pos = bv.position() + loff;
            switch (DataTypes.typeCategory[type]) {
            case 0:
                for (int n = first; n <= last; n++) {
                    data[index++] = bv.getFloat(pos);
                    pos += size;
                }
                break;
            case 1:
                for (int n = first; n <= last; n++) {
                    data[index++] = bv.getDouble(pos);
                    pos += size;
                }
                break;
            case 2:
                index = doSignedInteger(bv, pos, type, size, first, last,
                     data, index);
                break;
            case 3:
                index = doUnsignedInteger(bv, pos, type, size, first, last,
                     data, index);
                break;
            }
        }
        if (!var.recordVariance()) {
            for (int i = start; i <= end; i++) {
                data[i - start] = data[0];
            }
        }
        return data;
    }

    /**
     * returns range of values for the specified elements of a one
     * dimensional variable.
     * returns null if any of the specified elements is not valid.
     * throws UnsupportedFeatureException
     */
    public static double [][] getRangeForElements1(CDF thisCDF, Variable var,
        Integer istart, Integer iend, int[] idx) throws Throwable {
        if (!validElement(var, idx)) return null;
        int start = istart.intValue();
        int end = iend.intValue();
        int numberOfValues = var.getNumberOfValues();
        int size = var.getDataItemSize();
        int ne = idx.length;
        double [][] data = new double[end - start + 1][ne];

        int type = var.getType();
        int[] loff = new int[ne];
        for (int i = 0; i < ne; i++) {
            loff[i] = idx[i]*DataTypes.size[type];
        }
        // loff contains offsets from the beginning of the item
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        int [] blks =  
            getBlockRange(locations, var.recordVariance(), start, end);
        int firstBlock = blks[0];
        int lastBlock = blks[1];

        int index = 0;
        for (int blk = firstBlock; blk <= lastBlock; blk++) {
            Object[] oa = positionBuffer((CDFImpl)thisCDF, var, blks, blk,
                start, end);
            ByteBuffer bv = (ByteBuffer)oa[0];
            int first = ((Integer)oa[1]).intValue();
            int last = ((Integer)oa[2]).intValue();
            int pos = bv.position();
            switch (DataTypes.typeCategory[type]) {
            case 0:
                for (int n = first; n <= last; n++) {
                    for (int e = 0; e < ne; e++) {
                        data[index][e] = bv.getFloat(pos + loff[e]);
                    }
                    pos += size;
                    index++;
                }
                break;
            case 1:
                for (int n = first; n <= last; n++) {
                    for (int e = 0; e < ne; e++) {
                        data[index][e] = bv.getDouble(pos + loff[e]);
                    }
                    pos += size;
                    index++;
                }
                break;
            case 2:
                index = doSignedInteger(bv, pos, type, size, first, last,
                    loff,  data, index);
                break;
            case 3:
                index = doUnsignedInteger(bv, pos, type, size, first, last,
                    loff,  data, index);
                break;
            }
        }
        if (!var.recordVariance()) {
            for (int i = start; i <= end; i++) {
                for (int e = 0; e < ne; e++) {
                    data[i - start][e] = data[0][e];
                }
            }
        }
        return data;
    }

    public static double [][][] getRange2(CDF thisCDF, Variable var,
        Integer istart, Integer iend) {
        return null;
    }

    /**
     * returns String of length 'size' starting at current position
     * in the given ByteBuffer. On return, the buffer position is 1
     * advanced by the smaller of size, or the length of the null
     * terminated string
     */
    public static String getStringValue(ByteBuffer bv, int size) {
        byte [] ba = new byte[size];
        int i = 0;
        for (; i < size; i++) {
            ba[i] = bv.get();
            if (ba[i] == 0) break;
        }
        return new String(ba, 0, i);
    }

    public static String [] getStringSeries0(CDF thisCDF, Variable var) {
        int numberOfValues = var.getNumberOfValues();
        String [] data = new String[numberOfValues];
        int len = var.getNumberOfElements();
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        for (int blk = 0; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            ByteBuffer bv = positionBuffer((CDFImpl)thisCDF, var, loc[2],
                (loc[1] - loc[0] + 1));
            int pos = bv.position();
            for (int n = loc[0]; n <= loc[1]; n++) {
                data[n] = getStringValue(bv, len);
                pos += len;
                bv.position(pos);
            }
        }
        if (!var.recordVariance()) {
            for (int i = 1; i < numberOfValues; i++) {
                data[i] = data[0];
            }
        }
        return data;
    }
    public static String [][] getStringSeries1(CDF thisCDF, Variable var) {
        int nv = var.getNumberOfValues();
        if (nv == 0) return null;
        if (!var.recordVariance()) nv = 1;
        int elements = (((Integer)elementCount(var).elementAt(0))).intValue();
        String [][] data = new String[nv][elements];
        int size = var.getDataItemSize();
        int len = var.getNumberOfElements();
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        for (int blk = 0; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            ByteBuffer bv = positionBuffer((CDFImpl)thisCDF, var, loc[2],
                (loc[1] - loc[0] + 1));
            int pos = bv.position();
            for (int n = loc[0]; n <= loc[1]; n++) {
                for (int m = 0; m < elements; m++) {
                    data[n][m] = getStringValue(bv, len);
                    pos += len;
                    bv.position(pos);
                }
            }
        }
        return data;
    }

    public static String [][][] getStringSeries2(CDF thisCDF, Variable var) {
        return null;
    }

    static long longInt = ((long)1) << 32;

    /**
     * returns range of blocks containing the range of records (start, end).
     */
    static int [] getBlockRange(Vector locations, boolean recordVariance,
        int start, int end) {

        int firstBlock;
        int lastBlock;
        if (recordVariance) {
            firstBlock = -1;
            int blk = 0;
            for (; blk < locations.size(); blk++) {
                int [] loc = (int [])locations.elementAt(blk);
                if (start > loc[1]) continue;
                firstBlock = blk;
                break;
            }
            if (firstBlock < 0) return null;
            blk = firstBlock;
            lastBlock = locations.size() - 1;
            for (; blk < locations.size(); blk++) {
                int [] loc = (int [])locations.elementAt(blk);
                if (end > loc[1]) continue;
                lastBlock = blk;
                break;
            }
        } else {
            firstBlock = 0;
            lastBlock = 0;
        }
        return new int[] {firstBlock, lastBlock};
    }

    /**
     * returns ByteBuffer containing count values for variable var starting at
     * CDF offset value offset.
     */
    static ByteBuffer positionBuffer(CDFImpl impl, Variable var, int offset,
        int count) {
        ByteBuffer bv;
        if (!var.isCompressed()) {
            bv = impl.getValueBuffer(offset);
        } else {
            int size = var.getDataItemSize();
            bv = impl.getValueBuffer(offset, size , count);
        }
        bv.order(impl.getByteOrder());
        return bv;
    }

    /**
     * returns ByteBuffer, index of first entry and index of last entry for
     * the specified block of data corresponding to variable 'var' for the
     * range of records (start, end).
     */
    static Object[] positionBuffer(CDFImpl impl, Variable var, int[] blockRange,
        int blk, int start, int end) {
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        int [] loc = (int [])locations.elementAt(blk);
        int first = loc[0];
        int last = loc[1];
        ByteBuffer bv = positionBuffer(impl, var, loc[2], (last - first + 1));
        if (var.recordVariance()) {
            if (blk == blockRange[0]) {// position to first needed
                int size = var.getDataItemSize();
                bv.position(bv.position() + size*(start - first));
                first = start;
            }
            if (blk == blockRange[1]) {// position to first needed
                last = end;
            }
        }
        return new Object[] {bv, new Integer(first), new Integer(last)};
    }
    public static double [][][][] getSeries3(CDF thisCDF, Variable var) {
        int nv = var.getNumberOfValues();
        if (nv == 0) return null;
        if (!var.recordVariance()) nv = 1;
        int n0 = (((Integer)elementCount(var).elementAt(0))).intValue();
        int n1 = (((Integer)elementCount(var).elementAt(1))).intValue();
        int n2 = (((Integer)elementCount(var).elementAt(2))).intValue();
        double [][][][] data = new double[nv][n0][n1][n2];
        int type = var.getType();
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        for (int blk = 0; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            int first = loc[0];
            int last = loc[1];
            ByteBuffer bv = positionBuffer((CDFImpl)thisCDF, var, loc[2],
                (last - first + 1));
            switch (DataTypes.typeCategory[type]) {
            case 0:
                FloatBuffer bvf = bv.asFloatBuffer();
                if (var.rowMajority()) {
                    for (int n = first; n <= last; n++) {
                        for (int m = 0; m < n0; m++) {
                            for (int l = 0; l < n1; l++) {
                                for (int k = 0; k < n2; k++) {
                                    data[n][m][l][k] = bvf.get();
                                }
                            }
                        }
                    }
                } else {
                    for (int n = first; n <= last; n++) {
                        for (int m = 0; m < n2; m++) {
                            for (int l = 0; l < n1; l++) {
                                for (int k = 0; k < n0; k++) {
                                    data[n][k][l][m] = bvf.get();
                                }
                            }
                        }
                    }
                }
                break;
            case 1:
                DoubleBuffer bvd = bv.asDoubleBuffer();
                if (var.rowMajority()) {
                    for (int n = first; n <= last; n++) {
                        for (int m = 0; m < n0; m++) {
                            for (int l = 0; l < n1; l++) {
                                for (int k = 0; k < n2; k++) {
                                    data[n][m][l][k] = bvd.get();
                                }
                            }
                        }
                    }
                } else {
                    for (int n = first; n <= last; n++) {
                        for (int m = 0; m < n2; m++) {
                            for (int l = 0; l < n1; l++) {
                                for (int k = 0; k < n0; k++) {
                                    data[n][k][l][m] = bvd.get();
                                }
                            }
                        }
                    }
                }
                break;
            }
        }
        return data;
    }
    public static double[][][] getPoint3(CDF thisCDF, Variable var,
        Integer pt) throws Throwable {
        int point = pt.intValue();
        int type = var.getType();
        int itemSize = var.getDataItemSize();
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        for (int blk = 0; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            if (loc[1] < point) continue;
            ByteBuffer bv = positionBuffer((CDFImpl)thisCDF, var, loc[2],
                (loc[1] - loc[0] + 1));
            int pos = bv.position() + (point - loc[0])*itemSize;
            bv.position(pos);
            int n0 = (((Integer)elementCount(var).elementAt(0))).intValue();
            int n1 = (((Integer)elementCount(var).elementAt(1))).intValue();
            int n2 = (((Integer)elementCount(var).elementAt(2))).intValue();
            double [][][] da = new double[n0][n1][n2];
            Method method;
            switch (DataTypes.typeCategory[type]) {
            case 0:
                FloatBuffer bvf = bv.asFloatBuffer();
                if (var.rowMajority()) {
                    for (int i = 0; i < n0; i++) {
                        for (int j = 0; j < n1; j++) {
                            for (int k = 0; k < n2; k++) {
                                da[i][j][k] = bvf.get();
                            }
                        }
                    }
                } else {
                    for (int i = 0; i < n2; i++) {
                        for (int j = 0; j < n1; j++) {
                            for (int k = 0; k < n0; k++) {
                                da[k][j][i] = bvf.get();
                            }
                        }
                    }
                }
                return da;
            case 1:
                DoubleBuffer bvd = bv.asDoubleBuffer();
                if (var.rowMajority()) {
                    for (int i = 0; i < n0; i++) {
                        for (int j = 0; j < n1; j++) {
                            for (int k = 0; k < n2; k++) {
                                da[i][j][k] = bvd.get();
                            }
                        }
                    }
                } else {
                    for (int i = 0; i < n2; i++) {
                        for (int j = 0; j < n1; j++) {
                            for (int k = 0; k < n0; k++) {
                                da[k][j][i] = bvd.get();
                            }
                        }
                    }
                }
                return da;
            case 2:
                method = DataTypes.method[type];
                if (var.rowMajority()) {
                    for (int i = 0; i < n0; i++) {
                        for (int j = 0; j < n1; j++) {
                            for (int k = 0; k < n2; k++) {
                                Number num = 
                                    (Number)method.invoke(bv, new Object[] {});
                                da[i][j][k] = num.doubleValue();
                            }
                        }
                    }
                } else {
                    for (int i = 0; i < n2; i++) {
                        for (int j = 0; j < n1; j++) {
                            for (int k = 0; k < n0; k++) {
                                Number num = 
                                    (Number)method.invoke(bv, new Object[] {});
                                da[k][j][i] = num.doubleValue();
                            }
                        }
                    }
                }
                return da;
            case 3:
                method = DataTypes.method[type];
                if (var.rowMajority()) {
                    for (int i = 0; i < n0; i++) {
                        for (int j = 0; j < n1; j++) {
                            for (int k = 0; k < n2; k++) {
                                Number num = 
                                    (Number)method.invoke(bv, new Object[] {});
                                int x = num.intValue();
                                double d = (x >= 0)?(double)x:
                                    (double)(longInt + x);
                                da[i][j][k] = d;
                            }
                        }
                    }
                } else {
                    for (int i = 0; i < n2; i++) {
                        for (int j = 0; j < n1; j++) {
                            for (int k = 0; k < n0; k++) {
                                Number num = 
                                    (Number)method.invoke(bv, new Object[] {});
                                int x = num.intValue();
                                double d = (x >= 0)?(double)x:
                                    (double)(longInt + x);
                                da[k][j][i] = d;
                            }
                        }
                    }
                }
                return da;
            }
        }
        return null;
    }

    public static double [] get1DSeries(CDF thisCDF, Variable var, int[] pt)
        throws IllegalAccessException, InvocationTargetException {
        double [] data;
        int end = -1;
        int begin = 0;
        int nv = 0;
        if (pt != null) {
            if (var.recordVariance()) {
                begin = pt[0];
                nv = 1;
                if (pt.length > 1) {
                    end = pt[1];
                    nv = end - begin + 1;
                }
            }
        } else {
            nv = var.getNumberOfValues();
            if (nv == 0) return null;
        }
        if (!var.recordVariance()) nv = 1;
        int type = var.getType();
        int itemSize = var.getDataItemSize();
        int elements = itemSize/DataTypes.size[type];
        data = new double[nv*elements];

        float[] tf = null;
        if (DataTypes.typeCategory[type] == 0) tf = new float[nv*elements];

        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        ByteBuffer bv;
        int blk = 0;
        int offset = 0;
        if (pt == null) {
            begin = ((int [])locations.elementAt(0))[0];
            end = ((int [])locations.elementAt(locations.size() - 1))[1];
        }
        for (; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            int first = loc[0];
            int last = loc[1];
            if (last < begin) continue;
            int count = (last - first + 1);
            bv = positionBuffer((CDFImpl)thisCDF, var, loc[2], count);
            if (begin > first) {
                int pos = bv.position() + (begin - first)*itemSize;
                bv.position(pos);
            }
            if (end < 0) { // single point needed
                do1D(bv, type, tf, data, 0, elements);
                offset += elements;
            } else {
                int term = (end <= last)?end:last;
                int init = (begin > first)?begin:first;
                count = (term - init + 1);
                do1D(bv, type, tf, data, offset, count*elements);
                offset += count*elements;
            }
            if (end <= last) break;
        }
        if (offset == 0) return null;
        return data;
    }
    static void do1D(ByteBuffer bv, int type, float[] tf, double[] data,
       int offset, int number) throws IllegalAccessException,
       InvocationTargetException {
        Method method;
        switch (DataTypes.typeCategory[type]) {
        case 0:
            FloatBuffer bvf = bv.asFloatBuffer();
            bvf.get(tf, 0, number);
            for (int n = 0; n < number; n++) {
                data[offset + n] = tf[n];
            }
            break;
        case 1:
            DoubleBuffer bvd = bv.asDoubleBuffer();
            bvd.get(data, offset, number);
            break;
        case 2:
            method = DataTypes.method[type];
            for (int e = 0; e < number; e++) {
                Number num = (Number)method.invoke(bv, new Object[] {});
                data[offset + e] = num.doubleValue();
            }
            break;
        case 3:
            method = DataTypes.method[type];
            for (int e = 0; e < number; e++) {
                Number num = (Number)method.invoke(bv, new Object[] {});
                int x = num.intValue();
                data[offset + e] = (x >= 0)?(double)x:(double)(longInt + x);
            }
            break;
        }
    }
    public static double [] get1DSeries(CDF thisCDF, Variable var, int[] pt,
        int[] stride) throws IllegalAccessException, InvocationTargetException {
        double [] data;
        
        int end = -1;
        int begin = 0;
        int nv = 0;
        if (pt != null) {
            if (var.recordVariance()) {
                begin = pt[0];
                nv = 1;
                if (pt.length > 1) {
                    end = pt[1];
                    nv = end - begin + 1;
                }
            }
        } else {
            nv = var.getNumberOfValues();
        }
        if (nv == 0) return null;
        if (!var.recordVariance()) nv = 1;
        Stride strideObject = new Stride(stride);
        int _stride = strideObject.getStride(nv);
        if (_stride > 1) {
            nv = (nv/_stride);
            if ((nv % _stride) != 0) nv++;
        }
        int type = var.getType();
        int itemSize = var.getDataItemSize();
        int elements = itemSize/DataTypes.size[type];
        data = new double[nv*elements];

        float[] tf = null;
        if (_stride == 1) {
            if (DataTypes.typeCategory[type] == 0) tf = new float[nv*elements];
        }

        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        ByteBuffer bv;
        int blk = 0;
        int offset = 0;
        if (pt == null) {
            begin = ((int [])locations.elementAt(0))[0];
            end = ((int [])locations.elementAt(locations.size() - 1))[1];
        }
        int index = 0;
        for (; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            int first = loc[0];
            int last = loc[1];
            if (last < begin) continue;
            int count = (last - first + 1);
            bv = positionBuffer((CDFImpl)thisCDF, var, loc[2], count);
            int pos = 0;
            int init;
            if (begin > first) {
                pos = bv.position() + (begin - first)*itemSize;
                bv.position(pos);
                init = begin;
            } else {
                init = first;
                if (_stride > 1) {
                    int elapsed = first - begin;
                    if ((elapsed % _stride) != 0) {
                        init = first - (elapsed % _stride) + _stride;
                        pos = bv.position() + 
                            (_stride - (elapsed % _stride))*itemSize;
                        bv.position(pos);
                    }
                }
            }
            if (end < 0) { // single point needed
                do1D(bv, type, tf, data, 0, elements);
                offset += elements;
            } else {
                int term = (end <= last)?end:last;
                if (_stride == 1) {
                    count = (term - init + 1);
                    do1D(bv, type, tf, data, offset, count*elements);
                } else {
                    count = (term - init)/_stride;
                    if (count*_stride < (term - init)) count++;
                    if (DataTypes.typeCategory[type] == 0) {
                        tf = new float[count*elements];
                    }
                    // need to position to first desired element
                    do1D(bv, type, tf, data, offset, count,
                        elements, _stride);
                }
                offset += count*elements;
            }
            if (end <= last) break;
        }
        if (offset == 0) return null;
        return data;
    }
    static void do1D(ByteBuffer bv, int type, float[] tf, double[] data,
       int offset, int count, int elements, int _stride) throws
       IllegalAccessException, InvocationTargetException {
        Method method;
        int span = _stride*elements;
        switch (DataTypes.typeCategory[type]) {
        case 0:
            FloatBuffer bvf = bv.asFloatBuffer();
            for (int n = 0; n < count; n++) {
                bvf.position(n*span);
                bvf.get(tf, n*elements, elements);
            }
            for (int n = 0; n < count*elements; n++) {
                data[offset + n] = tf[n];
            }
            break;
        case 1:
            DoubleBuffer bvd = bv.asDoubleBuffer();
            for (int n = 0; n < count; n++) {
                bvd.position(n*span);
                bvd.get(data, offset, elements);
                offset += elements;
            }
            break;
        }
    }
    public static double [][] getSampledTimeSeries0(CDF thisCDF, Variable var,
        Boolean ignoreFill, double[] timeRange, int[] stride) throws Throwable
        {
        return getSampledTimeSeries(thisCDF, var, null, ignoreFill, timeRange,
            stride);
    }
    public static double [][] getSampledTimeSeries1(CDF thisCDF, Variable var,
        Integer which, Boolean ignoreFill, double[] timeRange, int[] stride)
        throws Throwable {
        return getSampledTimeSeries(thisCDF, var, which, ignoreFill, timeRange,
            stride);
    }
    public static double [][] getSampledTimeSeries(CDF thisCDF, Variable var,
        Integer which, Boolean ignoreFill, double[] timeRange, int[] stride)
        throws Throwable {
        if (var.getNumberOfValues() == 0) return null;
        boolean ignore = ignoreFill.booleanValue();
        double [] vdata;
        int [] recordRange = null;
        double [] times = null;
        times = ((CDFImpl)thisCDF).getTimes(var, false);
        if (times == null) return null;
        double[] stimes;
        Stride strideObject = new Stride(stride);
        if (timeRange == null) {
            vdata = (which == null)?getSeries0(thisCDF, var, strideObject):
                                    getElement1(thisCDF, var, which,
                                    strideObject);
        } else {
            recordRange = getRecordRange(thisCDF, var, timeRange);
            if (recordRange == null) return null;
            if (which == null) {
                vdata = getRange0(thisCDF, var, new Integer(recordRange[0]),
                                  new Integer(recordRange[1]), strideObject);
            } else {
                vdata = getRangeForElement1(thisCDF, var,
                    new Integer(recordRange[0]), new Integer(recordRange[1]),
                    which, strideObject);
            }
        }
        int _stride = strideObject.getStride();
        double [] fill = getFillValue(thisCDF, var);
        if ((!ignore) || (fill[0] != 0)) {
            if (timeRange == null) {
                if (_stride == 1) {
                    return new double [][] {times, vdata};
                } else {
                    stimes = new double[vdata.length];
                    for (int i = 0; i < vdata.length; i ++) {
                        stimes[i] = times[i*_stride];
                    }
                    return new double [][] {stimes, vdata};
                }
            } else {
                stimes = new double[vdata.length];
                if (_stride == 1) {
                    System.arraycopy(times, recordRange[0], stimes, 0,
                        vdata.length);
                    return new double [][] {stimes, vdata};
                } else {
                    int srec = recordRange[0];
                    for (int i = 0; i < vdata.length; i ++) {
                        stimes[i] = times[srec + i*_stride];
                    }
                    return new double [][] {stimes, vdata};
                }
            }
        }
        // fill values need to be filtered
        if (timeRange == null) {
            if (_stride == 1) {
                return filterFill(times, vdata, fill[1], 0);
            } else {
                stimes = new double[vdata.length];
                for (int i = 0; i < vdata.length; i ++) {
                    stimes[i] = times[i*_stride];
                }
                return filterFill(stimes, vdata, fill[1], 0);
            }
        } else {
            stimes = new double[vdata.length];
            if (_stride == 1) {
                System.arraycopy(times, recordRange[0], stimes, 0,
                    vdata.length);
                return filterFill(stimes, vdata, fill[1], 0);
            } else {
                int srec = recordRange[0];
                for (int i = 0; i < vdata.length; i ++) {
                    stimes[i] = times[srec + i*_stride];
                }
                return filterFill(stimes, vdata, fill[1], 0);
            }
        }
    }
    public static double [] getSeries0(CDF thisCDF, Variable var,
        Stride strideObject) throws IllegalAccessException,
        InvocationTargetException, Throwable {
        int numberOfValues = var.getNumberOfValues();
        if (numberOfValues == 0) return null;
        int type = var.getType();
        int numpt;
        int _stride = strideObject.getStride(numberOfValues);
        if (_stride == 1) {
            numpt = numberOfValues;
        } else {
            int cat = DataTypes.typeCategory[type];
            if (cat > 1) throw new Throwable("Type category " + cat +
            " not supported in this context");
            numpt = numberOfValues/_stride;
            if ((numpt*_stride) <  numberOfValues) numpt++;
        }
        double [] data = new double[numpt];
        int size = var.getDataItemSize();
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        int next = 0;
        for (int blk = 0; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            int first = loc[0];
            int last = loc[1];
            ByteBuffer bv = positionBuffer((CDFImpl)thisCDF, var, loc[2],
                (last - first + 1));
            Method method;
            int n = first % _stride;
            if (n == 0) {
                n = first;
            } else {
                n = (first - n) + _stride;
            }
            int pos = (n - first);
            switch (DataTypes.typeCategory[type]) {
            case 0:
                FloatBuffer bvf = bv.asFloatBuffer();
                for (; pos <= last; pos += _stride) {
                    data[next++] = bvf.get(pos);
                }
                break;
            case 1:
                DoubleBuffer bvd = bv.asDoubleBuffer();
                for (; pos <= last; pos += _stride) {
                    data[next++] = bvd.get(pos);
                }
                break;
            case 2:
                method = DataTypes.method[type];
                for (; n <= last; n += _stride) {
                    bv.position(4*n);
                    Number num = (Number)method.invoke(bv, new Object[] {});
                    data[next++] = num.doubleValue();
                }
                break;
            case 3:
                method = DataTypes.method[type];
                for (; n <= last; n += _stride) {
                    bv.position(4*n);
                    Number num = (Number)method.invoke(bv, new Object[] {});
                    int x = num.intValue();
                    data[next++] = (x >= 0)?(double)x:(double)(longInt + x);
                }
            }
        }
        if (!var.recordVariance()) {
            for (int i = 1; i < numpt; i++) {
                data[i] = data[0];
            }
        }
        return data;
    }
    public static double [] getElement1(CDF thisCDF, Variable var, Integer idx,
        Stride strideObject) throws Throwable {
        int element = idx.intValue();
        int nv = var.getNumberOfValues();
        if (nv == 0) return null;
        if (!var.recordVariance()) nv = 1;
        if (!validElement(var, new int[] {element})) return null;
        int type = var.getType();
        int numpt = nv;
        int _stride = strideObject.getStride(nv);
        if (_stride != 1) {
            int cat = DataTypes.typeCategory[type];
            if (cat > 1) throw new Throwable("Type category " + cat +
            " not supported in this context");
            numpt = nv/_stride;
            if ((numpt*_stride) <  nv) numpt++;
        }
        double [] data = new double[numpt];
        int size = var.getDataItemSize();
        int advance = size*_stride;

        int loff = element*DataTypes.size[type];
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        int point = 0;
        for (int blk = 0; blk < locations.size(); blk++) {
            int [] loc = (int [])locations.elementAt(blk);
            int first = loc[0];
            int last = loc[1];
            ByteBuffer bv = positionBuffer((CDFImpl)thisCDF, var, loc[2],
                (last - first + 1));
            Method method;
            int n = first % _stride;
            if (n == 0) {
                n = first;
            } else {
                n = (first - n) + _stride;
            }
            int pos = bv.position() + (n - first)*size + loff;
            switch (DataTypes.typeCategory[type]) {
            case 0:
                while (n <= last) {
                    data[point++] = bv.getFloat(pos);
                    n += _stride;
                    pos += advance;
                }
                break;
            case 1:
                while (n <= last) {
                    data[point++] = bv.getDouble(pos);
                    n += _stride;
                    pos += advance;
                }
                break;
            case 2:
                int res = doSignedInteger(bv, pos, type, size, n, last, data,
                new int[]{_stride}, point);
                point = res;
                break;
            case 3:
                res = doUnsignedInteger(bv, pos, type, size, n, last, data,
                new int[]{_stride}, point);
                point = res;
                break;
            }
        }
        return data;
    }
    static int doSignedInteger(ByteBuffer bv, int pos, int type,
        int size, int first, int last, double[] data, int[] stride,
        int point) throws IllegalAccessException, InvocationTargetException {
        Method method = DataTypes.method[type];
        int index = point;
        bv.position(pos);
        int _stride = stride[0];
        int advance = _stride*size;
        int n = first;
        while (n <= last) {
            Number num = (Number)method.invoke(bv, new Object[] {});
            data[index++] = num.doubleValue();
            n += _stride;
            pos += advance;
            bv.position(pos);
        }
        return index;
    }
    static int doUnsignedInteger(ByteBuffer bv, int pos, int type,
        int size, int first, int last, double[] data, int[] stride,
        int point) throws IllegalAccessException, InvocationTargetException {
        Method method = DataTypes.method[type];
        int index = point;
        bv.position(pos);
        int _stride = stride[0];
        int advance = _stride*size;
        int n = first;
        while (n <= last) {
            Number num = (Number)method.invoke(bv, new Object[] {});
            int x = num.intValue();
            data[index++] = (x >= 0)?(double)x:(double)(longInt + x);
            n += _stride;
            pos += advance;
            bv.position(pos);
        }
        return index;
    }
    public static double [] getRange0(CDF thisCDF, Variable var, Integer istart,
        Integer iend, Stride strideObject) throws Throwable {
        int begin = istart.intValue();
        if (begin < 0) throw new Throwable("getRange0 start < 0");
        int end = iend.intValue();
        int nv = var.getNumberOfValues();
        if (end > nv) throw new Throwable("getRange0 end > available " + nv);
        if (nv == 0) return null;
        if (!var.recordVariance()) nv = 1;
        nv = end - begin + 1;
        int _stride = strideObject.getStride(nv);
        if (_stride > 1) {
            int numpt = nv/_stride;
            if ((numpt*_stride) <  nv) numpt++;
            nv = numpt;
        }
        int type = var.getType();
        int itemSize = var.getDataItemSize();
        double [] data = new double[nv];

        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        int [] blks =
            getBlockRange(locations, var.recordVariance(), begin, end);
        int firstBlock = blks[0];
        int lastBlock = blks[1];
        int index = 0;
        for (int blk = firstBlock; blk <= lastBlock; blk++) {
            Object[] oa = positionBuffer((CDFImpl)thisCDF, var, blks, blk,
                begin, end);
            ByteBuffer bv = (ByteBuffer)oa[0];
            int first = ((Integer)oa[1]).intValue();
            int last = ((Integer)oa[2]).intValue() - first;
            int n;
            if (_stride > 1) {
                if (blk > firstBlock) {
                    int elapsed = first - begin;
                    if ((elapsed  % _stride) > 0) {
                        n = _stride - ((first - begin) % _stride);
                        int pos = bv.position() + n*itemSize;
                        bv.position(pos);
                        last -= n;
                    }
                }
            }
            n = 0;
            Method method;
            switch (DataTypes.typeCategory[type]) {
            case 0:
                FloatBuffer bvf = bv.asFloatBuffer();
                for (; n <= last; n += _stride) {
                    data[index++] = bvf.get(n);
                }
                break;
            case 1:
                DoubleBuffer bvd = bv.asDoubleBuffer();
                for (; n <= last; n += _stride) {
                    data[index++] = bvd.get();
                }
                break;
            case 2:
                method = DataTypes.method[type];
                for (; n <= last; n += _stride) {
                    Number num = (Number)method.invoke(bv, new Object[] {});
                    data[index++] = num.doubleValue();
                }
                break;
            case 3:
                method = DataTypes.method[type];
                for (; n <= last; n += _stride) {
                    Number num = (Number)method.invoke(bv, new Object[] {});
                    int x = num.intValue();
                    data[index++] = (x >= 0)?(double)x:(double)(longInt + x);
                }
            }
        }
        if (!var.recordVariance()) {
            for (int i = begin; i <= end; i += _stride) {
                data[i - begin] = data[0];
            }
        }
        return data;
    }
    public static double [] getRangeForElement1(CDF thisCDF, Variable var,
        Integer istart, Integer iend, Integer ielement, Stride strideObject)
        throws Throwable {
        int element = ielement.intValue();
        if (!validElement(var, new int[] {element})) return null;
        int begin = istart.intValue();
        int end = iend.intValue();
        int nv = var.getNumberOfValues();
        if (end > nv) throw new Throwable("getRange0 end > available " + nv);
        if (nv == 0) return null;
        if (!var.recordVariance()) nv = 1;
        nv = end - begin + 1;
        int _stride = strideObject.getStride(nv);
        if (_stride > 1) {
            int numpt = nv/_stride;
            if ((numpt*_stride) <  nv) numpt++;
            nv = numpt;
        }
        int type = var.getType();
        int itemSize = var.getDataItemSize();
        int advance = itemSize*_stride;
        double [] data = new double[nv];
        int loff = element*DataTypes.size[type];
        Vector locations = ((CDFImpl.DataLocator)var.getLocator()).locations;
        int [] blks =
            getBlockRange(locations, var.recordVariance(), begin, end);
        int firstBlock = blks[0];
        int lastBlock = blks[1];
        int index = 0;
        for (int blk = firstBlock; blk <= lastBlock; blk++) {
            Object[] oa = positionBuffer((CDFImpl)thisCDF, var, blks, blk,
                begin, end);
            ByteBuffer bv = (ByteBuffer)oa[0];
            int first = ((Integer)oa[1]).intValue();
            int last = ((Integer)oa[2]).intValue();
            int pos = bv.position() + loff;
            int n = first;
            if (_stride > 1) {
                if (blk > firstBlock) {
                    int elapsed = first - begin;
                    if ((elapsed % _stride) != 0) {
                        n = first + _stride - (elapsed % _stride);
                        pos += (n - first)*itemSize;
                    }
                }
            }
            switch (DataTypes.typeCategory[type]) {
            case 0:
                for (; n <= last; n += _stride) {
                    data[index++] = bv.getFloat(pos);
                    pos += advance;
                }
                break;
            case 1:
                for (; n <= last; n += _stride) {
                    data[index++] = bv.getDouble(pos);
                    pos += advance;
                }
                break;
            case 2:
                index = doSignedInteger(bv, pos, type, itemSize, n, last,
                     data, index, new int[]{_stride});
                break;
            case 3:
                index = doUnsignedInteger(bv, pos, type, itemSize, n, last,
                     data, index, new int[]{_stride});
                break;
            }
        }
        if (!var.recordVariance()) {
            int i = begin;
            int n = 0;
            while (i <= end) {
                data[n++] = data[0];
                i += _stride;
            }
        }
        return data;
    }
    static int doSignedInteger(ByteBuffer bv, int pos, int type,
        int size, int first, int last, double[] data, int index,
        int[] stride) throws IllegalAccessException, InvocationTargetException {
        Method method = DataTypes.method[type];
        bv.position(pos);
        for (int n = first; n <= last; n += stride[0]) {
            Number num = (Number)method.invoke(bv, new Object[] {});
            data[index++] = num.doubleValue();
            pos += size;
            bv.position(pos);
        }
        return index;
    }
    static int doUnsignedInteger(ByteBuffer bv, int pos, int type,
        int size, int first, int last, double[] data, int index,
        int[] stride) throws IllegalAccessException, InvocationTargetException {
        Method method = DataTypes.method[type];
        bv.position(pos);
        for (int n = first; n <= last; n += stride[0]) {
            Number num = (Number)method.invoke(bv, new Object[] {});
            int x = num.intValue();
            data[index++] = (x >= 0)?(double)x:(double)(longInt + x);
            pos += size;
            bv.position(pos);
        }
        return index;
    }

}