/******************************************************************************
* Copyright 1996-2013 United States Government as represented by the
* Administrator of the National Aeronautics and Space Administration.
* All Rights Reserved.
******************************************************************************/

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

/****************************************************************************
  Description:
    This program exports the contents of a CDF file into an XML file
    that conforms to cdf.dtd (default mode), cdf.xsd, or cdfxdf.dtd.
    The file conforms to the CDF Document Type Definition (cdf.dtd or
    cdfxdf.dtd) or the CDF XML Schema (cdf.xsd) is called a CDF markup
    language (CDFML) file.

       - cdf.dtd is a pure version of CDFML described in DTD.
       - cdf.xsd is a pure version of CDFML described in schema.
       - cdfxdf.dtd is a CDFML that employs some of the eXtensible Data 
            Format (XDF) tags within CDF tags.
 
    The name of the input CDF file is used as the default output file name.

  Usage: java CDF2CDFML [Options] cdfFileName 
 
  Options: 
     -mode:[dtd | xsd | cdfxdf].  -mode:dtd is the default.
         -mode:dtd creates a CDFML file that conforms to cdf.dtd (DTD)
         -mode:xsd creates a CDFML file that conforms to cdf.xsd (schema)
         -mode:cdfxdf creates a CDFML file that conforms to cdfxdf.dtd
 
     -varData:[yes | no].  -varData:yes is the default.
         -varData:yes extracts the variable data
         -varData:no doesn't extract the variable data
 
     -output:[outputFileName | STDOUT]
         The name of the input CDF file is used as the default output file 
         name.  Use this option to specify a different output file name.
         -output:STDOUT displays the XML output on the screen.
 
     -indentation:n (n is the number of spaces to skip between levels)

     -[showProgress | Progress]
         This option displays the processing progress on the screen.  
         It shows how many CDF variables are there to process and which
         variable is being processed.
 
     -strDelimiter:["userDefinedDelimiter" | "<element>"]
         The string delimiter to be used for an array of string data.

         Note that the user defined string delimiter (userDefinedDelimiter) 
         should be enclosed with a double quote.  If the value of
         this option is <element>, each element of the string array is
         described between the <element> and </element> tags.

     -noniso8601
         This option directs the date/time output not in ISO8601 format for
         CDF_EPOCH and CDF_EPOCH16 data types as:
           dd-MMM-yyyy hh:mm:ss.mmm and
           dd-MMM-yyyy hh:mm:ss.mmm.uuu.nnn.ppp 
         Otherwise, the default has the ISO8601 format:
           yyyy-mm-ddThh:mm:ss.mmm for CDF_EPOCH
           yyyy-mm-ddThh:mm:ss.mmmuuunnnppp for CDF_EPOCH16 
         For CDF_TIME_TT2000 data type, it is always in the form of ISO8601:
           yyyy-mm-ddThh:mm:ss.mmmuuunnn

     -withZ
         This option goes with ISO8601 date format to add an extra "Z" to
         the end of the time form. The default is no.

     -datatype
         This option places the data output in a value element with its
         data type as an attribute for each record. The valid data types,
         following http://www.w3.org/2001/XMLSchema, are:
          dateTime          string               float     
          double            byte                 unsignedByte     
          short             unsignedShort        int     
          unsignedInt       long                 unsignedLong     
         Without the option, the default, the data is in the record element.

     -[delete | nodelete]
         Use this option to control whether to replace the XML if it already
         exists. The default is to replace.

     -cdata
         This option will wrap string typed data in "<![[CDATA" and
         "]]>". Without it, the string will be presented as it, with the only
         exception of 1 character: "<". It will use the standard XML form to
         represent: "&lt;".

 HISTORY:
      May 2002         David Han        Original version.
      Oct 11, 2007     David Han        Added the -output:STDOUT option.
                                        Made "xsd" (schema) as the default
                                          from "dtd".
      Oct 10, 2012     Mike Liu         Added handling for CDF_TIME_TT2000.
      Jul 20, 2017     Mike Liu         Added "Z" to the end of encoded TT2000
                                        string.
      Jul 30, 2018     Mike Liu         Handle newer schema.
 *****************************************************************************/

public class CDF2CDFML implements CDFConstants {

    private static CDF cdf = null;
    private static PrintWriter  out = null;
    private static boolean  debug = false;
    private static int  indent = 0;
    private static int  MAX_STR = 80;
    private static String  recordTerminator = "&#10;";

    private static final String CDATA_BEGIN = "<![CDATA[";
    private static final String CDATA_END   = "]]>";

//    private static final String VAL_START = "<value xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xsd:";
    private static final String VAL_START = "<value xsi:type=\"xsd:";
    private static final String VAL_END =   "</value>";
    private static String  inFile = null,      /* Input file name            */
                           outFile = null,     /* Output file name           */
                           varData = "yes",    /* Extract variable data flag */
                           mode = "xsd";       /* Default translation mode   */

    private static String defaultStrDelimiter = "@~";  
    private static String strDelimiter = null;  
    private static boolean showProgress = false;
    private static boolean iso8601 = true;
    private static boolean withz = false;
    private static boolean choice = false;
    private static boolean delete = true;
    private static boolean useCdata = false;
    private static boolean useCDATA2 = false;
    private static String indentation = "   ";  /* Indentation between levels */

    public static void main(String[] args) throws Exception {

        // OutputStreamWriter outWriter;
        Writer outWriter;

        try {
            parseCmdLine(args);

            String originalInputFilename = inFile;
            inFile = removeFilePath(inFile);
            if ( !inFile.endsWith(".cdf") &&  !inFile.endsWith(".CDF") )
                inFile = inFile + ".cdf";

            if (outFile == null) 
                outFile = getDefaultOutputFileName(); 

            if (outFile.equals("STDOUT"))
                outWriter = new BufferedWriter (
                               new OutputStreamWriter(System.out));
            else {
                if (!delete) {
                   if (new File(outFile).exists()) {
                      System.out.println("ERROR... output file:"+outFile+
                                         " already exists. ");
                      System.exit(1);
                   }
                }
                outWriter = new OutputStreamWriter(
                              new BufferedOutputStream(
                                     new FileOutputStream(outFile), 4096),
                              "UTF-8");
            }

            // out = new PrintWriter(outWriter, true);
            out = new PrintWriter(outWriter, false);
            cdf = CDF.open(originalInputFilename, READONLYoff);

            printHeader();
            printCDFFileInfo();
            printGlobalAttributes();
            printVariables();

            if (!mode.equalsIgnoreCase("cdfxdf"))
                printOrphanAttributes();

            cdf.close();
            out.println ("</CDF>");
            out.flush();

            if (!outFile.equals("STDOUT")) {
               System.out.println ("\nTranslation completed successfully.\n");
               System.out.println ("   Input file name: "+originalInputFilename);
               System.out.println ("  Output file name: "+outFile);
               System.out.println ("");
            }

        } catch (Exception e) {
            if (cdf != null) cdf.close();
            System.out.println ("Exception occurred in main.\n"+e);
        }
    }


