// $Id: CDFUtils.java,v 1.1.1.1 2025/09/11 12:24:45 mhliu Exp $
/*
 * Copyright 1996-2014 United States Government as represented by the
 * Administrator of the National Aeronautics and Space Administration.
 * All Rights Reserved.
 */

package gsfc.nssdc.cdf.util;

import java.lang.reflect.Array;
import java.lang.*;
import java.util.*;
import java.io.*;
import gsfc.nssdc.cdf.*;
import gsfc.nssdc.cdf.util.*;

/**
 * This class contains the handy utility routines (methods) called by the
 * core CDF Java APIs.
 *
 * @version 1.0
 * @author David Han, GSFC <BR>
 *         Mike Liu, Raytheon ITSS <BR>
 */

public class CDFUtils implements CDFConstants {

    /******************************************************************
     * Gets the java signature of the given object.  <P>
     *
     * <B>NOTE</B>: Java primitive data types (e.g. int, long, byte, etc.) are
     *       not Objects.  Thus they must be passed-in as an Object by
     *       using a wrapper (e.g. Integer(23)). 
     *  
     * <PRE> 
     *  Signature                 Java Programming Language Type
     *  ---------                 ------------------------------
     *  [Z                        array of boolean
     *  [B                        array of byte
     *  [C                        array of char
     *  [S                        array of short
     *  [I                        array of int
     *  [J                        array of long
     *  [F                        array of float
     *  [D                        array of double 
     *
     *  L fully-qualified-class   fully-qualified class
     *  L fully-qualified-class;  array of fully-qualified class
     *  java.lang.Boolean         Boolean
     *  Ljava.lang.Boolean;       array of Boolean
     *  java.lang.Byte            Byte
     *  Ljava.lang.Byte;          array of Byte
     *  java.lang.Short           Short 
     *  Ljava.lang.Short;         array of Short 
     *  java.lang.Integer         Integer
     *  Ljava.lang.Integer;       array of Integer
     *  java.lang.Long            Long
     *  Ljava.lang.Long;          array of Long
     *  java.lang.Float           Float
     *  Ljava.lang.Float;         array of Float
     *  java.lang.Double          Double
     *  Ljava.lang.Double;        array of Double
     *  java.lang.String          String
     *  Ljava.lang.String;        array of String
     * </PRE>
     *
     * @param obj the object from which Java signature is retrieved <P>
     * @return Java signature of the given object
     *  
     ********************************************************************/

    public static String getSignature(Object obj) {
	StringBuffer sigBuf = new StringBuffer();
	String clazz = obj.getClass().getName();   // Get the class signature

	if (clazz.indexOf('[') == 0)               // Array 
            sigBuf.append(clazz);
	else {
	    StringTokenizer sigParts = new StringTokenizer(clazz, ".");
	    sigBuf.append("L");                    
	    do {
		sigBuf.append(sigParts.nextToken());
		if (sigParts.hasMoreTokens()) sigBuf.append("/");
	    } while (sigParts.hasMoreTokens());
	    sigBuf.append(";");
	}
	return sigBuf.toString();
    }
    
    /**
     * Gets the number of elements contained in the given data object.  <P>
     *
     * @param dataType the CDF data type of the object to be examined <P>
     * @param data the data object to be examined <P> 
     *
     * @return If the data is a string: number of characters in the string<BR>
     *         If the data is an array:  number of elements in the array <BR>
     *         Otherwise: 1 <P>
     * @exception CDFException if a problem occurs getting the number of
     *            elements
     */
    public static long getNumElements(long dataType, 
				      Object data)
	throws CDFException {
	if ((dataType == CDF_CHAR) || (dataType == CDF_UCHAR)) {
            String signature = CDFUtils.getSignature(data);
            if (signature.indexOf("String") != -1) {
              try {
                byte[] bytes = ((String)data).getBytes("UTF-8");
                return (bytes.length);
              } catch (Exception exp) {
	        return((String)data).length();
              }
            }
            else if (signature.indexOf("[B") != -1)
              return((byte[])data).length;
            else return 1;
	}
	else {
	    int which = (dataType == CDF_EPOCH16 ? 2 : 1);
	    String signature = CDFUtils.getSignature(data);
	    if (signature.charAt(0) == '[')    {           // it is an array
		switch (signature.charAt(1)) {
		case 'B':
		    return((byte [])data).length/which;
		case 'S':
		    return((short [])data).length/which;
		case 'I':
		    return((int [])data).length/which;
		case 'J':
		    return((long [])data).length/which;
		case 'F':
		    return((float [])data).length/which;
		case 'D':
		    return((double [])data).length/which;
		default: throw new CDFException(BAD_ARGUMENT);
		}
	    }
	    else
		return 1L;
	}
    }

    /**
     * Gets the number of strings contained in the given data object.  <P>
     *
     * @param data the data object to be examined <P> 
     *
     * @return The number of strings if the object is of string type and the
     *         data has the predefined STRINGDELIMITER (as "\\N ") delimiter
     *         in it. Or, 0 if non-string datat type. 
     */
    public static long getNumStrings(Object data) {
        String signature = CDFUtils.getSignature(data).toLowerCase();
        if (signature.charAt(0) == '[' &&
            signature.indexOf("string") != -1) {
          return ((String[])data).length;
        } else if (signature.indexOf("string") != -1) {
            long numStrings = 1;
            int ix;
            StringBuffer tmp = new StringBuffer((String)data.toString());
            while ((ix = tmp.indexOf(STRINGDELIMITER)) != -1) {
              tmp = new StringBuffer(tmp.substring(ix+STRINGDELIMITER.length()));
              ++numStrings;
            }
            return numStrings;
	} else 
          return 0L;
    }

    /**
     *  Prints the value of the given data on the screen.  Data can be a 
     *  java primitive data type, Java Object (non-array), or 
     *  1-dimensional array of primitive Java data type. 
     *
     *  @param data the data to be printed
     *
     */
    public static void printData (Object data) {

	CDFUtils.printData(data, 0, true, "");
    }

    /**
     *  Prints the value of the given data on the screen.  Data can be a
     *  java primitive data type, Java Object (non-array), or
     *  1-dimensional array of primitive Java data type.
     *
     *  @param data the data to be printed
     *  @param which the Epoch data type data indicator
     *               1 if CDF_EPOCH, 2 if CDF_EPOCH16 or 3 if CDF_TIME_TT2000
     *               0 Otherwise
     *  @param iso8601 the ISO 8601 indicator for EPOCH data
     *
     */
    public static void printData (Object data, int which, boolean iso8601) {

        CDFUtils.printData(data, which, iso8601, "");
    }
/*
    public static void printMultiData (Object data) {
        CDFUtils.printMultiData(data, 0, true, "");
    }

    public static void printMultiData (Object data, int which, boolean iso8601) {
        CDFUtils.printMultiData(data, which, iso8601, "");
    }

    public static void printMultiData (Object data, int which, boolean iso8601,
                                       String addZ) {

        if (data == null) return;
        int nDims = CDFUtils.getNumDimensions(data);
        if (nDims < 2) {
          printData (data, which, iso8601, addZ);
          System.out.println("");
        } else {
          int arrayLength = ((Object[])data).length;
          int i;
          Object subObj;
          for (i = 0; i < arrayLength; ++i) {
            subObj = ((Object[])data)[i];
            nDims = CDFUtils.getNumDimensions(subObj);
            if (nDims > 1)
              printMultiData (subObj, which, iso8601, addZ);
            else {
              printData (subObj, which, iso8601, addZ);
              System.out.println("");
            }
          }
        }

    }
*/
    /**
     *  Prints the value of the given data on the screen.  Data can be a
     *  java primitive data type, Java Object (non-array), or one or
     *  multi-dimensional array of primitive Java data type.
     *
     *  @param data the data to be printed
     *  @param which the Epoch data type data indicator
     *               1 if CDF_EPOCH, 2 if CDF_EPOCH16 or 3 if CDF_TIME_TT2000
     *               0 Otherwise
     *  @param iso8601 the ISO 8601 indicator for EPOCH data
     *  @param addZ Added the passed string to the end, (likely a "Z", to
     *              all CDF epoch data types, not just CDF_TIME_TT2000)
     *
     */
    public static void printData (Object data, int which, boolean iso8601,
                                  String addZ) {

        if (data == null) return;
        int nDims = CDFUtils.getNumDimensions(data);
        if (nDims > 1) {
          int arrayLength = ((Object[])data).length;
          int i;
          Object subObj;
          for (i = 0; i < arrayLength; ++i) {
            subObj = ((Object[])data)[i];
            printData (subObj, which, iso8601, addZ);
//            System.out.println("");
          }
        } else {
          /* Scalar or 1-D data */
          int  i, arrayLength, jj = 1;
          String signature = CDFUtils.getSignature(data);
          if (which == 2) jj = 2;
          if (signature.charAt(0) == '[') {
            arrayLength = Array.getLength(data);
            for (i=0; i < arrayLength; i=i+jj) {
                 if (signature.charAt(1) == 'B')
                     System.out.print (Array.getByte(data,i)+
                                       (i==(arrayLength-jj)?"":" "));

                 else if (signature.charAt(1) == 'S')
                     System.out.print (Array.getShort(data,i)+
                                       (i==(arrayLength-jj)?"":" "));

                 else if (signature.charAt(1) == 'I')
                     System.out.print (Array.getInt(data,i)+
                                       (i==(arrayLength-jj)?"":" "));

                 else if (signature.charAt(1) == 'J')
                     if (which == 0)
                       System.out.print (Array.getLong(data,i)+
                                         (i==(arrayLength-jj)?"":" "));
                     else
                       System.out.print (CDFTT2000.encode(Array.getLong(data,i))+
                                         addZ+(i==(arrayLength-jj)?"":" "));

                 else if (signature.charAt(1) == 'F')
                     System.out.print (Array.getFloat(data,i)+
                                       (i==(arrayLength-jj)?"":" "));

                 else if (signature.charAt(1) == 'D') {
		   if (which == 1) {
		     System.out.print (((!iso8601)?
                                        Epoch.encode(Array.getDouble(data,i)):
                                        (Epoch.encode4(Array.getDouble(data,i))+
                                         addZ))+(i==(arrayLength-jj)?"":" "));
                   } else if (which == 2) {
                     double[] mmm = new double[2];
                     mmm[0] = Array.getDouble(data,i);
                     mmm[1] = Array.getDouble(data,i+1);
                     System.out.print (((!iso8601)?
                                        Epoch16.encode(mmm):
                                        (Epoch16.encode4(mmm)+
                                         addZ))+(arrayLength==2?"":" "));
		   } else
                     System.out.print (Array.getDouble(data,i)+
                                       (i==(arrayLength-jj)?"":" "));
                 } else {
                     System.out.print("\"");
                     System.out.print(Array.get((String[])data, i));
                     System.out.print("\" ");
//                     if (i<(arrayLength-1)) System.out.print("\" ");
                 }
//                 System.out.println("");
            }
            System.out.println("");
          }
          else {
	    if (which == 1) 
              System.out.println ((!iso8601)?
                                Epoch.encode(((Double)data).doubleValue()):
                                Epoch.encode4(((Double)data).doubleValue())+addZ);
            else if (which == 3)
              System.out.println (CDFTT2000.encode(((Long)data).longValue())+addZ);
            else {
              if (data instanceof String) {
		int len = ((String)data).length();
		if (len == 1) {
		  byte[] bytes = ((String)data).getBytes(); 
		  if (bytes[0] == 0)
                    System.out.println ("\"\"");
		  else
		    System.out.println ("\""+data+"\"");
		} else
                  System.out.println ("\""+data+"\"");
              } else
                System.out.println (data);
            }
	  }
        }
    }


