//
// TextAdapter.java
//

/*
VisAD system for interactive analysis and visualization of numerical
data.  Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/

package visad.data.text;

import java.io.IOException;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import visad.Set;

import java.net.URL;

import visad.*;
import visad.VisADException;
import visad.data.in.ArithProg;
import visad.util.DataUtility;

import java.util.regex.*;


/** this is an VisAD file adapter for comma-, tab- and blank-separated
  * ASCII text file data.  It will attempt to create a FlatField from
  * the data and descriptions given in the file and/or the constructor.
  *
  * The text files contained delimited data.  The delimiter is 
  * determined as follows:  if the file has a well-known extension
  * (.csv, .tsv, .bsv) then the delimiter is implied by the extension.
  * In all other cases, the delimiter for the data (and for the
  * "column labels") is determined by reading the first line and
  * looking, in order, for a tab, comma, or blank.  Which ever one
  * is found first is taken as the delimiter.
  *
  * Two extra pieces of information are needed:  the VisAD "MathType"
  * which is specified as a string (e.g., (x,y)->(temperature))
  * and may either be the first line of the file or passed in through
  * one of the constructors.
  *
  * The second item are the "column labels" which contain the names
  * of each field in the data.  The names of all range components
  * specified in the "MathType" must appear.  The names of domain
  * components are optional.  The values in this string are separated
  * by the delimiter, as defined above.
  *
  * See visad.data.text.README.text for more details.
  * 
  * @author Tom Whittaker
  * 
  */
public class TextAdapter {

 private static final String ATTR_COLSPAN = "colspan";
 private static final String ATTR_VALUE   = "value";
 private static final String ATTR_OFFSET = "off";
 private static final String ATTR_ERROR  = "err";
 private static final String ATTR_SCALE = "sca";
 private static final String ATTR_POSITION ="pos";
 private static final String ATTR_FORMAT = "fmt";
 private static final String ATTR_TIMEZONE = "tz";
 private static final String ATTR_UNIT= "unit";
 private static final String ATTR_MISSING = "mis";
 private static final String ATTR_INTERVAL = "int";


  private static final String COMMA = ",";
  private static final String SEMICOLON = ";";
  private static final String TAB = "\t";
  private static final String BLANK = " ";
  private static final String BLANK_DELIM = "\\s+";



  private FlatField ff = null;
  private Field field = null;
  private boolean debug = false;
  private String DELIM;
  private boolean DOQUOTE = true;
  private boolean GOTTIME = false;


  HeaderInfo []infos;

  double[] rangeErrorEstimates;
  Unit[] rangeUnits;
  Set[] rangeSets;
  double[] domainErrorEstimates;
  Unit[] domainUnits;


  int[][] hdrColumns;
  int[][] values_to_index;

  private Hashtable properties;


  private boolean onlyReadOneLine = false;


  private Pattern skipPattern;

  /**If this is defined then when processing each tuple pass the processor the tuple
     and do not try to create the field */
  private StreamProcessor streamProcessor;

  /** Create a VisAD FlatField from a local Text (comma-, tab- or 
    * blank-separated values) ASCII file
    * @param filename name of local file.
    * @exception IOException if there was a problem reading the file.
    * @exception VisADException if an unexpected problem occurs.
    */
  public TextAdapter(String filename) throws IOException, VisADException {
    this(filename, null, null);
  }

  /** Create a VisAD FlatField from a local Text (comma-, tab- or 
    * blank-separated values) ASCII file
    * @param filename name of local file.
    * @param map the VisAD "MathType" as a string defining the FlatField
    * @param params the list of parameters used to define what columns
    *  of the text file correspond to what MathType parameters.
    * @exception IOException if there was a problem reading the file.
    * @exception VisADException if an unexpected problem occurs.
    */
  public TextAdapter(String filename, String map, String params) 
                         throws IOException, VisADException {
    InputStream is = new FileInputStream(filename);
    DELIM = getDelimiter(filename);
    readit(is, map, params);
  }

  /** Create a VisAD FlatField from a remote Text (comma-, tab- or 
    * blank-separated values) ASCII file
    *
    * @param url File URL.
    * @exception IOException if there was a problem reading the file.
    * @exception VisADException if an unexpected problem occurs.
    */
  public TextAdapter(URL url) throws IOException, VisADException {
    this(url, null, null);
  }