    /*********************************************************/
    /*  Parse the command line input that is in the form of  */
    /*     java CDF2CDFML [Options] cdfFileName              */
    /*                                                       */
    /*  See the top of this file for a detailed description  */
    /*  of the Options.                                      */
    /*********************************************************/
    private static void parseCmdLine (String[] args) {
        String numSpaces = null;

        if (args.length == 0)        // No input is given (i.e. java CDF2CDFML)
            exit("");

        else {                  // Process options
            for (int i=0; i < args.length; i++) {
                 if (i == (args.length-1)) {         // Get the CDF file name
                     inFile = args[i];          
                     if (!CDFUtils.cdfFileExists(inFile)) { 
                         System.out.println ("** Error: file '"+inFile+
                                             "' does not exist **");
                         System.exit (1); 
                     }
                 }
                 else {
                     int loc = args[i].indexOf(":");
                     if (args[i].startsWith("-mode:")) {
                         mode = args[i].substring(loc+1);
                         if (!mode.equalsIgnoreCase("dtd") && 
                             !mode.equalsIgnoreCase("xsd") &&
                             !mode.equalsIgnoreCase("cdfxdf")) 
                            exit("** Error: Invalid -mode entered **"); 
                     }
                     else if (args[i].startsWith("-varData:")) {
                         varData = args[i].substring(loc+1);
                         if (!varData.equalsIgnoreCase("yes") && 
                             !varData.equalsIgnoreCase("no")) 
                            exit("** Error: Invalid -varData entered **"); 
                     }
                     else if (args[i].startsWith("-output:") ||
                              args[i].startsWith("-xml:")) {
                         outFile = args[i].substring(loc+1);
                         if (outFile.equalsIgnoreCase("stdout"))
                             outFile = "STDOUT";
                         else { 
                             if ( !outFile.endsWith(".xml") &&
                                  !outFile.endsWith(".XML") &&
                                  !outFile.endsWith(".cdfml") &&
                                  !outFile.endsWith(".CDFML"))
                                 outFile = outFile + ".xml";
                         }
                     }
                     else if (args[i].startsWith("-indentation:")) {
                         numSpaces = args[i].substring(loc+1);
                         int loop = Integer.valueOf(numSpaces).intValue();
                         indentation = "";
                         for (int j=0; j < loop; j++)
                              indentation = indentation+" ";
                     }
                     else if (args[i].startsWith("-strDelimiter:")) {
                         strDelimiter = args[i].substring(loc+1);
                     }
                     else if (args[i].equalsIgnoreCase("-showprogress") ||
                              args[i].equalsIgnoreCase("-progress")) {
                         showProgress = true;
                     }
                     else if (args[i].equalsIgnoreCase("-noniso8601")) {
                         iso8601 = false;
                     }
                     else if (args[i].equalsIgnoreCase("-withz")) {
                         withz = true;
                     }
                     else if (args[i].startsWith("-debug:")) {
                         String debugStr = args[i].substring(loc+1);
                         if (debugStr.equalsIgnoreCase("true")) debug = true;
                     }
                     else if (args[i].equalsIgnoreCase("-datatype")) {
                         choice = true;
                     }
                     else if (args[i].equalsIgnoreCase("-cdata")) {
                         useCdata = true;
                     }
                     else if (args[i].equalsIgnoreCase("-delete")) {
                         delete = true;
                     }
                     else if (args[i].equalsIgnoreCase("-nodelete")) {
                         delete = false;
                     }
                     else {
                         exit ("** Error: Invalid option entered **");
                     }
                 }
            }
        }
        if (choice) {
          useCdata = false;
          iso8601 = true;
        }
        if (debug) {
            for (int i=0; i < args.length; i++)
                 System.out.println ("args["+i+"] = "+args[i]);
            System.out.println ("inFile="+inFile+", outputFile="+outFile+
                                ", mode="+mode+", vardata= "+varData+ 
                                ", indentation="+numSpaces+
                                ", strDelimiter="+strDelimiter+"**"+
                                ", showProgress="+showProgress+
                                ", noniso8601="+iso8601+
                                ", withz="+withz+
                                ", datatype="+choice+
                                ", cdata="+(!useCdata)+
                                ", delete="+delete);
        }

        if (inFile == null) exit ("** Error: CDF file name not specified **");
        
    }


    /*************************************************************************
     *  Print the appropriate CDFML file header.
     *************************************************************************/
    private static void printHeader () {
        out.println ("<?xml version=\"1.0\" standalone=\"no\"?>");
        if (mode.equalsIgnoreCase("dtd")) { 
            out.println ("<!-- Change \"http://cdf.gsfc.nasa.gov/"+
                         "cdf.dtd\" to \"cdf.dtd\" if you want to");
            out.println ("     use a local version of cdf.dtd. -->");
            out.println ("<!DOCTYPE CDF SYSTEM \""+
                         "http://cdf.gsfc.nasa.gov/cdf.dtd\">");
        } 
        else if (mode.equalsIgnoreCase("xsd")) {
            out.print ("<CDF xmlns=\"http://cdf.gsfc.nasa.gov\"");
            if (choice) {
              out.print(" xsi:schemaLocation=\"http://cdf.gsfc.nasa.gov/cdf.xsd\"");
              out.print(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"");
            }
            out.println (" name=\""+inFile+"\">");
        }
        else if (mode.equalsIgnoreCase("cdfxdf")) 
            out.println ("<!DOCTYPE CDF SYSTEM \"cdfxdf.dtd\">");

        if (!mode.equalsIgnoreCase("xsd")) 
            out.println ("<CDF name=\""+inFile+"\">");
    }


    /************************************************************************
     *  Indent before writing/displaying CDFML tags  
     ************************************************************************/
    private static void outputIndentation (int indent) {
        GetMyCDFData.outputIndentation (out, indent, indentation);
    }


    /************************************************************************
     *  If a compression is specified, print the compression type and its
     *  compression level.  The compression level is printed only if
     *  the compression method is GZIP.
     ************************************************************************/
    private static void printCompression (long cType,
                                          long[] compressionLevel) {
        if (cType != CDFConstants.NO_COMPRESSION) {
            String compression = CDFUtils.getStringCompressionType(cType);
            out.print (" compression=\""+compression);
            if (cType == CDFConstants.GZIP_COMPRESSION) 
                out.print ("."+compressionLevel[0]+"\"");
            else
                out.print ("\"");
        }
    }