    /**
     *  Prints the value of the given data on the screen.  Data can be a
     *  java primitive data type, Java Object (non-array), or
     *  1-dimensional array of primitive Java data type.
     *
     *  @param data the data to be printed
     *  @param which the Epoch data type data indicator
     *               1 if CDF_EPOCH, 2 if CDF_EPOCH16 or 3 if CDF_TIME_TT2000
     *               0 Otherwise
     *
     */
    public static void printData (Object data, int which) {

        CDFUtils.printData (data, which, true);
    }

    /**
     *  Prints the value of the given data on the screen.  Data can be a
     *  java primitive data type, Java Object (non-array), or
     *  1-dimensional array of primitive Java data type.
     *
     *  @param data the data to be printed
     *  @param dataType the CDF data type of the data
     *
     */
    public static void printData (Object data, long dataType) {

	if (dataType == CDF_EPOCH)
          CDFUtils.printData (data, 1);
	else if (dataType == CDF_EPOCH16)
          CDFUtils.printData (data, 2);
	else if (dataType == CDF_TIME_TT2000)
          CDFUtils.printData (data, 3);
	else
          CDFUtils.printData (data, 0);
    }

    /**
     *  Prints the value of the given data to the place designated by
     *  PrintWriter that can be a file, Sysem.out, System.err, and etc.  
     *  Data can be a java primitive data type, Java Object (non-array), or 
     *  1-dimensional array of primitive Java data type. <P>
     *
     *  The following example will send the contents of the given data
     *  to "myoutput.dat". 
     *  <PRE>
     *     OutputStreamWriter outWriter = null;
     *     PrintWriter out = null;
     *     try {
     *          outWriter = new OutputStreamWriter("myoutput.dat", "UTF-8");
     *          out = new PrintWriter(outWriter, true);
     *     } catch (Exception e) {
     *         System.out.println ("Exception occurred: "+e);
     *     }
     *     CDFUtils.printData (data, out);
     *  </PRE>
     *
     *  @param data the data to be printed <P>
     *  @param outWriter the print writer to which formatted representations 
     *                   of the object/data is printed as a text-output stream
     */
    public static void printData (Object data, PrintWriter outWriter) {

	CDFUtils.printData(data, outWriter, 0, true);
    }

    /**
     *  Prints the value of the given data to the place designated by
     *  PrintWriter that can be a file, Sysem.out, System.err, and etc.  
     *  Data can be a java primitive data type, Java Object (non-array), or 
     *  1-dimensional array of primitive Java data type. <P>
     *
     *  @param data the data to be printed <P>
     *  @param outWriter the print writer to which formatted representations 
     *                   of the object/data is printed as a text-output stream
     *  @param which the Epoch data type data indicator
     *               1 if CDF_EPOCH, 2 if CDF_EPOCH16 or 3 if CDF_TIME_TT2000
     *               0 Otherwise
     */
    public static void printData (Object data, PrintWriter outWriter,
				  int which) {
        CDFUtils.printData(data, outWriter, which, true);
    }

    /**
     *  Prints the value of the given data to the place designated by
     *  PrintWriter that can be a file, Sysem.out, System.err, and etc.  
     *  Data can be a java primitive data type, Java Object (non-array), or 
     *  1-dimensional array of primitive Java data type. <P>
     *
     *  @param data the data to be printed <P>
     *  @param outWriter the print writer to which formatted representations 
     *                   of the object/data is printed as a text-output stream
     *  @param which the Epoch data type data indicator
     *               1 if CDF_EPOCH, 2 if CDF_EPOCH16 or 3 if CDF_TIME_TT2000
     *               0 Otherwise
     *  @param iso8601 the ISO 8601 indicator for EPOCH data
     */
    public static void printData (Object data, PrintWriter outWriter,
				  int which, boolean iso8601) {

        CDFUtils.printData(data, outWriter, which, iso8601, "");
    }

    /**
     *  Prints the value of the given data to the place designated by
     *  PrintWriter that can be a file, Sysem.out, System.err, and etc.  
     *  Data can be a java primitive data type, Java Object (non-array), or 
     *  1-dimensional array of primitive Java data type. <P>
     *
     *  @param data the data to be printed <P>
     *  @param outWriter the print writer to which formatted representations 
     *                   of the object/data is printed as a text-output stream
     *  @param which the Epoch data type data indicator
     *               1 if CDF_EPOCH, 2 if CDF_EPOCH16 or 3 if CDF_TIME_TT2000
     *  @param iso8601 the ISO 8601 indicator for EPOCH data
     *  @param addZ Added the passed string to the end of an ISO8601 format
     *              string, (likely a "Z", to all CDF epoch data types, not 
     *              just CDF_TIME_TT2000).
     */
    public static void printData (Object data, PrintWriter outWriter,
				  int which, boolean iso8601, String addZ) {

        int  i, arrayLength, jj = 1;
        String signature = CDFUtils.getSignature(data);
	if (which == 2) jj = 2;

        if (signature.charAt(0) == '[') {
            arrayLength = Array.getLength(data);
            for (i=0; i < arrayLength; i=i+jj) {
                 if (signature.charAt(1) == 'B')
                     outWriter.print (Array.getByte(data,i)+" ");

                 else if (signature.charAt(1) == 'S')
                     outWriter.print (Array.getShort(data,i)+" ");

                 else if (signature.charAt(1) == 'I')
                     outWriter.print (Array.getInt(data,i)+" ");

                 else if (signature.charAt(1) == 'J')
                     if (which == 0)
                       outWriter.print (Array.getLong(data,i)+" ");
                     else
                       outWriter.print (CDFTT2000.encode(Array.getLong(data,i))+
                                        addZ+" ");

                 else if (signature.charAt(1) == 'F')
                     outWriter.print (Array.getFloat(data,i)+" ");

                 else if (signature.charAt(1) == 'D') {
                     if (which == 1) {
                       outWriter.print (((!iso8601)?
                                         Epoch.encode(Array.getDouble(data,i)):
                                         (Epoch.encode4(Array.getDouble(data,i))+
                                          addZ))+" ");
                     } else if (which == 2) {
                       double[] mmm = new double[2];
                       mmm[0] = Array.getDouble(data,i);
                       mmm[1] = Array.getDouble(data,i+1);
                       outWriter.print (((!iso8601)?
                                         Epoch16.encode(mmm):
                                         (Epoch16.encode4(mmm)+
                                          addZ))+(arrayLength==2?"":" "));
                     } else
                       outWriter.print (Array.getDouble(data,i)+" ");
		 }
            }
        }
        else {
	   if (which == 1)
	     outWriter.print ((!iso8601)?
                               Epoch.encode(((Double)data).doubleValue()):
                               (Epoch.encode4(((Double)data).doubleValue())+addZ));
           else if (which == 3)
             outWriter.print (CDFTT2000.encode(((Long)data).longValue())+addZ);
           else
	     outWriter.print (data);
	}
    }


    /**
     *  Prints the value of the given data on the screen.  Data can be a 
     *  java primitive data type, Java Object (non-array), or 
     *  1-dimensional array of primitive Java data type. The format is mainly
     *  applicable to floating point typed data.
     *
     *  @param data the data to be printed
     *  @param format the format specifically for the floating point data
     *
     */
    public static void printDataWithFormat (Object data, String format) {

	CDFUtils.printDataWithFormat(data, format, 0, true);
    }

    /**
     *  Prints the value of the given data on the screen.  Data can be a
     *  java primitive data type, Java Object (non-array), or
     *  1-dimensional array of primitive Java data type.
     *  Note: The format is mainly used for floating point data.
     *
     *  @param data the data to be printed
     *  @param format the format specifically for the floating point data
     *  @param which the Epoch data type data indicator
     *               1 if CDF_EPOCH, 2 if CDF_EPOCH16 or 3 if CDF_TIME_TT2000
     *               0 otherwise
     *  @param iso8601 the ISO 8601 indicator for EPOCH data
     *
     */
    public static void printDataWithFormat (Object data, String format,
                                            int which, boolean iso8601) {

        CDFUtils.printDataWithFormat(data, format, which, iso8601, "");
    }