  /** Create a VisAD FlatField from a local Text (comma-, tab- or 
    * blank-separated values) ASCII file
    * @param url File URL.
    * @param map the VisAD "MathType" as a string defining the FlatField
    * @param params the list of parameters used to define what columns
    *  of the text file correspond to what MathType parameters.
    * @exception IOException if there was a problem reading the file.
    * @exception VisADException if an unexpected problem occurs.
    */
  public TextAdapter(URL url, String map, String params) 
                        throws IOException, VisADException {
    DELIM = getDelimiter(url.getFile());
    InputStream is = url.openStream();
    readit(is, map, params);
  }


  /** Create a VisAD FlatField from a local Text (comma-, tab- or 
    * blank-separated values) ASCII file
    * @param inputStream The input stream to read from
    * @param delimiter the delimiter
    * @param map the VisAD "MathType" as a string defining the FlatField
    * @param params the list of parameters used to define what columns
    *  of the text file correspond to what MathType parameters.
    * @exception IOException if there was a problem reading the file.
    * @exception VisADException if an unexpected problem occurs.
    */
  public TextAdapter(InputStream inputStream, String delimiter, String map, String params) 
                         throws IOException, VisADException {
      this(inputStream, delimiter, map,params,false);
  }


  /** Create a VisAD FlatField from a local Text (comma-, tab- or 
    * blank-separated values) ASCII file
    * @param inputStream The input stream to read from
    * @param delimiter the delimiter
    * @param map the VisAD "MathType" as a string defining the FlatField
    * @param params the list of parameters used to define what columns
    *  of the text file correspond to what MathType parameters.
    * @param onlyReadOneLine If true then only read one line of data. This is used so client code can
    * read the meta data.
    * @exception IOException if there was a problem reading the file.
    * @exception VisADException if an unexpected problem occurs.
    */

  public TextAdapter(InputStream inputStream, String delimiter, String map, String params,boolean onlyReadOneLine) 
                         throws IOException, VisADException {
      this(inputStream, delimiter, map, params, null, onlyReadOneLine);
  }

  /** Create a VisAD FlatField from a local Text (comma-, tab- or 
    * blank-separated values) ASCII file
    * @param inputStream The input stream to read from
    * @param delimiter the delimiter
    * @param map the VisAD "MathType" as a string defining the FlatField
    * @param params the list of parameters used to define what columns
    *  of the text file correspond to what MathType parameters.
    * @param properties properties
    * @param onlyReadOneLine If true then only read one line of data. This is used so client code can
    * read the meta data.
    * @exception IOException if there was a problem reading the file.
    * @exception VisADException if an unexpected problem occurs.
    */

  public TextAdapter(InputStream inputStream, String delimiter, String map, String params,Hashtable properties, boolean onlyReadOneLine) 
                         throws IOException, VisADException {
      this(inputStream, delimiter, map, params, properties, onlyReadOneLine, null);
  }


  /** Create a VisAD FlatField from a local Text (comma-, tab- or 
    * blank-separated values) ASCII file
    * @param inputStream The input stream to read from
    * @param delimiter the delimiter
    * @param map the VisAD "MathType" as a string defining the FlatField
    * @param params the list of parameters used to define what columns
    *  of the text file correspond to what MathType parameters.
    * @param properties properties
    * @param onlyReadOneLine If true then only read one line of data. This is used so client code can
    * read the meta data.
    * @param skipPatternString if non-null then skip any line that matches this pattern
    * @exception IOException if there was a problem reading the file.
    * @exception VisADException if an unexpected problem occurs.
    */
  public TextAdapter(InputStream inputStream, String delimiter, String map, String params,Hashtable properties, boolean onlyReadOneLine,String skipPatternString) 
                         throws IOException, VisADException {
      this(inputStream, delimiter,  map, params, properties, onlyReadOneLine, skipPatternString,null); 
  }