    /************************************/
    /*  Print the CDF file information  */
    /************************************/
    private static void printCDFFileInfo () {
        try {
            String fileFormat = CDFUtils.getStringFormat(cdf);
            String majority = CDFUtils.getStringMajority(cdf);
            String encoding = CDFUtils.getStringEncoding(cdf);
            String checksum = CDFUtils.getStringChecksum(cdf);
            long lastUpdated = CDFUtils.getLeapSecondLastUpdated(cdf);

            indent++;
            outputIndentation(indent); 
            out.print ("<cdfFileInfo ");
            out.print ("fileFormat=\""+fileFormat+"\"");

            long   cType = cdf.getCompressionType();
            long[] compressionLevel = cdf.getCompressionParms();
            printCompression(cType, compressionLevel);

            out.print (" majority=\""+majority+"\"");
            out.print (" encoding=\""+encoding+"\"");
            
            long negToPosFp0 = cdf.confirmNegtoPosfp0();
            if (negToPosFp0 == CDFConstants.NEGtoPOSfp0on)
                out.print (" negToPosFp0=\"ENABLE\"");
            else
                out.print (" negToPosFp0=\"DISABLE\"");

            if (!checksum.equalsIgnoreCase("none") &&
                !checksum.equalsIgnoreCase("other"))
                out.print (" checksum=\""+checksum+"\"");

            if (lastUpdated > -1) {
                if (!choice)
                  out.print (" leapsecondlastupdated=\""+lastUpdated+"\"");
                else {
                  long yy, mm, dd;
                  yy = (long) (lastUpdated / 10000);
                  mm = (long) ((lastUpdated - yy * 10000) / 100);
                  dd = lastUpdated - yy * 10000 - mm * 100;
                  out.print (" leapsecondlastupdated=\""+yy+"-"+
                             (mm<10?"0":"")+mm+"-"+(dd<10?"0":"")+dd+
                             (withz?"Z":"")+"\"");
                }
            }

            out.println ("/>");

        } catch (Exception e) {
            System.out.println ("** Error occurred in printCDFFileInfo");
            System.out.println (e);
            System.exit (1);
        }
    }


    /*************************************/
    /*  Print the CDF global attributes  */
    /*************************************/
    private static void printGlobalAttributes () {
        try {
            Attribute a = null;
            String    attrName = null;
            int       i;
            long      numAttrs = cdf.getNumGattrs();
            Vector    ga = cdf.getGlobalAttributes();

            if (numAttrs > 0) {
                outputIndentation(indent);
                out.println ("<cdfGAttributes>");
                indent++;
                for (Enumeration e = ga.elements() ; e.hasMoreElements() ;) {
                    a = (Attribute) e.nextElement();
                    attrName = a.getName();
                    long n   = a.getNumEntries();
                    outputIndentation(indent);

                    // tag[0] - attribute tag  name
                    // tag[1] - attribute entry tag name

                    String[] tags =  getAttributeAndEntryTagNames();
                    Object newAttrName = GetMyCDFData.replaceStringNameToXML (
                                                          (Object)attrName);
                    out.println ("<"+tags[0]+" name=\""+newAttrName+"\">");
                    indent++;
                    if (mode.equalsIgnoreCase("cdfxdf")) {
                        outputIndentation(indent);
                        out.println ("<units><unitless/></units>");
                    }

                    Vector ent = a.getEntries();
                    boolean iso8601_x;
                    int iflag;
                    for (Enumeration e1=ent.elements(); e1.hasMoreElements();) {
                       Entry entry = (Entry) e1.nextElement();
                       if (entry != null) {
                         long eDataType = entry.getDataType();
                         outputIndentation(indent);
                         Object data = entry.getData();
                         if (!choice) {
                           out.print ("<"+tags[1]+" entryNum=\""+
                                      entry.getID()+"\""+
                                      " cdfDatatype=\""+
                                      CDFUtils.getStringDataType(eDataType)+
                                      "\">");
			   if (eDataType == CDF_EPOCH) iflag = 1; 
			   else if (eDataType == CDF_EPOCH16) iflag = 2;
                           else if (eDataType == CDF_TIME_TT2000) iflag = 3;
                           else iflag = 0;
                           if (eDataType == CDF_TIME_TT2000) iso8601_x = true;
                           else iso8601_x = iso8601;
                           useCDATA2 = false;
                           if ((eDataType == CDF_CHAR ||
                                eDataType == CDF_UCHAR) && useCdata)
                              useCDATA2 = GetMyCDFData.checkStringData(data);
                           if (useCDATA2) {
                              out.print (CDATA_BEGIN);
                              if (iflag == 0)
                                 CDFUtils.printData (data, out, iflag,
                                                     iso8601_x);
                              else {
                                 if (!withz)
                                   CDFUtils.printData (data, out, iflag,
                                                       iso8601_x);
                                 else
                                   CDFUtils.printData (data, out, iflag,
                                                       iso8601_x, "Z");
                               }
                               out.print (CDATA_END);
                           } else {
                             if (eDataType == CDF_CHAR ||
                                 eDataType == CDF_UCHAR) {
                               Object data1 = 
                                      GetMyCDFData.replaceStringDataToXML(data);
                                   CDFUtils.printData (data1, out, iflag,
                                                       iso8601_x);
                             } else {
                               if (iflag == 0 || !iso8601_x)
                                   CDFUtils.printData (data, out, iflag,
                                                       iso8601_x);
                               else {
                                 if (!withz)
                                   CDFUtils.printData (data, out, iflag,
                                                       iso8601_x);
                                 else
                                   CDFUtils.printData (data, out, iflag,
                                                       iso8601_x, "Z");
                               }
                             }
                           }
                           out.println ("</"+tags[1]+">");
                         } else { // New schema
                           GetMyCDFData gData;
                           out.println ("<"+tags[1]+" entryNum=\""+
                                        entry.getID()+"\" cdfDatatype=\""+
                                        CDFUtils.getStringDataType(eDataType)+
                                        "\">");
                           if (eDataType == CDF_CHAR ||
                               eDataType == CDF_UCHAR) {
                             gData = GetMyCDFData.create(data, eDataType, 1L,
                                                      new long[] {1L},
                                                      ((String)data).length());
                             gData.dumpData2 (out, eDataType, VAL_START,
                                              VAL_END, indent,
                                              indentation, withz, false);
                           } else {
                             String signature = CDFUtils.getSignature(data);
                             if (signature.charAt(0) == '[') {
                                int arrayLength = Array.getLength(data);
                                int nValues = arrayLength;
                                if (eDataType == CDF_EPOCH16)
                                  nValues = nValues / 2;
                                if (nValues > 1) {
                                  outputIndentation(indent-3);
                                }
                                gData = GetMyCDFData.create(data, eDataType, 1L,
                                                         new long[] {nValues},
                                                         (long) nValues);
                             } else
                               gData = GetMyCDFData.create(data, eDataType, 1L,
                                                           new long[] {1L}, 
                                                           1L);
                             gData.dumpData2 (out, eDataType, VAL_START,
                                              VAL_END, indent,
                                              indentation, withz, false);
                           }
                           outputIndentation(indent);
                           out.println ("</"+tags[1]+">");
                         }
                       }
                    }
                    indent--;
                    outputIndentation(indent);
                    out.println ("</"+tags[0]+">");
                }
                indent--;
                outputIndentation(indent);
                out.println ("</cdfGAttributes>");
            }

        } catch (Exception e) {
            System.out.println ("** Error occurred in printGlobalAttributes");
            System.out.println (e);
            System.exit (1);
        }
    }