    /**
     *  Prints the value of the given data on the screen.  Data can be a
     *  java primitive data type, Java Object (non-array), or
     *  1-dimensional array of primitive Java data type.
     *  Note: The format is mainly used for floating point data.
     *
     *  @param data the data to be printed
     *  @param format the format specifically for the floating point data
     *  @param which the Epoch data type data indicator
     *               1 if CDF_EPOCH, 2 if CDF_EPOCH16 or 3 if CDF_TIME_TT2000
     *  @param iso8601 the ISO 8601 indicator for EPOCH data
     *  @param addZ Added the passed string to the end, (likely a "Z", to
     *              all CDF epoch data types, not just CDF_TIME_TT2000)
     *
     */
    public static void printDataWithFormat (Object data, String format,
                                            int which, boolean iso8601,
                                            String addZ) {

        if (format == null) {
          CDFUtils.printData(data, which, iso8601, addZ);
          return;
        }
        String jFormat = CDFUtils.CFtoJformat(format);
        if (jFormat == null || CDFUtils.isFloatingPadValue (data) == 1) {
          CDFUtils.printData (data, which, iso8601, addZ);
          return;
        }
        int  i, arrayLength, jj = 1;
        String signature = CDFUtils.getSignature(data);
	if (which == 2) jj = 2;
        if (signature.charAt(0) == '[') {
            arrayLength = Array.getLength(data);
            for (i=0; i < arrayLength; i=i+jj) {
                 if (signature.charAt(1) == 'B') {
                     System.out.print (Array.getByte(data,i)+
                                       (i==(arrayLength-jj)?"":" "));

                 } else if (signature.charAt(1) == 'S') {
                     System.out.print (Array.getShort(data,i)+
                                       (i==(arrayLength-jj)?"":" "));


                 } else if (signature.charAt(1) == 'I') {
                     System.out.print (Array.getInt(data,i)+
                                       (i==(arrayLength-jj)?"":" "));


                 } else if (signature.charAt(1) == 'J') {
                     if (which == 0) {
                         System.out.print (Array.getLong(data,i)+
                                           (i==(arrayLength-jj)?"":" "));

                     } else
                       System.out.print (CDFTT2000.encode(Array.getLong(data,i))+
                                         addZ+(i==(arrayLength-jj)?"":" "));

                 } else if (signature.charAt(1) == 'F') {
                       float value = Array.getFloat(data,i);
                       if (CDFUtils.isFloatingPadValue(value) == 0)
                         System.out.format (jFormat+(i==(arrayLength-jj)?"":" "),
                                            value);
                       else
                         System.out.print (value+(i==(arrayLength-jj)?"":" "));

                 } else if (signature.charAt(1) == 'D') {
		   if (which == 1) {
		     System.out.print (((!iso8601)?
                                        Epoch.encode(Array.getDouble(data,i)):
                                        (Epoch.encode4(Array.getDouble(data,i))+
                                         addZ))+(i==(arrayLength-jj)?"":" "));
                   } else if (which == 2) {
                     double[] mmm = new double[2];
                     mmm[0] = Array.getDouble(data,i);
                     mmm[1] = Array.getDouble(data,i+1);
                     System.out.print (((!iso8601)?
                                        Epoch16.encode(mmm):
                                        (Epoch16.encode4(mmm)+
                                         addZ))+(arrayLength==2?"":" "));
		   } else {
                        double value = Array.getDouble(data,i);
                       if (CDFUtils.isFloatingPadValue(value) == 0)
                         System.out.format (jFormat+(i==(arrayLength-jj)?"":" "),
                                            value);
                       else
                         System.out.print (value+(i==(arrayLength-jj)?"":" "));
                   }
                 } else {
                     if (i>0) System.out.print(" \"");
                     System.out.print (Array.get((String[])data, i));
                     if (i<(arrayLength-1)) System.out.print("\" ");
                 }
            }
        }
        else {
	   if (which == 1) 
             System.out.print ((!iso8601)?
                               Epoch.encode(((Double)data).doubleValue()):
                               Epoch.encode4(((Double)data).doubleValue())+addZ);
           else if (which == 3)
             System.out.print (CDFTT2000.encode(((Long)data).longValue())+addZ);
           else {
               if ((signature.indexOf("Float") != -1 ||
                    signature.indexOf("Double") != -1)  &&
                   CDFUtils.isFloatingPadValue(data) == 0)
                 System.out.format (jFormat,data);
               else
                 System.out.println (data);

           }
	}
    }


    /**
     *  Prints the value of the given data on the screen.  Data can be a
     *  java primitive data type, Java Object (non-array), or
     *  1-dimensional array of primitive Java data type.
     *
     *  @param data the data to be printed
     *  @param format the format specifically for the floating point data
     *  @param which the Epoch data type data indicator
     *               1 if CDF_EPOCH, 2 if CDF_EPOCH16 or 3 if CDF_TIME_TT2000
     *               0 otherwise
     *
     */
    public static void printDataWithFormat (Object data, String format,
                                            int which) {

        CDFUtils.printDataWithFormat (data, format, which, false);
    }

    /**
     *  Prints the value of the given data to the place designated by
     *  PrintWriter that can be a file, Sysem.out, System.err, and etc.  
     *  Data can be a java primitive data type, Java Object (non-array), or 
     *  1-dimensional array of primitive Java data type. <P>
     *
     *  The following example will send the contents of the given data
     *  to "myoutput.dat". 
     *  <PRE>
     *     OutputStreamWriter outWriter = null;
     *     PrintWriter out = null;
     *     try {
     *          outWriter = new OutputStreamWriter("myoutput.dat", "UTF-8");
     *          out = new PrintWriter(outWriter, true);
     *     } catch (Exception e) {
     *         System.out.println ("Exception occurred: "+e);
     *     }
     *     CDFUtils.printData (data, out);
     *  </PRE>
     *
     *  @param data the data to be printed <P>
     *  @param format the format specifically for the floating point data
     *  @param outWriter the print writer to which formatted representations 
     *                   of the object/data is printed as a text-output stream
     */
    public static void printDataWithFormat (Object data, String format,
                                            PrintWriter outWriter) {

	CDFUtils.printDataWithFormat(data, format, outWriter, 0, false);
    }

    /**
     *  Prints the value of the given data to the place designated by
     *  PrintWriter that can be a file, Sysem.out, System.err, and etc.  
     *  Data can be a java primitive data type, Java Object (non-array), or 
     *  1-dimensional array of primitive Java data type. <P>
     *
     *  @param data the data to be printed <P>
     *  @param format the format specifically for the floating point data
     *  @param outWriter the print writer to which formatted representations 
     *                   of the object/data is printed as a text-output stream
     *  @param which the Epoch data type data indicator
     *               1 if CDF_EPOCH, 2 if CDF_EPOCH16 or 3 if CDF_TIME_TT2000
     */
    public static void printDataWithFormat (Object data, String format,
                                            PrintWriter outWriter,
				            int which) {
        CDFUtils.printDataWithFormat(data, format, outWriter, which, false);
    }

    /**
     *  Prints the value of the given data to the place designated by
     *  PrintWriter that can be a file, Sysem.out, System.err, and etc.  
     *  Data can be a java primitive data type, Java Object (non-array), or 
     *  1-dimensional array of primitive Java data type. <P>
     *
     *  @param data the data to be printed <P>
     *  @param format the format specifically for the floating point data
     *  @param outWriter the print writer to which formatted representations 
     *                   of the object/data is printed as a text-output stream
     *  @param which the Epoch data type data indicator
     *               1 if CDF_EPOCH, 2 if CDF_EPOCH16 or 3 if CDF_TIME_TT2000
     *  @param iso8601 the ISO 8601 indicator for EPOCH data
     */
    public static void printDataWithFormat (Object data, String format,
                                            PrintWriter outWriter,
				            int which, boolean iso8601) {

        CDFUtils.printDataWithFormat(data, format, outWriter, which, iso8601, "");
    }

    /**
     *  Prints the value of the given data to the place designated by
     *  PrintWriter that can be a file, Sysem.out, System.err, and etc.  
     *  Data can be a java primitive data type, Java Object (non-array), or 
     *  1-dimensional array of primitive Java data type. <P>
     *
     *  @param data the data to be printed <P>
     *  @param format the format specifically for the floating point data
     *  @param outWriter the print writer to which formatted representations 
     *                   of the object/data is printed as a text-output stream
     *  @param which the Epoch data type data indicator
     *               1 if CDF_EPOCH, 2 if CDF_EPOCH16 or 3 if CDF_TIME_TT2000
     *  @param iso8601 the ISO 8601 indicator for EPOCH data
     *  @param addZ Added the passed string to the end of an ISO8601 format
     *              string, (likely a "Z", to all CDF epoch data types, not 
     *              just CDF_TIME_TT2000).
     */
    public static void printDataWithFormat (Object data, String format,
                                            PrintWriter outWriter, int which,
				            boolean iso8601, String addZ) {

        if (format == null) {
            CDFUtils.printData (data, outWriter, which, iso8601, addZ);
            return;
        }
        String jFormat = CDFUtils.CFtoJformat(format);
        if (jFormat == null || CDFUtils.isFloatingPadValue (data) == 1) {
            CDFUtils.printData (data, outWriter, which, iso8601, addZ);
            return;
        }
        int  i, arrayLength, jj = 1;
        String signature = CDFUtils.getSignature(data);
	if (which == 2) jj = 2;

        if (signature.charAt(0) == '[') {
            arrayLength = Array.getLength(data);
            for (i=0; i < arrayLength; i=i+jj) {
                 if (signature.charAt(1) == 'B') {
                     outWriter.print (Array.getByte(data,i)+" ");

                 } else if (signature.charAt(1) == 'S') {
                     outWriter.print (Array.getShort(data,i)+" ");

                 } else if (signature.charAt(1) == 'I') {
                     outWriter.print (Array.getInt(data,i)+" ");

                 } else if (signature.charAt(1) == 'J') {
                     if (which == 0) {
                         outWriter.print (Array.getLong(data,i)+" ");
                     } else
                       outWriter.print (CDFTT2000.encode(Array.getLong(data,i))+
                                        addZ+" ");

                 } else if (signature.charAt(1) == 'F') {
                       float value = Array.getFloat(data,i);
                       if (CDFUtils.isFloatingPadValue(value) == 0)
                         outWriter.format (jFormat+" ", value);
                       else
                         outWriter.format (value+" ");

                 } else if (signature.charAt(1) == 'D') {
                     if (which == 1) {
                       outWriter.print (((!iso8601)?
                                         Epoch.encode(Array.getDouble(data,i)):
                                         (Epoch.encode4(Array.getDouble(data,i))+
                                          addZ))+" ");
                     } else if (which == 2) {
                       double[] mmm = new double[2];
                       mmm[0] = Array.getDouble(data,i);
                       mmm[1] = Array.getDouble(data,i+1);
                       outWriter.print (((!iso8601)?
                                         Epoch16.encode(mmm):
                                         (Epoch16.encode4(mmm)+
                                          addZ))+(arrayLength==2?"":" "));
                     } else {
                         double value = Array.getDouble(data,i);
                         if (CDFUtils.isFloatingPadValue(value) == 0)
                           outWriter.format (jFormat+" ", Array.getDouble(data,i));
                         else
                           outWriter.print (value+" ");
                     }
		 }
            }
        }
        else {
	   if (which == 1)
	     outWriter.print ((!iso8601)?
                               Epoch.encode(((Double)data).doubleValue()):
                               (Epoch.encode4(((Double)data).doubleValue())+addZ));
           else if (which == 3)
             outWriter.print (CDFTT2000.encode(((Long)data).longValue())+addZ);
           else {
               if ((signature.indexOf("Float") != -1 || 
                    signature.indexOf("Double") != -1)  &&
                   CDFUtils.isFloatingPadValue(data) == 0)
                 outWriter.format (jFormat+" ", data);
               else
                 outWriter.print (data+" ");
           }
	}
    }


    /**
     *  Returns the string value of the given data.
     *  Data can be a java primitive
     *  data type, Java Object (non-array), or 1-dimensional array of
     *  primitive Java data type. <P>
     *
     *  @param data the data to be parsed <P>
     *  @return The string value of the given data/object.  <BR>
     *          If the data is an array, its elements are delimited by a space.
     *
     */
    public static String getStringData (Object data) {

	return CDFUtils.getStringData(data, " ", 0);
    }