  /** Create a VisAD FlatField from a local Text (comma-, tab- or 
    * blank-separated values) ASCII file
    * @param inputStream The input stream to read from
    * @param delimiter the delimiter
    * @param map the VisAD "MathType" as a string defining the FlatField
    * @param params the list of parameters used to define what columns
    *  of the text file correspond to what MathType parameters.
    * @param properties properties
    * @param onlyReadOneLine If true then only read one line of data. This is used so client code can
    * read the meta data.
    * @param skipPatternString if non-null then skip any line that matches this pattern
    * @param streamProcessor Optional processor of the Tuple stream for point obs
    * @exception IOException if there was a problem reading the file.
    * @exception VisADException if an unexpected problem occurs.
    */
   public TextAdapter(InputStream inputStream, String delimiter, String map, String params,Hashtable properties, boolean onlyReadOneLine,String skipPatternString,StreamProcessor streamProcessor) 
                         throws IOException, VisADException {
    this.onlyReadOneLine = onlyReadOneLine;
    this.streamProcessor = streamProcessor;
    DELIM = delimiter;
    this.properties = properties;
    if(skipPatternString!=null && skipPatternString.length()>0) {
        skipPattern = Pattern.compile(skipPatternString);
    }
    readit(inputStream, map, params);
  }






  public static  String getDelimiter(String filename) {
    if(filename == null) return null;
    filename = filename.trim().toLowerCase();
    if (filename.endsWith(".csv")) return COMMA;
    if (filename.endsWith(".tsv")) return TAB;
    if (filename.endsWith(".bsv")) return BLANK;    
    return null;
  }


  /**
   * Is the given text line a comment
   *
   * @return is it a comment line
   */
  public static boolean isComment(String line) {
    return (line.startsWith("#") || 
            line.startsWith("!") || 
            line.startsWith("%") || 
            line.length() < 1);
  }

    public static  String readLine(BufferedReader bis) 
        throws IOException {
      while (true) {
        String line = bis.readLine();
        if (line == null) return null;
        if (!isText(line)) return null;
        if (isComment(line)) continue;
        return line.trim();
      }
    }