    /*************************************************************************  
     *  Print the following information for each variable in a CDF file:
     *    - variable information (e.g. dimensionality, dimesnion sizes, etc.)
     *    - variable attributes
     *    - variable data
     ************************************************************************/
    private static void printVariables () {
        try {
            int i = 1;
            long numVars = cdf.getNumVars();

            if (numVars > 0) {
               outputIndentation(indent);
               out.println ("<cdfVariables>");
               indent++;
               Vector  vars = cdf.getVariables();

               for (Enumeration e = vars.elements() ; e.hasMoreElements() ;) {
                   Variable v = (Variable) e.nextElement();
                   String varName = v.getName();

                   if (showProgress) {
                       long maxRec = v.getMaxWrittenRecord() + 1;
                       System.out.print ("Variable "+i+" of "+numVars+
                                         " ("+maxRec+" records): "+varName); 
                       i++;
                   }

                   outputIndentation(indent);
                   Object newVarName = GetMyCDFData.replaceStringNameToXML ((Object)varName);
                   out.println ("<variable name=\""+newVarName+"\">");
/*
                   if (varName.indexOf("\"") < 0)
                     out.println ("<variable name=\""+varName+"\">");
                   else
                     if (varName.indexOf("'") < 0)
                       out.println ("<variable name='"+varName+"'>");
                     else
                       out.println ("<variable name=#"+varName+"#>");
*/
                   printVarInfo(v);
                   printVarAttributes(v);
                   printVarData(v);
               }
               indent--;
               outputIndentation(indent);
               out.println ("</cdfVariables>");
            }

        } catch (Exception e) {
            System.out.println ("** Error occurred in printVariables");
            System.out.println (e);
            System.exit (1);
        }
    }


    /*************************************************************************
     *  Print the CDF variable information.
     ************************************************************************/
    private static void printVarInfo (Variable v) {
        try {
            String  recVariance, sparseRecord, variance;
            int     i;
            long    dataType, numDims, numElements, blockingFactor;
            long[]  dimSizes = {1L}, dimVariances;
            Object  padValue = null;
            String  strDataType;

            dataType         = v.getDataType();
            numDims          = v.getNumDims();
            dimSizes         = v.getDimSizes();
            numElements      = v.getNumElements();
            dimVariances     = v.getDimVariances();
            recVariance      = v.getRecVariance() ? "VARY" : "NOVARY";
            padValue         = v.getPadValue();
            blockingFactor   = v.getBlockingFactor();
            sparseRecord     = CDFUtils.getStringSparseRecord(v);

            indent++;
            outputIndentation(indent);

            strDataType = CDFUtils.getStringDataType(dataType);
            out.print ("<cdfVarInfo cdfDatatype=\""+strDataType+"\"");
            if (showProgress)
                System.out.print ("\t"+strDataType+"/");

            out.print (" numElements=\""+numElements+"\"");
            if (showProgress)
                System.out.print (numElements+"\t");

            out.print (" dim=\""+numDims+"\"");
            if (showProgress)
                System.out.print (numDims+":[");
            if (numDims > 0) {
                out.print (" dimSizes=\"");
                for (i=0; i < numDims; i++) {
                     if (i > 0) {
                         out.print (",");
                         if (showProgress)
                             System.out.print (",");
                     }    
                     out.print (dimSizes[i]);
                     if (showProgress)
                         System.out.print (dimSizes[i]);
                }
                out.print ("\"");
            }
            if (showProgress)
                System.out.print ("]\t");

            out.print (" recVariance=\""+recVariance+"\"");
            if (showProgress) {
                if (recVariance.equals("VARY"))
                    System.out.print ("T/");
                else
                    System.out.print ("F/");
            }

            if (numDims > 0) {
                out.print (" dimVariances=\"");
                for (i=0; i < numDims; i++) {
                     if (i > 0) out.print (",");
                     if (dimVariances[i] == CDFConstants.VARY)
                         variance = "VARY";
                     else
                         variance = "NOVARY";
                     out.print (variance);

                     if (showProgress) {
                         if (variance.equals("VARY"))
                             System.out.print ("T");
                         else
                             System.out.print ("F");
                     }
                }
                out.print ("\"");
            }

            if (showProgress) System.out.println ("");

            long   cType = v.getCompressionType();
            long[] compressionLevel = v.getCompressionParms();
            printCompression (cType, compressionLevel);

            if (sparseRecord.equals("None")) {
                long numRecordsAllocate = v.getNumWrittenRecords();
                if (numRecordsAllocate > 0) 		
                  out.print (" numRecordsAllocate=\""+
                             numRecordsAllocate+"\"");
            }
            else
                out.print (" sparseRecords=\""+sparseRecord+"\"");
            
            if (v.checkPadValueExistence()) {    // Pad value has been defined
                if (dataType == CDF_EPOCH) {
                  if (!iso8601)
                    padValue = Epoch.encode(((Double) padValue).doubleValue());
                  else
                    padValue = Epoch.encode4(((Double) padValue).doubleValue())
                               + (withz?"Z":"");
		} else if (dataType == CDF_EPOCH16) {
                  if (!iso8601)
                    padValue = Epoch16.encode((double[]) padValue);
                  else
                    padValue = Epoch16.encode4((double[]) padValue)+
                               (withz?"Z":"");
                } else if (dataType == CDF_TIME_TT2000) {
                  padValue = CDFTT2000.encode(((Long) padValue).longValue())+
                             (withz?"Z":"");
                } else if (dataType == CDF_CHAR || dataType == CDF_UCHAR) {
		  char[] tmp = ((String)padValue).toCharArray();
		  if (!((String)padValue).matches(
                       "[0-9A-Za-z !*#$%&'()*+,-./:;<=>?@\\\\\\[\\]^_`{|}~]*") ||
                      (tmp.length != (int) numElements)) {
                    System.out.println ("Var: "+v.getName()+" padValue="+
                                        (String)padValue+" invalid string..."+
                                         " replaced by blank(s)...");
                    char[] tmp2 = new char[(int)numElements];
		    Arrays.fill(tmp2, ' ');
		    padValue = new String(tmp2);
		  }
                }
                out.print (" padValue=\""+padValue+"\"");
            }

            if (blockingFactor > 0)
                out.print (" blockingFactor=\""+blockingFactor+"\"");

            out.println ("/>");

        } catch (Exception e) {
            System.out.println ("** Error occurred in printVariables");
            System.out.println (e);
            System.exit (1);
        }
    }