    /**
     *  Merge an array of strings into a single string with "\ N ",
     *  a 3-char separator. <P>
     *
     *  @param data the strings to be merged. <P>
     *  @return The merged string. <BR>
     *          If the input is any single string, then it returns as is.
     *
     */
    public static String mergeFromStrings (Object data) {

        long numStrings = CDFUtils.getNumStrings(data);
        if (numStrings > 1) {
          StringBuffer tmp = new StringBuffer();
          int ix;
          for (ix = 0; ix < numStrings; ++ix) {
            tmp.append(((String[])data)[ix]);
            if (ix != (numStrings-1)) tmp.append(STRINGDELIMITER);
          }
          return tmp.toString();
        } else
//          return (String)data;
          try {
            return new String(((String)data).getBytes(), "UTF-8");
          } catch (Exception ee) {
            return (String)data;
          }
    }

    /**
     *  Break down a merged string into their original array of strings. 
     *  This is a reversed method of MergeStrings. The mergeed string uses
     *  "\ N ", a 3-char, as the separator. <P>
     *
     *  @param data the merged string to be broken down. <P>
     *  @return An array of strings. <BR>
     *          If the input string has no separator, it is a single string.
     *          The returned array will have only one element.
     *
     */
    public static String[] breakIntoStrings (String data) {

        long numStrings = CDFUtils.getNumStrings(data);
        String[] data2 = new String[(int)numStrings];
        int ix;
        StringBuffer tmp = new StringBuffer(data);
        for (ix = 0; ix < numStrings; ++ix) {
           int dstr = tmp.indexOf(STRINGDELIMITER);
           if (dstr != -1) {
             data2[ix] = tmp.substring(0, dstr).toString();
             tmp = new StringBuffer(tmp.substring(dstr+STRINGDELIMITER.length()));
           } else {
             data2[ix] = new String(tmp);
           }
        }
        return data2;
    }

    /**
     *  Returns the string value of the given data.
     *  Data can be a java primitive
     *  data type, Java Object (non-array), or 1-dimensional array of
     *  primitive Java data type. <P>
     *
     *  @param data the data to be encoded. <P>
     *  @param epochType epoch type indicator (==1 CDF_EPOCH, ==2 CDF_EPOCH16, ==0 others) <P>
     *  @return The string value of the given data/object.  <BR>
     *          If the data is an array, its elements are delimited by a space.
     *
     */
    public static String getStringData (Object data, int epochType) {

        return CDFUtils.getStringData(data, " ", epochType);
    }

    /**
     *  Returns the string of the value of the given data.  
     *  Data can be a java primitive
     *  data type, Java Object (non-array), or 1-dimensional array of
     *  primitive Java data type. <P>
     *
     *  @param data the data to be encoded. <P>
     *  @param separator the delimiter for array elements. <P>
     *  @return The string value of the given data/object. <BR>
     *          If the data is an array, its elements are delimited by 
     *          the user defined separator. 
     *
     */
    public static String getStringData (Object data, String separator) {

	return CDFUtils.getStringData(data, separator, 0);
    }

    /**
     *  Returns the string of the value of the given data.  
     *  Data can be a java primitive
     *  data type, Java Object (non-array), or 1-dimensional array of
     *  primitive Java data type. <P>
     *
     *  @param data the data to be encoded. <P>
     *  @param separator the delimiter for array elements. <P>
     *  @param epochType Epoch or Epoch16 data type indicator <BR>
     *         == 1 for EPOCH, == 2 for EPOCH16, == 3 for TT2000, 
     *         == 0 for other data types <P>
     *  @return The string value of the given data/object. <BR>
     *          If the data is an array, its elements are delimited by 
     *          the user defined separator. 
     *
     */
    public static String getStringData (Object data, String separator,
                                        int epochType) {

        return CDFUtils.getStringData(data, separator, 0, "");
    }

    /**
     *  Returns the string of the value of the given data.  
     *  Data can be a java primitive
     *  data type, Java Object (non-array), or 1-dimensional array of
     *  primitive Java data type. <P>
     *
     *  @param data the data to be encoded. <P>
     *  @param separator the delimiter for array elements. <P>
     *  @param epochType Epoch or Epoch16 data type indicator <BR>
     *         == 1 for EPOCH, == 2 for EPOCH16, == 3 for TT2000, 
     *         == 0 for other data types <P>
     *  @param addZ Added the passed string to the end, likely a "Z", to
     *              CDF_TIME_TT2000 data type
     *  @return The string value of the given data/object. <BR>
     *          If the data is an array, its elements are delimited by 
     *          the user defined separator. 
     *
     */
    public static String getStringData (Object data, String separator,
                                        int epochType, String addZ) {
        int  i, arrayLength, jj;
        String signature = CDFUtils.getSignature(data);
	StringBuffer buffer = new StringBuffer();

	if (epochType == 2) jj = 2;
	else jj = 1;
        if (signature.charAt(0) == '[') {
            arrayLength = Array.getLength(data);
            for (i=0; i < arrayLength; i=i+jj) {
                 if (i > 0) buffer.append(separator);
                 if (signature.charAt(1) == 'B')
		   buffer.append(Array.getByte(data,i));

                 else if (signature.charAt(1) == 'S')
		   buffer.append(Array.getShort(data,i));

                 else if (signature.charAt(1) == 'I')
		   buffer.append(Array.getInt(data,i));

                 else if (signature.charAt(1) == 'J')
                   if (epochType == 0)
		     buffer.append(Array.getLong(data,i));
                   else {
                     String tmp = CDFTT2000.encode(Array.getLong(data,i));
                     buffer.append(tmp);
                   }

                 else if (signature.charAt(1) == 'F')
		   buffer.append(Array.getFloat(data,i));

                 else if (signature.charAt(1) == 'D') {
		   if (epochType == 1) {
		     String tmp = Epoch.encode(Array.getDouble(data,i));
		     buffer.append(tmp);
		   } else if (epochType == 2) {
		     double[] mmm = new double[2];
		     mmm[0] = Array.getDouble(data,i);
		     mmm[1] = Array.getDouble(data,i+1);
		     String tmp = Epoch16.encode(mmm);
		     buffer.append(tmp);
		   } else
		     buffer.append(Array.getDouble(data,i));
		 }
                 else if (signature.charAt(1) == 'C')
                   buffer.append(Array.getChar(data,i));
		 else if (signature.indexOf("String") != -1)
		   buffer.append(((String[])data)[i]);
	    }
//	     buffer.setLength(buffer.length() - separator.length());
        } else {
                 if (signature.indexOf("Byte") != -1)    
                   buffer.append(((Byte)data).toString());
    
                 else if (signature.indexOf("Short") != -1)    
                   buffer.append(((Short)data).toString());
    
                 else if (signature.indexOf("Integer") != -1)    
                   buffer.append(((Integer)data).toString());

                 else if (signature.indexOf("Long") != -1)
                   if (epochType == 0)
                     buffer.append(((Long)data).toString());
                   else
                     buffer.append(CDFTT2000.encode(((Long)data).longValue())+addZ);

                 else if (signature.indexOf("Float") != -1)
                   buffer.append(((Float)data).toString());

                 else if (signature.indexOf("Double") != -1) {
		   if (epochType == 1) {
                     String tmp = Epoch.encode(((Double)data).doubleValue());
                     buffer.append(tmp);
                   } else
                     buffer.append(((Double)data).toString());

                 } else if (signature.indexOf("Character") != -1)
                   buffer.append(((Character)data).toString());

                 else if (signature.indexOf("String") != -1)
                   buffer.append(((String)data));

	}
        return buffer.toString();
    }

    /**
     *  Returns the string of the value of the given data.  
     *  Data can be a java primitive data type, Java Object (non-array), or
     *  1-dimensional array of primitive or Java data type. The data shall
     *  be one of CDF numerical data types. The value
     *  will be formatted in format form if it is presented.
     *  <P>
     *
     *  @param data the data to be encoded. <P>
     *  @param format the data format to be used. <P>
     *  @param separator the delimiter for array elements. <P>
     *  @return The string value of the given data/object. <BR>
     *          If the data is an array, its elements are delimited by 
     *          the user defined separator. 
     *
     */
    public static String getStringFloatingData (Object data, String format,
                                                String separator) {
        int  i, arrayLength;
        String signature = CDFUtils.getSignature(data);
	StringBuffer buffer = new StringBuffer();

        if (signature.charAt(0) == '[') {
            arrayLength = Array.getLength(data);
            for (i=0; i < arrayLength; ++i) {
                 if (i > 0) buffer.append(separator);
                 if (signature.charAt(1) == 'B')
		   buffer.append(Array.getByte(data,i));

                 else if (signature.charAt(1) == 'S')
		   buffer.append(Array.getShort(data,i));

                 else if (signature.charAt(1) == 'I')
		   buffer.append(Array.getInt(data,i));

                 else if (signature.charAt(1) == 'J')
		   buffer.append(Array.getLong(data,i));

                 else if (signature.charAt(1) == 'F') {
                   float value = Array.getFloat(data,i);
                   if (format == null ||
                       isFloatingPadValue(value) == 1)
                       buffer.append(value);
                     else {
                       format = CDFUtils.CFtoJformat (format);
                       buffer.append(String.format(format, value));
                     }

                 } else if (signature.charAt(1) == 'D') {
                   double value = Array.getDouble(data,i);
                   if (format == null ||
                       isFloatingPadValue(value) == 1)
                       buffer.append(value);
                     else {
                       format = CDFUtils.CFtoJformat (format);
                       buffer.append(String.format(format, value));
                     }
		 }
	    }
        } else {
                 if (signature.indexOf("Byte") != -1)    
                   buffer.append(((Byte)data).toString());
    
                 else if (signature.indexOf("Short") != -1)    
                   buffer.append(((Short)data).toString());
    
                 else if (signature.indexOf("Integer") != -1)    
                   buffer.append(((Integer)data).toString());

                 else if (signature.indexOf("Long") != -1)
                     buffer.append(((Long)data).toString());

                 else if (signature.indexOf("Float") != -1) {
                   float value = ((Float)data).floatValue();
                   if (format == null ||
                       isFloatingPadValue(value) == 1)
                       buffer.append(value);
                     else {
                       format = CDFUtils.CFtoJformat (format);
                       buffer.append(String.format(format, value));
                     }

                 } else if (signature.indexOf("Double") != -1) {
                   double value = ((Double)data).doubleValue();
                   if (format == null ||
                       isFloatingPadValue(value) == 1)
                       buffer.append(value);
                     else {
                       format = CDFUtils.CFtoJformat (format);
                       buffer.append(String.format(format, value));
                     }
                 }

	}
        return buffer.toString();
    }

    /**
     * Gets the string value of the CDF data type for the given variable. <P>
     *
     * @param var the CDF variable to be examined <P>
     * @return See getStringDataType (long cdfDataType) for possible return
     *         values. 
     */
    public static String getStringDataType(Variable var) {
	return CDFUtils.getStringDataType(var.getDataType());
    }