  void readit(InputStream is, String map, String params) 
                              throws IOException, VisADException {
    // read the ASCII file, using commas as field separators
    // first line is a header line

    List realTypes = new ArrayList();
    ff = null;
    field = null;

    if (debug) System.out.println("####   Text Adapter v2.x running");

    BufferedReader bis = new BufferedReader(new InputStreamReader(is));

    // mapping defines how the names are mapped
    // for example:   (x,y) => (one, two, three)

    String maps = null;
    if (map == null) {
      maps = readLine(bis);
      if(maps != null) {
          maps = maps.trim();
      }
    } else {
      maps = map;
    }

    if (maps != null) {
       maps = makeMT(maps);
    }
    if (maps == null) {
      throw new visad.data.BadFormException(
        "TextAdapter: Invalid or missing MathType");
    }

    ListnameChanges = new ArrayList();


    if (debug) System.out.println("Specified MathType = "+maps);

    // but first, we need to get the column headers because they
    // may have [units] associated with them.  The column headers
    // primarily define where the data are.

    String hdr = null;
    if (params == null) {
      hdr = readLine(bis);
    } else {
      hdr = params;
    }

    String hdrDelim = DELIM;
    if (DELIM == null) {
      if (hdr.indexOf(BLANK) != -1) hdrDelim = BLANK_DELIM; 
      if (hdr.indexOf(COMMA) != -1) hdrDelim = COMMA; 
      if (hdr.indexOf(SEMICOLON) != -1) hdrDelim = SEMICOLON; 
      if (hdr.indexOf(TAB) != -1) hdrDelim = TAB; 

      if (debug) System.out.println("Using header delimiter = "+ hdrDelim + "("+
                                     (hdrDelim.getBytes())[0] + ")");
    }
    
    // squeeze out extra blank spaces
    if (hdrDelim.equals(BLANK) || hdrDelim.equals(BLANK_DELIM)) {
  	  //System.out.println("line before squeeze: " + line);
        hdr = hdr.replaceAll("\\s++", " ").trim();
  	  //System.out.println("line after squeeze: " + line);
    }


    String[] sthdr = hdr.split(hdrDelim);
    // since blanks separate the metadata, if we have a blank 
    // delimiter, we run into problems.  Loop through the header and
    // put humpty dumpty back together again
    if (hdrDelim.equals(BLANK_DELIM) || hdrDelim.equals(BLANK)) {
        List chunks = new ArrayList();
        for (int i = 0; i < sthdr.length; i++) {
            String subchunk = sthdr[i].trim();
            int m = subchunk.indexOf("[");
            if (m == -1) {
                chunks.add(subchunk);
                continue;
            }
            // have "[", find "]"
            int m2 = subchunk.indexOf("]");
            while (m2 < 0 && i < sthdr.length) {
               i++;
               subchunk += " " +sthdr[i].trim();
               m2 = subchunk.indexOf("]");
            }
            chunks.add(subchunk);
        }
        sthdr = (String[]) chunks.toArray(new String[chunks.size()]);
    }
    int nhdr = sthdr.length;
    infos    = new HeaderInfo[nhdr];
    for(int i=0;i= name.length()) {
          infos[i].name = name.substring(0,m).trim();
        } else {
          infos[i].name = (name.substring(0,m)+name.substring(m2+1)).trim();
        }

        // 0. Remove any spaces around the "=" signs
        String cl = name.substring(m+1,m2).trim();
        cl = cl.replaceAll(" +=","=");
        cl = cl.replaceAll("= +","=");


        String[] stcl = cl.split(BLANK_DELIM);
        int ncl = stcl.length;

        if (ncl == 1 && cl.indexOf("=") == -1) {
          hdrUnitString = cl;  // backward compatible...

        } else {
          for (int l = 0; l  < ncl; l++) {
            String s = stcl[l];
            String[] sts = s.split("=");
            if (sts.length != 2) {
              throw new VisADException("TextAdapter: Invalid clause in: "+s);
            }
            String tok = sts[0];
            String val = sts[1];
            
            // check for quoted strings
            if (val.startsWith("\"")) {

              // see if ending quote also fetched
              if (val.endsWith("\"")) {
                String v2 = val.substring(1,val.length()-1);
                val = v2;

              } else {
                // if not, then reparse stcl to suck up spaces...
                try {
                  String v2="";
                  for (int q=l+1; q < ncl; q++) {
                      String  vTmp = stcl[q];
                      // find next token that has a " in it
                      int pos = vTmp.indexOf("\"");
                      l++;
                      if (pos < 0) {  // no "
                          v2 = v2+" "+vTmp;
                      } else {
                          v2 = v2+" "+vTmp.substring(0,pos);
                          break;
                      }
                  }
                  String v3 = val.substring(1)+v2;
                  val = v3;

                //} catch (NoSuchElementException nse2) {
                } catch (ArrayIndexOutOfBoundsException nse2) {
                  val="";
                }
              }
            }

            if (debug) System.out.println("####   tok = "+tok+ " val = '"+val+"'");

            if (tok.toLowerCase().startsWith(ATTR_UNIT)) {
              hdrUnitString = val;

            } else if (tok.toLowerCase().startsWith(ATTR_MISSING)) {
                infos[i].missingString = val.trim();
              try {
                infos[i].missingValue = Double.parseDouble(val);
              } catch (java.lang.NumberFormatException me) {
                  infos[i].missingValue = Double.NaN;
              }
            } else if (tok.toLowerCase().startsWith(ATTR_INTERVAL)) {
              infos[i].isInterval = -1;
              if (val.toLowerCase().startsWith("t")) infos[i].isInterval = 1;
              if (val.toLowerCase().startsWith("f")) infos[i].isInterval = 0;
              if (infos[i].isInterval == -1) {
                throw new VisADException("TextAdapter: Value of \'interval\' must be \'true\' or \'false\'");
              }
            } else if (tok.toLowerCase().startsWith(ATTR_ERROR)) {
                infos[i].errorEstimate = Double.parseDouble(val);
            } else if (tok.toLowerCase().startsWith(ATTR_SCALE)) {
                infos[i].scale = Double.parseDouble(val);
            } else if (tok.toLowerCase().startsWith(ATTR_OFFSET)) {
              infos[i].offset = Double.parseDouble(val);
            } else if (tok.toLowerCase().startsWith(ATTR_VALUE)) {
              infos[i].fixedValue = val.trim();
              numHdrValues++;
            } else if (tok.toLowerCase().startsWith(ATTR_COLSPAN)) {
              infos[i].colspan = (int)Double.parseDouble(val.trim());
            } else if (tok.toLowerCase().startsWith(ATTR_POSITION)) {
              String[] stp = val.split(":");
              if (stp.length != 2) {
                throw new VisADException("TextAdapter: invalid Position parameter in:"+s);
              }
              hdrColumns[0][i] = Integer.parseInt(stp[0].trim());
              hdrColumns[1][i] = Integer.parseInt(stp[1].trim());

            } else if (tok.toLowerCase().startsWith(ATTR_FORMAT)) {
                infos[i].formatString = val.trim();
            } else if (tok.toLowerCase().startsWith(ATTR_TIMEZONE)) {
                infos[i].tzString = val.trim();
            } else {
              throw new VisADException("TextAdapter: invalid token name: "+s);
            }

          }
        }

      }