    /*************************************************************************
     *  Print the CDF variable attributes
     ************************************************************************/
    private static void printVarAttributes (Variable v) {
        try {
            Attribute a = null;
            String    attrName = null;

            /**************************************************************
                Check and see if there are any variable attributes that
                are associated with the passed Variable.                   
             **************************************************************/
            Vector   va = v.getAttributes();
            if (va.size() == 0) return;      

            /*******************************************/
            /*  Process and print variable attributes  */
            /*******************************************/
            outputIndentation(indent);
            out.println ("<cdfVAttributes>");
            boolean iso8601_x;
            int iflag;
            GetMyCDFData gData;
            for (Enumeration e=va.elements(); e.hasMoreElements();) {
                 a = (Attribute) e.nextElement();
                 attrName = a.getName();
                 Object newAttrName = GetMyCDFData.replaceStringNameToXML ((Object)attrName);
                 try {
                   Entry entry = a.getEntry(v);
                   if (entry != null) {
                      indent++;
                      outputIndentation(indent);
                      // tag[0] - attribute tag  name
                      // tag[1] - attribute entry tag name

                      String[] tags =  getAttributeAndEntryTagNames();
                      out.println ("<"+tags[0]+" name=\""+newAttrName+"\">");
                      indent++;
                      if (mode.equalsIgnoreCase("cdfxdf")) {
                          outputIndentation(indent);
                          out.println ("<units><unitless/></units>");
                      }
                      long eDataType = entry.getDataType();
                      long eNumElements = entry.getNumElements();
                      Object data = entry.getData();
                      if (eDataType == CDF_EPOCH) iflag = 1;
                      else if (eDataType == CDF_EPOCH16) iflag = 2;
                      else if (eDataType == CDF_TIME_TT2000) iflag = 3;
                      else iflag = 0;
                      if (eDataType == CDF_TIME_TT2000) iso8601_x = true;
                      else iso8601_x = iso8601;
                      outputIndentation(indent);
                      out.print ("<"+tags[1]+" cdfDatatype=\""+
                                 CDFUtils.getStringDataType(eDataType)+"\"");
                      if (!choice) {
                         useCDATA2 = false;
                         if (eDataType == CDF_CHAR || eDataType == CDF_UCHAR) {
                           if (useCdata)
                             useCDATA2 = GetMyCDFData.checkStringData(data);
                           String signature = CDFUtils.getSignature(data);
                           if (useCDATA2) {
                             if (signature.charAt(0) == '[') {
                               out.print(" elementDelimiter=\""+defaultStrDelimiter+"\">");
                               String[] datax = (String[]) data;
                               for (int iu=0; iu<datax.length; ++iu) {
                                 if (iu != 0) out.print (defaultStrDelimiter);
                                 out.print (CDATA_BEGIN);
                                 out.print (GetMyCDFData.replaceStringDataToXML(datax[iu]));
                                 out.print (CDATA_END);
                               }
                             } else {
                               out.print (">"+CDATA_BEGIN);
                               out.print (GetMyCDFData.replaceStringDataToXML(data));
                               out.print (CDATA_END);
                             }
                           } else {
                             if (signature.charAt(0) == '[') {
                               int numStrs = Array.getLength(data);
                               out.print(" elementDelimiter=\""+defaultStrDelimiter+"\">");
                               String[] tmp1 = (String[]) data;
                               StringBuffer sb = new StringBuffer();
                               for (int jj=0; jj < numStrs;++jj) {
                                 if (jj == 0)
                                   sb.append(GetMyCDFData.replaceStringDataToXML(tmp1[jj]));
                                 else
                                   sb.append(defaultStrDelimiter+
                                             GetMyCDFData.replaceStringDataToXML(tmp1[jj]));
                               }
                               out.print(sb.toString());
                             } else {
                               out.print (">");
                               CDFUtils.printData (
                                        GetMyCDFData.replaceStringDataToXML(data),
                                        out, iflag, iso8601_x);
                             }
                           }
                         } else {
                           out.print (">");
                           CDFUtils.printData (data, out, iflag, iso8601_x,
                                               (withz?"Z":""));
                         }
                        out.println ("</"+tags[1]+">");
                      } else { // New schema
                         out.println (">");
                         if (eDataType == CDF_CHAR || eDataType == CDF_UCHAR) {
                           if (CDFUtils.getSignature(data).charAt(0) == '[') {
                               int numStrs = Array.getLength(data);
                               gData = GetMyCDFData.create(data, eDataType, 1L,
                                                           new long[] {(long)numStrs},
                                                           numStrs);
                           } else  
                             gData = GetMyCDFData.create(data, eDataType, 1L,
                                                         new long[] {1L},
                                                         ((String)data).length());
                           gData.dumpData2 (out, eDataType, VAL_START,
                                            VAL_END, indent,
                                            indentation, withz, false);
                         } else {
                           if (eNumElements == 1) {
                             gData = GetMyCDFData.create(data,
                                                         eDataType, 1L,
                                                         new long[] {1L}, 1L);
                             gData.dumpData2 (out, eDataType, VAL_START,
                                              VAL_END, indent,
                                              indentation, withz, false);
                           } else {
                             if (eNumElements > 1) 
                               gData = GetMyCDFData.create(data, eDataType,
                                                    1L,
                                                    new long[] {eNumElements},
                                                    1L);
                             else
                               gData = GetMyCDFData.create(data,
                                                           eDataType, 0L,
                                                           new long[] {0L},
                                                           1L);
                             gData.dumpData2 (out, eDataType, VAL_START,
                                              VAL_END, indent,
                                              indentation, withz, false);
                           }
                         }
                         outputIndentation(indent);
                         out.println ("</"+tags[1]+">");
                      }
                      indent--;
                      outputIndentation(indent);
                      out.println ("</"+tags[0]+">");
                      indent--;
                   }
                 } catch (CDFException ex) {
                 }
            }
            outputIndentation(indent);
            out.println ("</cdfVAttributes>");

        } catch (Exception e) {
            System.out.println ("** Error occurred in printVarAttributes");
            System.out.println (e);
            System.exit (1);
        }
    }