    /**
     * Gets the string value of the CDF data type for the given entry. <P>
     *
     * @param entry the entry to be examined <P>
     * @return String representation of the entry's CDF data type.  
     *         See getStringDataType (long cdfDataType) for possible return
     *         values. 
     */
    public static String getStringDataType(Entry entry) {
        return CDFUtils.getStringDataType(entry.getDataType());
    }


    /**
     * Returns whether a CDF data type is a string type.
     *
     * @param dataType the data type to be examined <P>
     * @return true if it is (CDF_CHAR or CDF_UCHAR)
     *         false otherwise
     */
    public static boolean isStringDataType(long dataType) {
        return (dataType == CDF_CHAR || dataType == CDF_UCHAR);
    }


    /**
     * Returns whether a CDF data type is an epoch related type.
     *
     * @param dataType the data type to be examined <P>
     * @return true if it is (CDF_EPOCH, CDF_EPOCH16 or CDF_TIME_TT2000)
     *         false otherwise
     */
    public static boolean isEpochDataType(long dataType) {
        return (dataType == CDF_EPOCH || dataType == CDF_EPOCH16 ||
                dataType == CDF_TIME_TT2000);
    }


    /**
     *  Gets the string representation of the given CDF data type. <P>
     *
     *  @param cdfDataType the CDF data type to be examined and translated <P>
     *                     It should be one of the following: <BR>
     *          <UL>
     *             <LI>CDF_BYTE</LI>
     *             <LI>CDF_CHAR</LI>
     *             <LI>CDF_UCHAR</LI>
     *             <LI>CDF_INT1</LI>
     *             <LI>CDF_UINT1</LI>
     *             <LI>CDF_INT2</LI>
     *             <LI>CDF_UINT2</LI>
     *             <LI>CDF_INT4</LI>
     *             <LI>CDF_UINT4</LI>
     *             <LI>CDF_INT8</LI>
     *             <LI>CDF_REAL4</LI>
     *             <LI>CDF_FLOAT</LI>
     *             <LI>CDF_REAL8</LI>
     *             <LI>CDF_DOUBLE</LI>
     *             <LI>CDF_EPOCH</LI>
     *             <LI>CDF_EPOCH16</LI>
     *             <LI>CDF_TIME_TT2000</LI>
     *          </UL><P>
     *  @return String representation of cdfDataType.  
     *          The returned value is one of the valid values describe above
     *          for cdfDataType.  "UNKNOWN" is returned if invalid cdfDataType 
     *          is given.
     */
    public static String getStringDataType (long cdfDataType) {
        String cDataType;

        if (cdfDataType == CDF_BYTE) 
            cDataType = "CDF_BYTE"; 

        else if (cdfDataType == CDF_CHAR) 
            cDataType = "CDF_CHAR";

        else if (cdfDataType == CDF_UCHAR) 
            cDataType = "CDF_UCHAR";

        else if (cdfDataType == CDF_INT1) 
            cDataType = "CDF_INT1";

        else if (cdfDataType == CDF_UINT1) 
            cDataType = "CDF_UINT1";

        else if (cdfDataType == CDF_INT2) 
            cDataType = "CDF_INT2";

        else if (cdfDataType == CDF_UINT2) 
            cDataType = "CDF_UINT2";

        else if (cdfDataType == CDF_INT4) 
            cDataType = "CDF_INT4";

        else if (cdfDataType == CDF_UINT4) 
            cDataType = "CDF_UINT4";

        else if (cdfDataType == CDF_INT8) 
            cDataType = "CDF_INT8";

        else if (cdfDataType == CDF_REAL4) 
            cDataType = "CDF_REAL4";

        else if (cdfDataType == CDF_FLOAT) 
            cDataType = "CDF_FLOAT";

        else if (cdfDataType == CDF_REAL8) 
            cDataType = "CDF_REAL8";

        else if (cdfDataType == CDF_DOUBLE) 
            cDataType = "CDF_DOUBLE";

        else if (cdfDataType == CDF_EPOCH) 
            cDataType = "CDF_EPOCH";

        else if (cdfDataType == CDF_EPOCH16)
            cDataType = "CDF_EPOCH16";

        else if (cdfDataType == CDF_TIME_TT2000)
            cDataType = "CDF_TIME_TT2000";

        else
            cDataType = "UNKNOWN";

        return cDataType;
    }

    /**
     *  Gets the long value of the given CDF data type in string.
     *  This is a reverse function from getStringDataType. <P>
     *
     *  @param cdfDataType the string CDF data type to be examined and 
     *                     translated.  It should be one of the following 
     *                     values: 
     *          <UL>
     *             <LI>CDF_BYTE</LI>
     *             <LI>CDF_CHAR</LI>
     *             <LI>CDF_UCHAR</LI>
     *             <LI>CDF_INT1</LI>
     *             <LI>CDF_UINT1</LI>
     *             <LI>CDF_INT2</LI>
     *             <LI>CDF_UINT2</LI>
     *             <LI>CDF_INT4</LI>
     *             <LI>CDF_UINT4</LI>
     *             <LI>CDF_INT8</LI>
     *             <LI>CDF_REAL4</LI>
     *             <LI>CDF_FLOAT</LI>
     *             <LI>CDF_REAL8</LI>
     *             <LI>CDF_DOUBLE</LI>
     *             <LI>CDF_EPOCH</LI>
     *             <LI>CDF_EPOCH16</LI>
     *             <LI>CDF_TIME_TT2000</LI>
     *          </UL><P>
     *  @return long representation of cdfDataType.  The returned value is
     *          one of the valid values described above for cdfDataType.  -1 is
     *          returned if invalid cdfDataType is given. 
     */
    public static long getDataTypeValue (String cdfDataType) {
        long lDataType;

        if (cdfDataType.equals( "CDF_BYTE"))
            lDataType = CDF_BYTE;

        else if (cdfDataType.equals( "CDF_CHAR"))
            lDataType = CDF_CHAR;

        else if (cdfDataType.equals( "CDF_UCHAR"))
            lDataType = CDF_UCHAR;

        else if (cdfDataType.equals( "CDF_INT1"))
            lDataType = CDF_INT1;

        else if (cdfDataType.equals( "CDF_UINT1"))
            lDataType = CDF_UINT1;

        else if (cdfDataType.equals( "CDF_INT2"))
            lDataType = CDF_INT2;

        else if (cdfDataType.equals( "CDF_UINT2"))
            lDataType = CDF_UINT2;

        else if (cdfDataType.equals( "CDF_INT4"))
            lDataType = CDF_INT4;

        else if (cdfDataType.equals( "CDF_UINT4"))
            lDataType = CDF_UINT4;

        else if (cdfDataType.equals( "CDF_INT8"))
            lDataType = CDF_INT8;

        else if (cdfDataType.equals( "CDF_REAL4"))
            lDataType = CDF_REAL4;

        else if (cdfDataType.equals( "CDF_FLOAT"))
            lDataType = CDF_FLOAT;

        else if (cdfDataType.equals( "CDF_REAL8"))
            lDataType = CDF_REAL8;

        else if (cdfDataType.equals( "CDF_DOUBLE"))
            lDataType = CDF_DOUBLE;

        else if (cdfDataType.equals( "CDF_EPOCH"))
            lDataType = CDF_EPOCH;

	else if (cdfDataType.equals( "CDF_EPOCH16"))
	    lDataType = CDF_EPOCH16;

	else if (cdfDataType.equals( "CDF_TIME_TT2000"))
	    lDataType = CDF_TIME_TT2000;

        else
            lDataType = -1;

        return lDataType;
    }

    /**
     *  Gets the string representation of the given CDF compression type. <P>
     *
     *  @param compressionType the CDF compression type to be translated.
     *                         it should be one of the following: 
     *          <UL>
     *             <LI>NO_COMPRESSION
     *             <LI>RLE_COMPRESSION
     *             <LI>HUFF_COMPRESSION
     *             <LI>AHUFF_COMPRESSION
     *             <LI>GZIP_COMPRESSION
     *          </UL><P>
     *                         
     *  @return String representation of compressionType.
     *          The returned value is one of the following: <BR>
     *          <UL>
     *             <LI>NONE
     *             <LI>RLE
     *             <LI>HUFF (as Huffman)
     *             <LI>AHUFF (as Adaptive Huffman)
     *             <LI>GZIP
     *             <LI>UNKNOWN (for unknown compressionType)
     *          </UL>
     */
    public static String getStringCompressionType (long compressionType) {
/*
        String cDataTypes[] = {"NONE",   "RLE", "Huffman",
                               "Adaptive Huffman", " ",   "GZIP"};
*/
        String cDataTypes[] = {"NONE",   "RLE", "HUFF",
                               "AHUFF",  " ",   "GZIP"};

        // RICE (4) is no longer supported.
        if ((compressionType > GZIP_COMPRESSION) || (compressionType == 4L))
            return "UNKNOWN";
        else
            return (cDataTypes[(int)compressionType]);
    }

    /**
     *  Gets the long representation of the given CDF compression type 
     *  in string. <P>
     *
     *  @param compressionType the CDF compression type to be translated. 
     *         It should be one of the following: <BR>
     *          <UL>
     *             <LI>NONE
     *             <LI>RLE
     *             <LI>HUFF (as Huffman)
     *             <LI>AHUFF (as Adaptive Huffman)
     *             <LI>GZIP
     *          </UL><P>
     *
     *  @return long representation of compressionType. 
     *          The returned value is one of the following: <BR>
     *          <UL>
     *             <LI>NO_COMPRESSION
     *             <LI>RLE_COMPRESSION
     *             <LI>HUFF_COMPRESSION
     *             <LI>AHUFF_COMPRESSION
     *             <LI>GZIP_COMPRESSION
     *             <LI>-1 (for unknown compressionType)
     *          </UL>
     */

    public static long getLongCompressionType (String compressionType) {
/*
        String cDataTypes[] = {"NONE",   "RLE", "Huffman",
                               "Adaptive Huffman", " ",   "GZIP"};
*/
        String cDataTypes[] = {"NONE",   "RLE", "HUFF",
                               "Ahuff",  " ",   "GZIP"};

        // RICE (4) is no longer supported.
	int i;
	for (i = 0; i < cDataTypes.length; i++) {
	   if (compressionType.equalsIgnoreCase(cDataTypes[i]))
	     return (long) i;
	}
        if (compressionType.equalsIgnoreCase("NONE"))
	     return (long) 0;
        return -1;
    }

    /**
     *  Gets the string representation of the given variable's 
     *  compression type. <P>
     *
     *  @param var the variable to be examined <P>
     *  @return string representation of the given variable's compression 
     *          type. See getStringCompressionType(long compressionType) for
     *          possible return values. 
     */
    public static String getStringCompressionType (Variable var) {
        return CDFUtils.getStringCompressionType(var.getCompressionType());
    }