      if(properties!=null) {
          for(int headerIdx=0;headerIdx -1) { // if no list, get from file
            values_to_index[0][countValues] = k;
            values_to_index[2][countValues] = i;
          }

          break;
       }

    } 

    if (gotName) continue;

    // or see if its a range name...

    for (int k=0; k=0) {  // fixed value
        String name  = line.substring(0,index).trim();
        String value  = line.substring(index+1).trim();
        boolean foundIt = false;
        for(int paramIdx=0;paramIdx= nhdr) {  // are we past where domain would be found?

            if (!gotFirst) {
              throw new VisADException(
                        "TextAdapter: Cannot find first raster value");
            }

            rvaluePointer ++;
            rValues[rvaluePointer] = getVal(sa, irange);

          } else {  // or are we still looking for domain?
          
            if (values_to_index[0][i] != -1) {
              dValues[values_to_index[0][i]] = getVal(sa, i);
            }

            if (gotFirst) {  // already gathering data
              rvaluePointer ++;
              rValues[rvaluePointer] = getVal(sa, irange);

            } else {
               if (values_to_index[1][i] != -1) {
                 // cannot dimension the array until we have found
                 // the first set of range values!!
                 rValues = new double[n - i];
                 irange = i;
                 rValues[rvaluePointer] = getVal(sa, irange);
                 gotFirst = true;
               }
            }

          }
        }
         
      } else {  // is probably NOT a raster
        dataArray = new Data[numRng];
        if (debug) System.out.println("probably not a raster...");
	if (streamProcessor==null) {
	    rValues = new double[numRng];
	}
        MathType thisMT;
        if (n > nhdr) n = nhdr; // in case the # tokens > # parameters
        n +=numHdrValues;

        int tokenIdx = 0;   // token counter


        for (int i=0; i= tokens.length) {   // more params than tokens
	      sa = "";                    // need to have a missing value
	  } else {
	      sa = tokens[tokenIdx++].trim();
	      int moreColumns = infos[i].colspan-1;
	      while (moreColumns>0) {
		  sa = sa + " " + tokens[tokenIdx++].trim();
		  moreColumns--;
	      }
          }

          String sThisText;

          if (values_to_index[0][i] != -1) {
	      if(dValues!=null)
		  dValues[values_to_index[0][i]] = getVal(sa, i);
          } else if (values_to_index[1][i] != -1) {
            int tupleIndex = values_to_index[1][i];
            int infosIndex = values_to_index[2][i];
            thisMT = rangeType.getComponent(tupleIndex);
            if (thisMT instanceof TextType) {
              // if Text, then check for quoted string
              if (sa.startsWith("\"")) {
                if (sa.endsWith("\"")) {  // if single token ends with quote
                  String sa2 = sa.substring(1,sa.length()-1);
                  sThisText = sa2;
                } else {
                  // TODO:  work on this
                  try {
                    String delim = 
                        dataDelim.equals(BLANK_DELIM) ? BLANK : dataDelim;
                    String sa2="";
                    for (int q=tokenIdx; q < tokens.length; q++) {
                        String  saTmp = tokens[q];
                        // find next token that has a " in it
                        int pos = saTmp.indexOf("\"");
                        tokenIdx++;
                        if (pos < 0) {  // no dataDelim
                            sa2 = sa2+delim+saTmp;
                        } else {
                            sa2 = sa2+saTmp.substring(0,pos);
                            //tokens[tokenIdx] = saTmp.substring(pos+1);
                            break;
                        }
                    }

                    //sThisText = sa.substring(1)+sa2;
                    sThisText = sa.substring(1)+delim+sa2;
                  //} catch (NoSuchElementException nse) {
                  } catch (ArrayIndexOutOfBoundsException nse) {
                    sThisText = "";
                  }
                }

                if (debug) System.out.println("####   Text value='"+sThisText+"'");

              // if not quoted, then take "as is"
              } else {
                sThisText = sa;
              }


              // now make the VisAD Data 
              try {
                dataArray[tupleIndex] = 
                        new Text((TextType)thisMT, sThisText);

                if (debug) System.out.println("dataArray[" + 
                          tupleIndex + "] = " + 
                          dataArray[tupleIndex]);
              } catch (Exception e) {
                System.out.println(" Exception converting " + 
                                       thisMT + " to TextType " + e);
              }
            // if not Text, then treat as numeric
            } else {
		//              if(true) continue;
              double value = getVal(sa,i);

	      if(rValues!=null)
		  rValues[tupleIndex] = value;
              try {
                  if(prototypeReals[i]==null) {
                      prototypeReals[i] =    new Real((RealType) thisMT, value, infos[infosIndex].unit);
                  }
                  dataArray[tupleIndex] = 
                      prototypeReals[i].cloneButValue(value);
                  if(debug)System.out.println("dataArray[" + 
                    tupleIndex + "] = " + 
                    dataArray[tupleIndex]);

              } catch (Exception e) {
                System.out.println(" Exception converting " + thisMT + " " + e);
                e.printStackTrace();
              }
            }
          }
        }
      }

      if(tryToMakeTuple) {
        try {
            if (dataArray != null) {
		if (streamProcessor!=null) {
		    streamProcessor.processValues(dataArray);
		} else {
		    if(tupleType == null) {
			tuple = new Tuple(dataArray);
			tupleType = (TupleType)tuple.getType();
		    } else {
			tuple = new Tuple(tupleType, dataArray, false, false);
		    }
		}
            }
        } catch (visad.TypeException te) {
          // do nothing: it means they are all reals
          // tuple = new RealTuple(dataArray);
          tuple = null;
          tryToMakeTuple = false; 
        } catch(NullPointerException npe) {
            for(int i=0;i 1) numVal = numElements;
    if (numDom > 2 && numRng == 1 && numElements == 1) {
      numVal = numSamples / (2 * numDom);
      keepConstant = true;
    }

    for (int i=0; i");
    if (k < 0) {
        //      System.out.println("TextAdapter: invalid MathType form; -> required");
      return null;
    }

    StringBuffer sb = new StringBuffer("");
    for (int i=0; i");

    if (t.charAt(k-1) != ')' ) {
      if (t.charAt(k+2) != '(' ) {
        String t2 = "("+t.substring(0,k) + ")->("+t.substring(k+2)+")";
        t = t2;
      } else {
        String t2 = "("+t.substring(0,k) + ")"+t.substring(k);
        t = t2;
      }

    } else if (t.charAt(k+2) != '(' ) {
      String t2 = t.substring(0,k+2)+"("+t.substring(k+2)+")";
      t = t2;
    }

    if (!t.startsWith("((") ) {
      String t2= "("+t+")";
      t = t2;
    }

    return t;
  }

  private static final boolean isText(String s)
  {
    final int len = (s == null ? -1 : s.length());

    if (len <= 0) {
      // well, it's not really *binary*, so pretend it's text
      return true;
    }

    for (int i = 0; i < len; i++) {
      final char ch = s.charAt(i);
      if (Character.isISOControl(ch) && !Character.isWhitespace(ch)) {
        // we might want to special-case formfeed/linefeed/newline here...
        return false;
      }
    }

    return true;
  }


  /** 
   * generate a DateTime from a string
   * @param string - Formatted date/time string
   *
   * @return - the equivalent VisAD DateTime for the string
   *
   * (lifted from au.gov.bom.aifs.common.ada.VisADXMLAdapter.java)
   */
  private static visad.DateTime makeDateTimeFromString(String string, 
                                                       String format, String tz)
    throws java.text.ParseException
  {
    visad.DateTime dt = null;
    // try to parse the string using the supplied DateTime format
    try {
      if(dateParsers!=null) {
         for(int i=0;i values[0][0];
    if (ascending) {
      for (int i=1; i values[0][i - 1]) {
          ordered = false;
          break; 
        }
      }
    }

    Set set = null;

    if (ordered) {
      ArithProg arithProg = new ArithProg();
      if (arithProg.accumulate(values[0])) {
        // The domain values form an arithmetic progression (ordered and
        // equally spaced) so use a linear set.
        set = new Linear1DSet(type, values[0][0], values[0][numSamples - 1],
          numSamples);
      } else {
        // The samples are ordered, so use a gridded set.
        set = new Gridded1DDoubleSet(type, values, numSamples);
      }
    } else {
      set = new Irregular1DSet(type, Set.doubleToFloat(values));
    }

    return set;
  }


    private static class HeaderInfo {
        String  name;
        Unit    unit;
        double  missingValue = Double.NaN;
        String  missingString;
        String  formatString; 
        String  tzString = "GMT";
        int     isInterval = 0;
        double  errorEstimate=0;
        double  scale=1.0;
        double  offset=0.0;
        String  fixedValue;
        int     colspan = 1;
	boolean isText = false;

        public boolean isParam(String param) {
            return name.equals(param)  || name.equals(param+"(Text)");
        }
        public String toString() {
            return name;
        }


    }


    /**
     * Read in  the given file and return the processed data
     *
     * @param file The file to read in
     * @return the data
     */
    public static Data processFile(String file) throws Exception {
        TextAdapter ta = new TextAdapter(file);
        System.out.println(ta.getData().getType());
        return ta.getData();
    }



//  uncomment to test


  public static void main(String[] args) throws Exception {
    if (args.length == 0) {
      System.out.println("Must supply a filename");
      System.exit(1);
    }
    TextAdapter ta = new TextAdapter(args[0]);
    System.out.println(ta.getData().getType());
    new visad.jmet.DumpType().dumpMathType(ta.getData().getType(),System.out);
    new visad.jmet.DumpType().dumpDataType(ta.getData(),System.out);
    System.out.println("####  Data = "+ta.getData());
    System.out.println("EOF... ");
  }




    /**
     * A cut-and-paste from the IDV Misc method
     * Decodes a string representation of a latitude or longitude and
     * returns a double version (in degrees).  Acceptible formats are:
     * 
     * +/-  ddd:mm, ddd:mm:, ddd:mm:ss, ddd::ss, ddd.fffff ===>   [+/-] ddd.fffff
     * +/-  ddd, ddd:, ddd::                               ===>   [+/-] ddd
     * +/-  :mm, :mm:, :mm:ss, ::ss, .fffff                ===>   [+/-] .fffff
     * +/-  :, ::                                          ===>       0.0
     * Any of the above with N,S,E,W appended
     * 
* * @param latlon string representation of lat or lon * @return the decoded value in degrees */ public static double decodeLatLon(String latlon) { // first check to see if there is a N,S,E,or W on this latlon = latlon.trim(); int dirIndex = -1; int southOrWest = 1; double value = Double.NaN; if (latlon.indexOf("S") > 0) { southOrWest = -1; dirIndex = latlon.indexOf("S"); } else if (latlon.indexOf("W") > 0) { southOrWest = -1; dirIndex = latlon.indexOf("W"); } else if (latlon.indexOf("N") > 0) { dirIndex = latlon.indexOf("N"); } else if (latlon.indexOf("E") > 0) { dirIndex = latlon.indexOf("E"); } if (dirIndex > 0) { latlon = latlon.substring(0, dirIndex).trim(); } // now see if this is a negative value if (latlon.indexOf("-") == 0) { southOrWest *= -1; latlon = latlon.substring(latlon.indexOf("-") + 1).trim(); } if (latlon.indexOf(":") >= 0) { //have something like DD:MM:SS, DD::, DD:MM:, etc int firstIdx = latlon.indexOf(":"); String hours = latlon.substring(0, firstIdx); String minutes = latlon.substring(firstIdx + 1); String seconds = ""; if (minutes.indexOf(":") >= 0) { firstIdx = minutes.indexOf(":"); String temp = minutes.substring(0, firstIdx); seconds = minutes.substring(firstIdx + 1); minutes = temp; } try { value = (hours.equals("") == true) ? 0 : Double.parseDouble(hours); if ( !minutes.equals("")) { value += Double.parseDouble(minutes) / 60.; } if ( !seconds.equals("")) { value += Double.parseDouble(seconds) / 3600.; } } catch (NumberFormatException nfe) { value = Double.NaN; } } else { //have something like DD.ddd try { value = Double.parseDouble(latlon); } catch (NumberFormatException nfe) { value = Double.NaN; } } return value * southOrWest; } public interface StreamProcessor { public void processValues(Data[] tuple) throws VisADException ; } }

Ads help maintain this website.