    /*************************************************************************
     *  Print the variable data.
     ************************************************************************/
    private static void printVarData (Variable v) {
        try {
            long    maxRec, numDims, numElements, numValuesToRead, dataType;
            long    recCount;
            long[]  dimSizes = {1L};
            long[]  dimIndices   = {0L};
            long[]  dimIntervals = {1L};
            String  elementDelimiter = " ";
            CDFData cdfdata = null;

            maxRec = v.getMaxWrittenRecord();


            /*******************************************************
               if varData = "no", do not extract variable data.
               if maxRec < 0, then the variable doesn't have data.
            ********************************************************/
            if (varData.equalsIgnoreCase("no") || maxRec < 0) { 
                indent--;
                outputIndentation(indent);
                out.println ("</variable>");
                return;
            }

            numDims      = v.getNumDims();
            dimSizes     = v.getDimSizes();
            numElements  = v.getNumElements();
            dataType     = v.getDataType();

            numValuesToRead = 1;
            if (numDims > 0) {
                dimIntervals = new long[dimSizes.length];
                dimIndices   = new long[dimSizes.length];
                for (int i=0; i < dimSizes.length; i++) {
                     dimIntervals[i] = 1;
                     dimIndices[i]   = 0;
                     numValuesToRead *= dimSizes[i];
                }
            }
            elementDelimiter = " ";

            outputIndentation(indent);
            out.println ("<cdfVarData>");

            if (mode.equalsIgnoreCase("cdfxdf")) 
                printXDFtags (v, maxRec, numValuesToRead);

            /********************************************************/
            /*  maxRec represents the last record number for this   */
            /*  variable, not the number of records.                */
            /*                                                      */
            /*  NOTE: maxRec starts at 0, so if the value of maxRec */
            /*        is 2, the actual number of records is 3.      */
            /*        If there are no records exists, the value of  */
            /*        maxRec is -1.                                 */
            /********************************************************/
            long incrementCounter, numRecordsToRead, recNo;
            long sparseRecord = v.getSparseRecords();

            if (sparseRecord == NO_SPARSERECORDS) 
                incrementCounter = (numDims == 0 ? 1000 : 200);
            else
                incrementCounter = 1;
            // for (int i=0; i <= maxRec; i++) {
            for (int i=0; i <= maxRec; i+=incrementCounter) {
                 if ((i+incrementCounter) > maxRec)
                     numRecordsToRead = maxRec - i +1;
                 else
                     numRecordsToRead = incrementCounter; 
                 if (numDims > 0)
                     cdfdata = v.getHyperDataObject((long) i, 
                                                    numRecordsToRead, 1L,
                                                    dimIndices,
                                                    dimSizes,
                                                    dimIntervals);
                 else 
                     cdfdata = v.getRecordsObject((long) i,
                                                   numRecordsToRead);
                 // Get the status of the last CDF call
                 long status = cdf.getStatus();

                 Object dataArray = cdfdata.getData();
                 useCDATA2 = false;
                 if ((dataType == CDF_CHAR || dataType == CDF_UCHAR) &&
                     useCdata)
                    useCDATA2 = GetMyCDFData.checkStringData(dataArray);
                 for (int j=0; j < numRecordsToRead; j++) {
                      Object datum;
                      if (numRecordsToRead > 1) 
                          if (dataType != CDF_EPOCH16)
                            datum = Array.get(dataArray, j);
                          else {
                            double[] epoch16 = new double[2];
                            epoch16[0] = ((Double) Array.get(dataArray, 2*j)).
                                          doubleValue();
                            epoch16[1] = ((Double) Array.get(dataArray, 2*j+1)).
                                          doubleValue();
                            datum = (Object) epoch16;
                          }
                      else 
                          datum = dataArray;
                      GetMyCDFData data = GetMyCDFData.create(datum, dataType,
                                                              numDims, dimSizes,
                                                              numElements);
                      String   recBeginTag;
                      boolean  elementFlag = false;
                      if (!mode.equalsIgnoreCase("cdfxdf")) {
                          StringBuffer recordTag = new StringBuffer("<record ");
                          recNo = i + j;
                          String recNum = "recNum=\""+recNo+"\"";
                          recordTag.append(recNum);
                          if (status != VIRTUAL_RECORD_DATA) {
                              if (dataType == CDF_CHAR || 
                                  dataType == CDF_UCHAR) { 
                                 if (strDelimiter == null) 
                                     elementDelimiter = defaultStrDelimiter; 
                                 else {
                                     elementDelimiter = strDelimiter;
                                     if (elementDelimiter.equals("\""))
                                         elementDelimiter = "&quot;";
                                 }
                                 if (elementDelimiter.equals("<element>")) {
                                     elementFlag = true;
                                     recordTag.append(recNum);
                                     recordTag.append(recNum);
                                 }
                                 else {
                                     if (numDims >= 1) {
                                       if (!choice) {
                                         recordTag.append(" elementDelimiter=\"");
                                         recordTag.append(elementDelimiter);
                                         recordTag.append("\"");
                                       }
                                     }
                                     recordTag.append(">");
                                 }
                                 recBeginTag = recordTag.toString();
                             }
                             else {
                                 elementDelimiter = " ";
                                 recBeginTag = "<record recNum=\""+recNo+"\">";
                             }
                             if (!choice)
                                data.dumpData (out, recBeginTag, "</record>",
                                               elementDelimiter, 
                                               elementFlag, indent, indentation,
                                               iso8601, withz, useCDATA2);
                             else {
                                outputIndentation(indent+1);
                                out.println (recBeginTag);
                                data.dumpData2 (out, dataType, VAL_START,
                                                VAL_END, indent+1,
                                                indentation, withz, useCDATA2);
                                outputIndentation(indent+1);
                                out.println ("</record>");

                             }
                         }   // if (status != VIRTUAL_RECORD_DATA)
                     }  
                     else {   // mode = cdfxdf
                       if (!choice)
                         data.dumpData (out, "", "", " ", 
                                        elementFlag, indent, indentation,
                                        iso8601, withz, useCDATA2);
                       else
                         data.dumpData2 (out, dataType, VAL_START, VAL_END,
                                         indent+1, indentation, withz, useCDATA2);
                     }
                 }   // end of for (int j=0; j < numRecordsToRead; j++)

            }     // end of "for (int i=0; i <= maxRec; i++)"
            if (mode.equalsIgnoreCase("cdfxdf")) { 
                outputIndentation(indent);
                out.println ("</data>");
                indent--;                 
                outputIndentation(indent);
                out.println ("</array>");
                indent--;                  
            }
            outputIndentation(indent);
            out.println ("</cdfVarData>");
            indent--;                   
            outputIndentation(indent);
            out.println ("</variable>");

        } catch (Exception e) {
            System.out.println ("** Error occurred in printVarData");
            System.out.println (e);
            System.exit (1);
        }
    }


    /*************************************************************************
     *  In the CDF domain, metadata and its description are called 
     *  attribute and entry, respectively.  Whereas, eXtensible Data 
     *  Format (XDF) labels them as "parameter" and "value".  
     *
     *  This routine returns appropriate tag names for metadata and its
     *  description based upon the requested translation mode that was
     *  entered from the command line.
     ************************************************************************/
    private static String[] getAttributeAndEntryTagNames () {
        String attrNames[] = new String[2]; 

        if (mode.equalsIgnoreCase("cdfxdf")) {
            attrNames[0] = "parameter";            // Attribute tag name
            attrNames[1] = "value";                // Attribute entry tag name
        }
        else {    // translation mode = dtd or xsd
            attrNames[0] = "attribute";
            attrNames[1] = "entry";
        }
        return attrNames;
    }
 