    /**
     *  Gets the string representation of the given CDF file's 
     *  compression type. <P>
     *
     *  @param cdf the CDF to be examined <P>
     *  @return string representation of the given CDF file's compression 
     *          type. See getStringCompressionType(long compressionType) for
     *          possible return values. 
     */
    public static String getStringCompressionType (CDF cdf) {
        return CDFUtils.getStringCompressionType(cdf.getCompressionType());
    }


    /**
     *  Gets the string value of the given CDF encoding type. <P>
     *
     *  @param encodingType the CDF encoding type to be examined.  It should
     *                      be one of the following: <BR> 
     *          <UL>
     *             <LI>NETWORK_ENCODING
     *             <LI>SUN_ENCODING
     *             <LI>DECSTATION_ENCODING
     *             <LI>SGi_ENCODING
     *             <LI>IBMPC_ENCODING
     *             <LI>IBMRS_ENCODING
     *             <LI>HOST_ENCODING
     *             <LI>PPC_ENCODING
     *             <LI>HP_ENCODING
     *             <LI>NeXT_ENCODING
     *             <LI>ALPHAOSF1_ENCODING
     *             <LI>ALPHAVMSd_ENCODING
     *             <LI>ALPHAVMSg_ENCODING
     *             <LI>ALPHAVMSi_ENCODING
     *             <LI>ARM_LITTLE_ENCODING
     *             <LI>ARM_BIG_ENCODING
     *             <LI>IA64VMSi_ENCODING
     *             <LI>IA64VMSd_ENCODING
     *             <LI>IA64VMSg_ENCODING
     *          </UL><P>
     *
     *  @return string representation of encodingType.
     *          The returned value is one of the following: <BR>
     *          <UL>
     *             <LI>NETWORK
     *             <LI>SUN
     *             <LI>DECSTATION
     *             <LI>SGi
     *             <LI>IBMPC
     *             <LI>IBMRS
     *             <LI>HOST
     *             <LI>PPC
     *             <LI>HP
     *             <LI>NeXT
     *             <LI>ALPHAOSF1
     *             <LI>ALPHAVMSd
     *             <LI>ALPHAVMSg
     *             <LI>ALPHAVMSi
     *             <LI>ARM_LITTLE
     *             <LI>ARM_BIG
     *             <LI>IA64VMSi
     *             <LI>IA64VMSd
     *             <LI>IA64VMSg
     *             <LI>UNKNOWN (for unknown encodingType)
     *          </UL>
     */
    public static String getStringEncoding (long encodingType) {
        String eDataTypes[] = {" ",          "NETWORK", 
                               "SUN",        "VAX", 
                               "DECSTATION", "SGi",
                               "IBMPC",      "IBMRS", 
                               "HOST",       "PPC", 
                               " ",          "HP",
                               "NeXT",       "ALPHAOSF1",  
                               "ALPHAVMSd",  "ALPHAVMSg",  
                               "ALPHAVMSi",  "ARM_LITTLE",
                               "ARM_BIG",    "IA64VMSi",
                               "IA64VMSd",   "IA64VMSg"}; 

        if ((encodingType == 0L) || (encodingType == 10L) || 
            (encodingType > IA64VMSg_ENCODING))

            return "UNKNOWN";

        else
            return (eDataTypes[(int)encodingType]);
    }

    /**
     *  Gets the long value of the given CDF encoding type in string. <P>
     *
     *  @param encodingType the CDF encoding type to be examined. 
     *                      It should be one of the following: <BR>
     *          <UL>
     *             <LI>NETWORK
     *             <LI>SUN
     *             <LI>DECSTATION
     *             <LI>SGi
     *             <LI>IBMPC
     *             <LI>IBMRS
     *             <LI>HOST
     *             <LI>PPC
     *             <LI>HP
     *             <LI>NeXT
     *             <LI>ALPHAOSF1
     *             <LI>ALPHAVMSd
     *             <LI>ALPHAVMSg
     *             <LI>ALPHAVMSi
     *             <LI>ARM_LITTLE
     *             <LI>ARM_BIG
     *             <LI>IA64VMSi
     *             <LI>IA64VMSd
     *             <LI>IA64VMSg
     *          </UL><P>
     * 
     *  @return long representation of encodingType.  
     *          The returned value is one of the following: <BR>
     *          <UL>
     *             <LI>NETWORK_ENCODING
     *             <LI>SUN_ENCODING
     *             <LI>DECSTATION_ENCODING
     *             <LI>SGi_ENCODING
     *             <LI>IBMPC_ENCODING
     *             <LI>IBMRS_ENCODING
     *             <LI>HOST_ENCODING
     *             <LI>PPC_ENCODING
     *             <LI>HP_ENCODING
     *             <LI>NeXT_ENCODING
     *             <LI>ALPHAOSF1_ENCODING
     *             <LI>ALPHAVMSd_ENCODING
     *             <LI>ALPHAVMSg_ENCODING
     *             <LI>ALPHAVMSi_ENCODING
     *             <LI>ARM_LITTLE_ENCODING
     *             <LI>ARM_BIG_ENCODING
     *             <LI>IA64VMSi_ENCODING
     *             <LI>IA64VMSd_ENCODING
     *             <LI>IA64VMSg_ENCODING
     *             <LI>-1 (for unknown encodingType)
     *          </UL>
     */
    public static long getLongEncoding (String encodingType) {
        String eDataTypes[] = {" ",          "NETWORK",
                               "SUN",        "VAX",
                               "DECSTATION", "SGi",
                               "IBMPC",      "IBMRS",
                               "HOST",       "PPC",
                               " ",          "HP",
                               "NeXT",       "ALPHAOSF1",
                               "ALPHAVMSd",  "ALPHAVMSg",
                               "ALPHAVMSi",  "ARM_LITTLE",
                               "ARM_BIG",    "IA64VMSi",
                               "IA64VMSd",   "IA64VMSg"};

	if ("".equals(encodingType) || encodingType.equals(" "))
	   return -1;
	else {
	  for (int i = 0; i < eDataTypes.length; i++) {
	     if (i == 0 || i == 10) continue;
	     if (encodingType.equalsIgnoreCase(eDataTypes[i]))
	       return (long) i;
	  }
            return -1;
	}

    }

    /**
     *  Get the string value of the given CDF's encoding type. <P>
     *
     *  @param cdf the CDF to be examined <P>
     *  @return string representation of the given CDF's encoding type.
     *          See getStringEncoding(long encodingType) for possible 
     *          return values.
     */
    public static String getStringEncoding (CDF cdf) {
        return CDFUtils.getStringEncoding(cdf.getEncoding());
    }


    /**
     *  Gets the string value of the given CDF decoding type <P>.
     *
     *  @param decodingType the CDF decoding type to be examined.  It 
     *                      should be one of the following: <BR> 
     *          <UL>
     *             <LI>NETWORK_DECODING
     *             <LI>SUN_DECODING
     *             <LI>DECSTATION_DECODING
     *             <LI>SGi_DECODING
     *             <LI>IBMPC_DECODING
     *             <LI>IBMRS_DECODING
     *             <LI>HOST_DECODING
     *             <LI>PPC_DECODING
     *             <LI>HP_DECODING
     *             <LI>NeXT_DECODING
     *             <LI>ALPHAOSF1_DECODING
     *             <LI>ALPHAVMSd_DECODING
     *             <LI>ALPHAVMSg_DECODING
     *             <LI>ALPHAVMSi_DECODING
     *             <LI>ARM_LITTLE_DECODING
     *             <LI>ARM_BIG_DECODING
     *             <LI>IA64VMSi_DECODING
     *             <LI>IA64VMSd_DECODING
     *             <LI>IA64VMSg_DECODING
     *             <LI>-1 (for unknown encodingType)
     *          </UL><P>
     *  @return string representation of decodingType. See
     *          getStringEncoding (long encodingType) for possible return
     *          values. <P>
     *  @exception CDFException if a problem occurs getting the string value
     *             of the given decoding type
     */
    public static String getStringDecoding (long decodingType) 
        throws CDFException {

        return CDFUtils.getStringEncoding(decodingType);
    }

    /**
     *  Gets the string value of the given CDF file's decoding type. <P>
     *
     *  @param cdf the CDF to be examined <P>
     *  @return string representation of the given CDF file's decoding type.
     *          See getStringEncoding (long encodingType) for possible return
     *          values. <P>
     *  @exception CDFException if a problem occurs getting the value
     *             of the decoding type defined for the given CDF
     */
    public static String getStringDecoding (CDF cdf) 
        throws CDFException {

        return CDFUtils.getStringEncoding(cdf.confirmDecoding());
    }


    /**
     *  Gets the string value of the given CDF majority. <P>
     *
     *  @param majorityType the CDF majority to be translated <P>
     *  @return string representation of majorityType. 
     *          The returned value is one of the following: <BR>
     *          <UL>
     *             <LI>ROW
     *             <LI>COLUMN
     *             <LI>UNKNOWN (for unknown majorityType)
     *          </UL>
     */
    public static String getStringMajority (long majorityType) {
        String mType = null;

        if (majorityType == ROW_MAJOR)
            mType = "ROW";

        else if (majorityType == COLUMN_MAJOR)
            mType = "COLUMN";
        
        else
            mType = "UNKNOWN";

        return (mType);
    }

    /**
     *  Gets the long value of the given CDF majority. <P>
     *
     *  @param majorityType the CDF majority to be translated. It should be  
     *                      either ROW or COLUMN <P>
     *  @return long representation of majorityType. 
     *          The returned value is one of the following: <BR>
     *          <UL>
     *             <LI>ROW_MAJOR
     *             <LI>COLUMN_MAJOR
     *             <LI>-1 (for unknown majorityType)
     *          </UL>
     */
    public static long getLongMajority (String majorityType) {
        long mType;

        if (majorityType.equalsIgnoreCase("ROW"))
	    mType = ROW_MAJOR;

        else if (majorityType.equalsIgnoreCase("COLUMN"))
	    mType = COLUMN_MAJOR;

        else
            mType = -1;

        return (mType);
    }

    /**
     *  Gets the string value of the given CDF file's majority. <P>
     *
     *  @param cdf the CDF to be examined <P>
     *  @return string representation of the given CDF file's majority.
     *          The returned value is one of the following: <BR>
     *          <UL>
     *             <LI>ROW
     *             <LI>COLUMN
     *          </UL>
     */
    public static String getStringMajority (CDF cdf) {
        return CDFUtils.getStringMajority(cdf.getMajority());
    }


    /**
     *  Gets the string value of the given CDF's file format. <P>
     *
     *  @param formatType the CDF file format to be translated.  It should be
     *                    either SINGLE or MULTI <P>
     *  @return string representation of formatType. The returned
     *          value is either SINGLE, MULTI, or UNKNOWN.
     */
    public static String getStringFormat (long formatType) {
        String fType = null;

        if (formatType == SINGLE_FILE)
            fType = "SINGLE";

        else if (formatType == MULTI_FILE)
            fType = "MULTI";

        else
            fType = "UNKNOWN";

        return (fType);
    }


