package postgresql.util;

import java.sql.*;
import java.util.*;

/**
 * This class is used to tokenize the text output of postgres.
 *
 * <p>It's mainly used by the geometric classes, but is useful in parsing any
 * output from custom data types output from postgresql.
 *
 * @see postgresql.geometric.PGbox
 * @see postgresql.geometric.PGcircle
 * @see postgresql.geometric.PGlseg
 * @see postgresql.geometric.PGpath
 * @see postgresql.geometric.PGpoint
 * @see postgresql.geometric.PGpolygon
 */
public class PGtokenizer
{
  // Our tokens
  protected Vector	tokens;
  
  /**
   * Create a tokeniser.
   *
   * <p>We could have used StringTokenizer to do this, however, we needed to
   * handle nesting of '(' ')' '[' ']' '&lt;' and '&gt;' as these are used
   * by the geometric data types.
   *
   * @param string containing tokens
   * @param delim single character to split the tokens
   */
  public PGtokenizer(String string,char delim)
  {
    tokenize(string,delim);
  }
  
  /**
   * This resets this tokenizer with a new string and/or delimiter.
   *
   * @param string containing tokens
   * @param delim single character to split the tokens
   */
  public int tokenize(String string,char delim)
  {
    tokens = new Vector();
    
    // nest holds how many levels we are in the current token.
    // if this is > 0 then we don't split a token when delim is matched.
    //
    // The Geometric datatypes use this, because often a type may have others
    // (usualls PGpoint) imbedded within a token.
    //
    // Peter 1998 Jan 6 - Added < and > to the nesting rules
    int nest=0,p,s;
    
    for(p=0,s=0;p<string.length();p++) {
      char c = string.charAt(p);
      
      // increase nesting if an open character is found
      if(c == '(' || c == '[' || c == '<')
	nest++;
      
      // decrease nesting if a close character is found
      if(c == ')' || c == ']' || c == '>')
	nest--;
      
      if(nest==0 && c==delim) {
	tokens.addElement(string.substring(s,p));
	s=p+1; // +1 to skip the delimiter
      }
      
    }
    
    // Don't forget the last token ;-)
    if(s<string.length())
      tokens.addElement(string.substring(s));
    
    return tokens.size();
  }
  
  /**
   * @return the number of tokens available
   */
  public int getSize()
  {
    return tokens.size();
  }
  
  /**
   * @param n Token number ( 0 ... getSize()-1 )
   * @return The token value
   */
  public String getToken(int n)
  {
    return (String)tokens.elementAt(n);
  }
  
  /**
   * This returns a new tokenizer based on one of our tokens.
   *
   * The geometric datatypes use this to process nested tokens (usually
   * PGpoint).
   *
   * @param n Token number ( 0 ... getSize()-1 )
   * @param delim The delimiter to use
   * @return A new instance of PGtokenizer based on the token
   */
  public PGtokenizer tokenizeToken(int n,char delim)
  {
    return new PGtokenizer(getToken(n),delim);
  }
  
  /**
   * This removes the lead/trailing strings from a string
   * @param s Source string
   * @param l Leading string to remove
   * @param t Trailing string to remove
   * @return String without the lead/trailing strings
   */
  public static String remove(String s,String l,String t)
  {
    if(s.startsWith(l))	s = s.substring(l.length());
    if(s.endsWith(t))	s = s.substring(0,s.length()-t.length());
    return s;
  }
  
  /**
   * This removes the lead/trailing strings from all tokens
   * @param l Leading string to remove
   * @param t Trailing string to remove
   */
  public void remove(String l,String t)
  {
    for(int i=0;i<tokens.size();i++) {
      tokens.setElementAt(remove((String)tokens.elementAt(i),l,t),i);
    }
  }
  
  /**
   * Removes ( and ) from the beginning and end of a string
   * @param s String to remove from
   * @return String without the ( or )
   */
  public static String removePara(String s)
  {
    return remove(s,"(",")");
  }
  
  /**
   * Removes ( and ) from the beginning and end of all tokens
   * @return String without the ( or )
   */
  public void removePara()
  {
    remove("(",")");
  }
  
  /**
   * Removes [ and ] from the beginning and end of a string
   * @param s String to remove from
   * @return String without the [ or ]
   */
  public static String removeBox(String s)
  {
    return remove(s,"[","]");
  }
  
  /**
   * Removes [ and ] from the beginning and end of all tokens
   * @return String without the [ or ]
   */
  public void removeBox()
  {
    remove("[","]");
  }
  
  /**
   * Removes &lt; and &gt; from the beginning and end of a string
   * @param s String to remove from
   * @return String without the &lt; or &gt;
   */
  public static String removeAngle(String s)
  {
    return remove(s,"<",">");
  }
  
  /**
   * Removes &lt; and &gt; from the beginning and end of all tokens
   * @return String without the &lt; or &gt;
   */
  public void removeAngle()
  {
    remove("<",">");
  }
}