    /*****************************************************************/
    /*  Print the XDF data tags (i.e. array and its subordinates).   */ 
    /*  More detailed information is described in XDF_017.dtd.       */
    /*                                                               */
    /*  This routine is only called if the requested mode is         */
    /*  "cdfxdf" (i.e. java CDF2CDFML -mode:cdfxdf <cdf file name>.  */
    /*****************************************************************/
    private static void printXDFtags (Variable v,
                                      long maxRec, 
                                      long numValuesToRead) 
    {
        long  id, dataType;

        id       = v.getID();           // get the variable id
        dataType = v.getDataType();

        indent++;
        outputIndentation(indent);
        out.println ("<array>");
        indent++;
        outputIndentation(indent);
        out.println ("<units><unitless/></units>");

        outputIndentation(indent);
        out.println ("<dataFormat>");
        indent++;
        outputIndentation(indent);

        switch ((int) dataType) {
            case (int)CDF_CHAR:
            case (int)CDF_UCHAR:
            case (int)CDF_EPOCH:
	    case (int)CDF_EPOCH16:
	    case (int)CDF_TIME_TT2000:
                out.println ("<string length=\""+MAX_STR+"\"/>");
                break;

            case (int)CDF_BYTE:
            case (int)CDF_INT1:
            case (int)CDF_INT2:
            case (int)CDF_UINT1:
            case (int)CDF_INT4:
            case (int)CDF_UINT2:
            case (int)CDF_UINT4:
                out.println ("<integer width=\"10\"/>");
                break;

            case (int)CDF_REAL4:
            case (int)CDF_FLOAT:
            case (int)CDF_REAL8:
            case (int)CDF_DOUBLE:
                out.println ("<float width=\"18\" "+"precision=\"9\"/>");
                break;

            case (int)CDF_INT8:
                out.println ("<integer width=\"20\"/>");
                break;

            default:
                break;

        }  /* switch (dataType) */

        indent--;
        outputIndentation(indent);
        out.println ("</dataFormat>");

        outputIndentation(indent);
        out.println ("<axis axisId=\"recNoforVarnum"+id+"\">");
        indent++;
        outputIndentation(indent);
        out.println ("<axisUnits><unitless/></axisUnits>");
        outputIndentation(indent);
        out.println ("<valueList size=\""+(maxRec+1)+"\"/>");
        indent--;
        outputIndentation(indent);
        out.println ("</axis>");

        outputIndentation(indent);
        out.println ("<axis axisId=\"record"+id+"\">");
        indent++;
        outputIndentation(indent);
        out.println ("<axisUnits><unitless/></axisUnits>");
        outputIndentation(indent);
        out.println ("<valueList size=\""+numValuesToRead+"\"/>");
        indent--;
        outputIndentation(indent);
        out.println ("</axis>");

        outputIndentation(indent);
        out.println ("<read>");
        indent++;
        outputIndentation(indent);
        out.println ("<for axisIdRef=\"recNoforVarnum"+id+"\">");
        indent++;
        outputIndentation(indent);
        out.println ("<for axisIdRef=\"record"+id+"\">");
        indent++;
        outputIndentation(indent);
        out.println ("<textDelimiter repeatable=\"yes\" "+
                     "recordTerminator=\""+recordTerminator+"\"/>");
        indent--;
        outputIndentation(indent);
        out.println ("</for>");
        indent--;
        outputIndentation(indent);
        out.println ("</for>");
        indent--;
        outputIndentation(indent);
        out.println ("</read>");
        outputIndentation(indent);
        out.println ("<data>");
    }


    /**************************************************************/
    /*  Print variable attributes that do not have any variables  */
    /*  associated with them (i.e. empty attributes).             */
    /**************************************************************/
    private static void printOrphanAttributes() 
    {
        Vector emptyAttrs = cdf.getOrphanAttributes(); 
        
        if (emptyAttrs.size() > 0) {
            String attrName;
            outputIndentation(indent);
            out.println ("<orphanAttributes>");
            indent++;
            for (Enumeration e=emptyAttrs.elements(); e.hasMoreElements();) {
                 Attribute a = (Attribute) e.nextElement();
                 attrName = a.getName();
                 outputIndentation(indent);
                 Object newAttrName = GetMyCDFData.replaceStringNameToXML ((Object)attrName);
                 if (mode.equalsIgnoreCase("cdfxdf")) {
                     out.println ("<parameter name=\""+newAttrName+"\">");
                     indent++;
                     outputIndentation(indent);
                     out.println ("<units><unitless/></units>");
                     outputIndentation(indent);
                     out.println ("<value>></value>");
                     indent--;
                     outputIndentation(indent);
                     out.println ("</parameter> ");
                 }
                 else {
                   out.println ("<attribute name=\""+newAttrName+"\"></attribute>");
/*
                   if (attrName.indexOf("\"") < 0)
                     out.println ("<attribute name=\""+attrName+"\"/>");
                   else
                     if (attrName.indexOf("'") < 0)
                       out.println ("<attribute name='"+attrName+"'/>");
                     else
                       out.println ("<attribute name=#"+attrName+"#/>");
*/
                 }
            }
            indent--;
            outputIndentation(indent);
            out.println ("</orphanAttributes>");
        }
    }


    /*************************************************************************
     *  This method returns the output file name if one is not provided 
     *  through the -output option from the command line. 
     ************************************************************************/
    private static String getDefaultOutputFileName () {
        String fileName;
        int    loc1 = inFile.lastIndexOf(".cdf");
        int    loc2 = inFile.lastIndexOf(".CDF");
        boolean extCDF = inFile.endsWith(".cdf") ||
                         inFile.endsWith(".CDF");

        // Input file name given from the command line doesn't have
        // .cdf or .CDF extension in it.  
        if (!extCDF) 
            fileName = inFile + ".xml";

        else {         // Remove the .cdf or .CDF extension
            if (loc1 != -1) {
                fileName = inFile.substring(0,loc1);
                fileName = fileName + ".xml";
            }
            else {
                fileName = inFile.substring(0,loc2);
                fileName = fileName + ".XML";
            }
        }
        return fileName;
    }


    /*************************************************************************
     *  This method removes the file path from the given file name and
     *  returns the file name.
     ************************************************************************/
    private static String removeFilePath (String fileName) {
        String fileSeparator = System.getProperty("file.separator");
        int loc = fileName.lastIndexOf(fileSeparator);
        fileName = fileName.substring(loc+1);

        return fileName;
    }


    /************************************************************************* 
     *  If an invalid option is entered from the command line, this routine 
     *  is invoked to print an appropriate message followed by its proper
     *  syntax.
     ************************************************************************/
    private static void exit (String msg) {
        if (!msg.equals("")) System.out.println (msg);
        usage();
        System.exit(1);
    }