    /**
     *  Gets the long value of the given CDF file format in string. <P>
     *
     *  @param formatType the CDF file format to be translated.  It
     *                    should be either SINGLE or MULTI. <P>
     *  @return long representation of formatType.  The returned value is
     *          one of the following:
     *          <UL>
     *             <LI>SINGLE_FILE
     *             <LI>MULTI_FILE
     *             <LI>-1 (for unknown format type)
     *          </UL>
     */
    public static long getLongFormat (String formatType) {
        long fType;

        if (formatType.equalsIgnoreCase("SINGLE"))
	    fType = SINGLE_FILE;

        else if (formatType.equalsIgnoreCase("MULTI"))
	    fType = MULTI_FILE;

        else
            fType = -1;

        return (fType);
    }

    /**
     *  Gets the string value of the given CDF's file format. <P>
     *
     *  @param cdf the CDF to be examined <P>
     *  @return string representation of given CDF's file format. The returned
     *          value is either SINGLE, MULTI, or UNKNOWN.
     */
    public static String getStringFormat (CDF cdf) {
        return CDFUtils.getStringFormat(cdf.getFormat());
    }

    /**
     *  Gets the string value of the given sparse record type. <P>
     *
     *  @param sparseRecordType the sparse record type to be translated.  
     *                It should be one of the following: <BR>
     *          <UL>
     *             <LI>NO_SPARSERECORDS</LI>
     *             <LI>PAD_SPARSERECORDS</LI>
     *             <LI>PREV_SPARSERECORDS</LI>
     *          </UL><P>
     *  @return string representation of sparseRecordType.  The returned
     *          value is one of the following:<BR>
     *          <UL>
     *             <LI>None
     *             <LI>PAD
     *             <LI>PREV
     *             <LI>UNKNOWN
     *          </UL>
     */
    public static String getStringSparseRecord (long sparseRecordType) {
        String sType = null;

        if (sparseRecordType == NO_SPARSERECORDS)
            sType = "None";

        else if (sparseRecordType == PAD_SPARSERECORDS)
            sType = "PAD";

        else if (sparseRecordType == PREV_SPARSERECORDS)
            sType = "PREV";

        else
            sType = "UNKNOWN";

        return (sType);
    }

    /**
     *  Gets the long value of the given CDF's checksum in string. <P>
     *
     *  @param checksum the checksum string of which to be translated.
     *  @return long value of checksum type. The returned
     *          value is either NONE_CHECKSUM, MD5_CHECKSUM,
     *          or OTHER_CHECKSUM.
     */
    public static long getLongChecksum (String checksum) {
        long cType;
        if (checksum.equalsIgnoreCase("None"))
            cType = NONE_CHECKSUM;

        else if (checksum.equalsIgnoreCase("md5"))
            cType = MD5_CHECKSUM;

        else if (checksum.equalsIgnoreCase("other"))
            cType = OTHER_CHECKSUM;

        else
            cType = NO_CHECKSUM;

        return (cType);

    }

    /**
     *  Gets the date of the given CDF's last updated for the leap second. <P>
     *
     *  @param cdf the CDF with which its leap second last updated.  
     *  @return value (in YYYYMMDD) representation of leap second last updated,
     *          if set. -1 if the value is not set (prior to CDF V3.5.1).
     *  @throws CDFException An exception is thrown if the operation fails
     */
    public static long getLeapSecondLastUpdated (CDF cdf) throws CDFException {
        return cdf.getLeapSecondLastUpdated();
    }

    /**
     *  Gets the string value of the given CDF's checksum. <P>
     *
     *  @param cdf the CDF with which its checksum to be translated.  
     *  @return string representation of checksum type. The returned
     *          value is either NONE, MD5, or OTHER.
     */
    public static String getStringChecksum (CDF cdf) {
        return CDFUtils.getStringChecksum(cdf.getChecksum());
    }

    /**
     *  Gets the string value of the given CDF's checksum. <P>
     *
     *  @param checksumType the CDF checksum to be translated.  It should be
     *                    either NO_CHECKSUM (or NONE_CHECKSUM) or MD5_CHECKSUM <P>
     *  @return string representation of checksumType. The returned
     *          value is either NONE, MD5, or OTHER.
     */
    public static String getStringChecksum (long checksumType) {
        String cType = null;

        if (checksumType == NO_CHECKSUM)
            cType = "NONE";

        else if (checksumType == MD5_CHECKSUM)
            cType = "MD5";

        else
            cType = "OTHER";

        return (cType);
    }


    /**
     *  Gets the long value of the given sparse record type in string. <P>
     *
     *  @param sparseRecordType the sparse record type to be translated. 
     *         It should be one of the following: <BR>
     *          <UL>
     *             <LI>None 
     *             <LI>PAD or sRecords.PAD
     *             <LI>PREV or sRecords.PREV
     *          </UL><P>
     *  @return long representation of sparseRecordType. The returned value
     *          is one of the following: <BR>
     *          <UL>
     *             <LI>NO_SPARSERECORDS
     *             <LI>PAD_SPARSERECORDS
     *             <LI>PREV_SPARSERECORDS
     *             <LI>-1 (for unknown sparse record type)
     *          </UL>
     */
    public static long getLongSparseRecord (String sparseRecordType) {
	long sType;

        if (sparseRecordType.equalsIgnoreCase("None"))
	    sType = NO_SPARSERECORDS;

        else if (sparseRecordType.equalsIgnoreCase("sRecords.PAD") || 
		 sparseRecordType.equalsIgnoreCase("PAD")) 
	    sType = PAD_SPARSERECORDS;

        else if (sparseRecordType.equalsIgnoreCase("sRecords.PREV") || 
		 sparseRecordType.equalsIgnoreCase("PREV"))
	    sType = PREV_SPARSERECORDS;

        else
            sType = -1;

        return (sType);
    }

    /**
     *  Gets the string value of the given variable's sparse record type. <P>
     *
     *  @param var the variable to be examined <P>
     *  @return string representation of the given variable's sparse
     *          record type.  The returned value is one of the following:<BR>
     *          <UL>
     *             <LI>None
     *             <LI>PAD
     *             <LI>PREV
     *             <LI>UNKNOWN
     *          </UL>

     */
    public static String getStringSparseRecord (Variable var) {
        return CDFUtils.getStringSparseRecord (var.getSparseRecords()); 
    }

    /**
     *  Checks the existence of the given CDF file name.  If the file
     *  name doesn't have ".cdf" file extension, it adds ".cdf" 
     *  suffix at the end of the file name before checking the existence
     *  of the file.  If the file exists in the current directory, it
     *  returns TRUE.  Otherwise, FALSE is returned.
     *
     *  @param fileName the name of the CDF file to be checked for existence<P>
     *  @return true - if fileName exists in the current directory<BR>
     *          false - if fileName doesn't exist in the current directory
     */
    public static boolean cdfFileExists (String fileName) {
       if (fileName == null) return false;
       String file1 = null, file2 = null;
       if (fileName.indexOf(".cdf") == -1 && fileName.indexOf(".CDF") == -1) {
         file1 = new StringBuffer(fileName).append(".cdf").toString();
         file2 = new StringBuffer(fileName).append(".CDF").toString();
       } else {
         file1 = fileName;
       }
       File cdfFile = new File(file1);
       if (cdfFile.exists()) return true;
       if (file2 != null) {
         cdfFile = new File(file2);
         if (cdfFile.exists()) return true;
       }
       return false;
    }

    /**
     *  Convert a C/Fortran-based print format to a corresponding Java style
     *  format for numerical data. 
     *  @param cfFormat C/Fortran-based format string <P>
     *  @return Java style format
     *          or null if not a valid or a non-handled C/Fortran format<BR>
     */
    public static String CFtoJformat (String cfFormat) {
     
       if (cfFormat == null) return null;
       String format;
       if (cfFormat.indexOf("%") != -1) format = CtoJformat (cfFormat);
       else format = FtoJformat (cfFormat);
       return format;
    }

    /**
     *  Convert a Fortran-based print format to a corresponding Java style
     *  format for numerical data. Limited Fortran format with I, F, E and G
     *  forms.
     *  @param fFormat Fortran-based format string <P>
     *  @return jFormat Java style format
     *          or null if not a valid or a non-handled Fortran format<BR>
     */
    public static String FtoJformat (String fFormat) {
     
       if (fFormat == null) return null;
       StringBuffer sb = new StringBuffer();
       String header = fFormat.substring(0,1).toLowerCase();
       if (header.equals("i")) {
         /*********************************************************************
         * Integer/decimal.
         *********************************************************************/
         String format = fFormat.substring(1);
         if (format.length() == 0) {
           sb.append("%d");
         } else {
           int iPointer = format.indexOf(".");
           if (iPointer == -1) {
             sb.append("%").append(format).append("d");
           } else {
             String w = format.substring(0, iPointer);
             String m = format.substring(iPointer+1);
             sb.append("%").append(w).append("d");
           }
         }
       } else if (header.equals("f")) {
         /*********************************************************************
         * Floating point/decimal form.
         *********************************************************************/
         String format = fFormat.substring(1);
         if (format.length() == 0) {
           sb.append("%f");
         } else {
           int iPointer = format.indexOf("."); 
           if (iPointer == -1) {
             return null;
           } else {
             String w = format.substring(0, iPointer);
             String d = format.substring(iPointer+1);
             sb.append("%").append(w).append(".").append(d).append("f");
           }
         }
       } else if (header.equals("e") || header.equals("d")) {
         /*********************************************************************
         * Floating point/exponential form.
         *********************************************************************/
         String format = fFormat.substring(1);
         if (format.length() == 0) {
           sb.append("%d");
         } else {
           int iPointer = format.indexOf("."); 
           if (iPointer == -1) {
             return null;
           } else {
             String w = format.substring(0, iPointer);
             String d = format.substring(iPointer+1);
             sb.append("%").append(w).append(".").append(d).append("e");
           }
         }
       } else if (header.equals("f")) {
         /*********************************************************************
         * Floating point/decimal form.
         *********************************************************************/
         String format = fFormat.substring(1);
         if (format.length() == 0) {
           sb.append("%d");
         } else {
           int iPointer = format.indexOf("."); 
           if (iPointer == -1) {
             return null;
           } else {
             String w = format.substring(0, iPointer);
             String d = format.substring(iPointer+1);
             sb.append("%").append(w).append(".").append(d).append("f");
           }
         }
       } else if (header.equals("g")) {
         /*********************************************************************
         * Floating point/exponential form.
         *********************************************************************/
         String format = fFormat.substring(1);
         if (format.length() == 0) {
           sb.append("%d");
         } else {
           int iPointer = format.indexOf("."); 
           if (iPointer == -1) {
             return null;
           } else {
             String w = format.substring(0, iPointer);
             String d = format.substring(iPointer+1);
             sb.append("%").append(w).append(".").append(d).append("g");
           }
         }
       }
       if (sb.length() == 0) return null;
       else return sb.toString();
    }

    /**
     *  Convert a C-based print format to a corresponding Java style
     *  format for numerical data. Limited C format with I, F, E and G
     *  forms.
     *  @param cFormat C-based format string <P>
     *  @return jFormat Java style format
     *          or null if not a valid or a non-handled Fortran format<BR>
     */
    public static String CtoJformat (String cFormat) {
     
       if (cFormat == null) return null;
       if (cFormat.startsWith("%")) cFormat = cFormat.substring(1);
       int num1, num2, i;
       int len = cFormat.length();
       int iPointer = cFormat.indexOf(".");
       i = 0;
       num1 = num2 = 0;
       while (i < len && Character.isDigit(cFormat.charAt(i))) {
         num1 = 10 * num1 + Character.getNumericValue(cFormat.charAt(i));
         ++i;
       }
       if (iPointer != -1) {
         i = iPointer + 1;
         num2 = 0;
         while (i < len && Character.isDigit(cFormat.charAt(i))) {
           num2 = 10 * num2 + Character.getNumericValue(cFormat.charAt(i));
           ++i;
         }
       }
       StringBuffer sb = new StringBuffer();
       String type = cFormat.substring(len-1).toLowerCase();
       if (type.equals("i") || type.equals("d") || type.equals("u") ||
           type.equals("h") || type.equals("l")) {
         /*********************************************************************
         * Integer/decimal.
         *********************************************************************/
         if (num1 == 0)
           sb.append("%d");
         else
           sb.append("%").append(num1).append("d");
       } else if (type.equals("f")) {
         /*********************************************************************
         * Floating point/decimal form.
         *********************************************************************/
         if (num1 == 0)
           sb.append("%f");
         else
           if (num2 == 0)
             sb.append("%").append(num1).append("f");
           else
             sb.append("%").append(num1).append(".").append(num2).append("f");
       } else if (type.equals("e")) {
         /*********************************************************************
         * Floating point/exponential form.
         *********************************************************************/
         if (num1 == 0)
           sb.append("%e");
         else
           if (num2 == 0)
             sb.append("%").append(num1).append("e");
           else
             sb.append("%").append(num1).append(".").append(num2).append("e");
       } else if (type.equals("g")) {
         /*********************************************************************
         * Floating point/exponential form.
         *********************************************************************/
         if (num1 == 0)
           sb.append("%g");
         else
           if (num2 == 0)
             sb.append("%").append(num1).append("g");
           else
             sb.append("%").append(num1).append(".").append(num2).append("g");
       }
       if (sb.length() == 0) return null;
       else return sb.toString();
    }

    /**
     * Check if a data is a floating point and its value is a pad value. 
     *  @param dataType The data type of the data <P>
     *  @param value    A single data object <P>
     *  @return 1 if the data is of floating point and it is a pad value
     *          0 otherwise<BR>
     */

    public static int isFloatingPadValue (long dataType, Object value) {

        if (dataType == CDF_REAL4 || dataType == CDF_FLOAT) {
          if (((Float)value).floatValue() <= DEFAULT_FLOAT_PADVALUE ||
              ((Float)value).floatValue() >= -DEFAULT_FLOAT_PADVALUE) return 1;
          else return 0;
        } else if (dataType == CDF_REAL8 || dataType == CDF_DOUBLE) {
          if (((Double)value).doubleValue() <= DEFAULT_DOUBLE_PADVALUE ||
              ((Double)value).doubleValue() >= -DEFAULT_DOUBLE_PADVALUE) return 1;
          else return 0;
        } else
          return 0;
    }

    /**
     * Check if a data is a floating point and its value is a pad value. 
     *  @param value    A data object <P>
     *  @return 1 if the data is of floating point and it is a pad value
     *          0 otherwise<BR>
     */

    public static int isFloatingPadValue (Object value) {

        String signature = CDFUtils.getSignature(value);
        if (signature.charAt(0) == '[') {
            if (signature.charAt(1) == 'F')
               return CDFUtils.isFloatingPadValue (Array.getFloat(value, 0));
            else if (signature.charAt(1) == 'D')
               return CDFUtils.isFloatingPadValue (Array.getDouble(value, 0));
            else
               return 0;
        }

        if (value instanceof Float) {
          if (((Float)value).floatValue() <= DEFAULT_FLOAT_PADVALUE ||
              ((Float)value).floatValue() >= -DEFAULT_FLOAT_PADVALUE) return 1;
          else return 0;
        } else if (value instanceof Double) {
          if (((Double)value).doubleValue() <= DEFAULT_DOUBLE_PADVALUE ||
              ((Double)value).doubleValue() >= -DEFAULT_DOUBLE_PADVALUE) return 1;
          else return 0;
        } else
          return 0;
    }

    /**
     * Check if a data is a floating point and its value is a pad value. 
     *  @param dataType The data type of the data <P>
     *  @param value    The float data <P>
     *  @return 1 if the data is of floating point and it is a pad value
     *          0 otherwise<BR>
     */ 

    public static int isFloatingPadValue (long dataType, float value) {

        if (dataType == CDF_REAL4 || dataType == CDF_FLOAT) {
          if (value <= DEFAULT_FLOAT_PADVALUE ||
              value >= -DEFAULT_FLOAT_PADVALUE) return 1;
          else return 0;
        } else
          return 0;
    }

    /**
     * Check if a data is a floating point and its value is a pad value. 
     *  @param dataType The data type of the data <P>
     *  @param value    The double data <P>
     *  @return 1 if the data is of floating point and it is a pad value
     *          0 otherwise<BR>
     */

    public static int isFloatingPadValue (long dataType, double value) {
          
        if (dataType == CDF_REAL8 || dataType == CDF_DOUBLE) {
          if (value <= DEFAULT_DOUBLE_PADVALUE ||
              value >= -DEFAULT_DOUBLE_PADVALUE) return 1;
          else return 0;
        } else
          return 0;
    }

    /**
     * Check if a float data is a pad value. 
     *  @param value    a float data <P>
     *  @return 1 if the data is a pad value
     *          0 otherwise<BR>
     */ 

    public static int isFloatingPadValue (float value) {

        if (value <= DEFAULT_FLOAT_PADVALUE ||
            value >= -DEFAULT_FLOAT_PADVALUE) return 1;
        else return 0;
    }

    /**
     * Check if a double data is a pad value. 
     *  @param value    a double data <P>
     *  @return 1 if the data is a pad value
     *          0 otherwise<BR>
     */

    public static int isFloatingPadValue (double value) {
          
        if (value <= DEFAULT_DOUBLE_PADVALUE ||
            value >= -DEFAULT_DOUBLE_PADVALUE) return 1;
        else return 0;
    }

    public static int getNumDimensions(Object obj) {
    /**
     * Get the number of dimensions of the given object.
     * @param obj The given object
     * @return the number of dimensions
     */

        String sig = CDFUtils.getSignature(obj);
        int i, ndims = 0;
        for (i = 0; i < sig.length(); ++i) {
          if (sig.charAt(i) != '[') break;
          ++ndims;
        }
        return ndims;
/*
        Object tmp = obj;
        int nDimensions = 0;
        while (true) {
          if (tmp instanceof Object[]) {
            tmp = ((Object[]) tmp)[0];
            nDimensions++;
          }
          else if (tmp.getClass().isArray()) {
            ++nDimensions;
            break;
          } else
            break;
        }
        return nDimensions;
*/
    }

    public static long[] getDimensionSizes(Object obj) {
    /**
     * Returned the dimensional sizes of the given object.
     * @param obj The given object
     * @return dimensiosnal sizes
     *         null if obj is a scalar
     */

        int ndims = 0;
        ndims = getNumDimensions(obj);
        if (ndims == 0) return null;
        long[] dims = new long[ndims]; 
        int i = 0;
        Object tmp = obj;
        while (true) {
          if (tmp instanceof Object[]) {
            dims[i] = (long) ((Object[]) tmp).length;
            tmp = ((Object[]) tmp)[0];
            ++i;
          }
          else if (tmp.getClass().isArray()) {
            dims[i] = (long) Array.getLength(tmp);
            break;
          } else
            break;
        }
        return dims;
    }

    public static byte[] UTF8toUTF16LE(Object utf8) {
    /**
     * Convert a string or UTF-8 encoded byte array to a UTF-16LE encoded byte
     * array.
     * @param utf8 A string or a UTF8 encoded byte array
     * @return UTF-16LE encoded byte array
     *         null if an error occurs while conversion
     */

        String signature = CDFUtils.getSignature (utf8);
        try {
          if (signature.charAt(0) == '[') {
            return (new String((byte[])utf8, "UTF-8")).getBytes("UTF-16LE");
          } else {
            return ((String)utf8).getBytes("UTF-16LE");
          }
        } catch (Exception ee) {
          return null;
        }
    }

    public static byte[] UTF16LEtoUTF8(Object utf16) {
    /**
     * Convert a string or a UTF-16LE encoded byte array to a UTF-8 encoded
     * byte array. 
     * @param utf16 A string or a UTF-16LE encoded byte array
     * @return UTF-8 encoded byte array
     *         null if an error occurs while conversion
     */

        String signature = CDFUtils.getSignature (utf16);
        try {
          if (signature.charAt(0) == '[') {
            return (new String((byte[])utf16, "UTF-16LE")).getBytes("UTF-8");
          } else {
            return ((String)utf16).getBytes("UTF-8");
          }
        } catch (Exception ee) {
          return null;
        }
    }

    public static byte[] UTF8toCp1252(Object utf8) {
    /**
     * Convert a string or a UTF-8 encoded byte array to a Windows-1252 encoded
     * byte array.
     * @param utf8 A string or UTF8 encoded byte array
     * @return Windows-1252 encoded byte array
     *         null if an error occurs while conversion
     */

        String signature = CDFUtils.getSignature (utf8);
        try {
          if (signature.charAt(0) == '[') {
            return (new String((byte[])utf8, "UTF-8")).getBytes("Windows-1252");
          } else {
            return ((String)utf8).getBytes("Windows-1252");
          }
        } catch (Exception ee) {
          return null;
        }
    }

    public static byte[] Cp1252toUTF8(Object cp1252) {
    /**
     * Convert a string or Windows-1252 encoded byte array to a UTF-8 encoded
     * byte array.
     * @param cp1252 A string or a Windows-1252 encoded byte array
     * @return UTF-8 encoded byte array
     *         null if an error occurs while conversion
     */

        String signature = CDFUtils.getSignature (cp1252);
        try {
          if (signature.charAt(0) == '[') {
            return (new String((byte[])cp1252, "Windows-1252")).getBytes("UTF-8");
          } else {
            return ((String)cp1252).getBytes("UTF-8");
          }
        } catch (Exception ee) {
          return null;
        }
    }

}