    /************************************************************************* 
     *  Print the proper syntax for invoking this program. 
     ************************************************************************/
    private static void usage() {
       System.out.println ("\nDescription:");
       System.out.println ("    This program exports the contents of a CDF "+
                           "file into an XML file");
       System.out.println ("    that conforms to cdf.dtd (default mode), "+
                           "cdf.xsd, or cdfxdf.dtd.");
       System.out.println ("    The file conforms to the CDF Document Type "+
                           "Definition (cdf.dtd or");
       System.out.println ("    cdfxdf.dtd) or the CDF XML Schema "+
                           "(cdf.xsd) is called a CDF markup");
       System.out.println ("    language (CDFML) file.");
       System.out.println ("");
       System.out.println ("      - cdf.dtd is a pure version of CDFML "+
                           "described in DTD."); 
       System.out.println ("      - cdf.xsd is a pure version of CDFML "+
                           "described in schema."); 
       System.out.println ("      - cdfxdf.dtd is a CDFML that employs "+
                           "some of the eXtensible Data ");
       System.out.println ("        Format (XDF) tags within CDF tags.");
       System.out.println (" ");
       System.out.println ("    The name of the input CDF file is used "+
                           "as the default output file name.");
       System.out.println (" ");
       System.out.println ("Usage: java CDF2CDFML [Options] cdfFileName ");
       System.out.println (" ");
       System.out.println ("Options: ");
       System.out.println ("   -mode:[dtd | xsd | cdfxdf].  "+
                           "-mode:dtd is the default.");
       System.out.println ("       -mode:dtd creates a CDFML file that "+
                           "conforms to cdf.dtd (DTD)");
       System.out.println ("       -mode:xsd creates a CDFML file that "+
                           "conforms to cdf.xsd (schema)");
       System.out.println ("       -mode:cdfxdf creates a CDFML file that "+
                           "conforms to cdfxdf.dtd");
       System.out.println (" ");
       System.out.println ("   -varData:[yes | no].  -varData:yes is "+
                           "the default.");
       System.out.println ("       -varData:yes extracts the variable data"); 
       System.out.println ("       -varData:no doesn't extract the "+
                           "variable data"); 
       System.out.println (" ");
       System.out.println ("   -[output | xml]:[outputFileName | STDOUT]");
       System.out.println ("       -[output|xml]:outputFileName");
       System.out.println ("           The name of the input CDF file is used "+
                           "as the default output file");
       System.out.println ("           name.  Use this option to specify a "+
                           "different output file name.");
       System.out.println ("       -[output|xml]:STDOUT");
       System.out.println ("           Displays the XML output on the screen.");
       System.out.println (" ");
       System.out.println ("   -indentation:n (n is the number of spaces to "+
                           "skip between levels)");
       System.out.println (" ");
       System.out.println ("   -[showProgress | Progress]");
       System.out.println ("       This option displays the processing "+
                           "progress on the screen.");
       System.out.println ("       It shows how many CDF variables are there "+
                           "to process and which");
       System.out.println ("       variable is being processed.");
       System.out.println (" ");
       System.out.println ("   -debug:true");
       System.out.println ("       This option displays the specifications to "+
                           "run the application.");
       System.out.println (" ");
       System.out.println ("   -strDelimiter:[\"userDefinedDelimiter\" | "+
                           "\"<element>\"]");
       System.out.println ("       The string delimiter to be used for an "+
                           "array of string data.");
       System.out.println ("");
       System.out.println ("       -strDelimiter:\"<element>\" option "+
                           "describes data between the");
       System.out.println ("           <element index=\"indexNum\"> and "+
                           "</element> tags.");
       System.out.println ("");
       System.out.println ("   -noniso8601");
       System.out.println ("      This option directs the date/time output not in ISO 8601");
       System.out.println ("      format for CDF_EPOCH and CDF_EPOCH16 data types as:");
       System.out.println ("        dd-MMM-yyyy hh:mm:ss.mmm and");
       System.out.println ("        dd-MMM-yyyy hh:mm:ss.mmm.uuu.nnn.ppp.");
       System.out.println ("      Otherwise, the default has the following ISO8601 format:");
       System.out.println ("        yyyy-mm-ddThh:mm:ss.mmm for CDF_EPOCH");
       System.out.println ("        yyyy-mm-ddThh:mm:ss.mmmuuunnnppp for CDF_EPOCH16");
       System.out.println ("      For CDF_TIME_TT2000, it is always in ISO8601 form:");
       System.out.println ("        yyyy-mm-ddThh:mm:ss.mmmuuunnn");
       System.out.println ("");
       System.out.println ("   -withZ");
       System.out.println ("      This option goes with ISO8601 date output to add an extra \"Z\" to");
       System.out.println ("      the end of the time form. The default is no.");
       System.out.println ("");
       System.out.println ("   -datatype");
       System.out.println ("      This option will make an enhanced form of XML output, placing each");
       System.out.println ("      data value, in each \"value\" element line, along with its data type.");
       System.out.println ("      The data types follow http://www.w3.org/2001/XMLSchema. For");
       System.out.println ("      dimensional values, they are wrapped around around \"array\" element.");
       System.out.println ("      The valid data types are as follows:");
       System.out.println ("       \"dateTime\"     \"string\"         \"float\"");
       System.out.println ("       \"double\"       \"byte\"           \"unsignedByte\"");
       System.out.println ("       \"short\"        \"unsignedShort\"  \"int\"");
       System.out.println ("       \"unsignedInt\"  \"long\"");
       System.out.println ("      Note: \"dateTime\" represents data from CDF_EPOCH, CDF_EPOCH16");
       System.out.println ("            or CDF_TIME_TT2000 epoch data types.");
       System.out.println ("      For example, a CDF_INT2 variable (1-dim of 3 elements),");
       System.out.println ("      its default output for a record has this simple form:");
       System.out.println ("        <record recNum=\"0\">1 2 3</record>");
       System.out.println ("      With this \"-datatype\" option, the output will look as follows:");
       System.out.println ("        <record recNum=\"0\">");
       System.out.println ("           <array>");
       System.out.println ("             <value xsi:type=\"xsd:short\">1</value>");
       System.out.println ("             <value xsi:type=\"xsd:short\">2</value>");
       System.out.println ("             <value xsi:type=\"xsd:short\">3</value>");
       System.out.println ("           </array>");
       System.out.println ("        </record>");
       System.out.println ("");
       System.out.println ("   -[delete | nodelete]");
       System.out.println ("      Use this option to control whether to replace the XML if it");
       System.out.println ("      already exists. The default is to replace.");
       System.out.println ("");
       System.out.println ("   -cdata");
       System.out.println ("      This option only applies to string typed variable data and attributes.");
       System.out.println ("      By default, data will be displayed as is with an exception of a");
       System.out.println ("      special character: \"<\", which will be replaced by the standard XML");
       System.out.println ("      representation of \"&lt;\". With this option, data will be wrapped within");
       System.out.println ("      the strings: \"<![[CDATA\" and \"]]>\" only if a string has that special");
       System.out.println ("      \"<\" in it.");
       System.out.println ("");
       System.out.println ("Examples: ");

       System.out.println ("   1) java CDF2CDFML test.cdf (same as "+
                           "java CDF2CDFML -mode:dtd test.cdf)");
       System.out.println ("   2) java CDF2CDFML -mode:cdfxdf test.cdf");
       System.out.println ("   3) java CDF2CDFML -output:mytest.xml test.cdf");
       System.out.println ("   4) java CDF2CDFML -indentation:2 test.cdf "+
                           "(use 2 spaces for indentation)");
       System.out.println ("          The default indentation is 3 spaces "+ 
                           "between levels");
       System.out.println ("   5) java CDF2CDFML -strDelimiter:\"'\" "+
                           "-output:test.xml test.cdf ");
       System.out.println ("   6) java CDF2CDFML -strDelimiter:\\\"\"\" "+
                           "-output:test.xml test.cdf ");
       System.out.println ("   7) java CDF2CDFML -strDelimiter:\"<element>\" "+
                           "-output:test.xml test.cdf ");
       System.out.println (" ");
    }

}
