/*
 *
 * Copyright (c) 2007
 * Adrian Michel
 * http://www.tradery.com
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Adrian Michel makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
*/


package  com.tradery.contentmodel;

public abstract class AbstractExpression extends SyntaxTree

{
  AbstractExpression( String name ) { super( name ); }

  public abstract void push();
  public abstract void pop();
  public void pop( int n ) { while( n-- > 0 ) pop(); }


  public void plus() { plus(""); }
  public void star() { star(""); }
  public void opt() { opt(""); }
  public void neutral() { neutral( ""); }
  public void range( int min, int max )
    throws BadLimitsException { range( "", min, max ); }

  public void or() { or(""); }
  public void and() { and(""); }
  public void all() { all(""); }

  public abstract void plus( String str );
  public abstract void star( String str );
  public abstract void opt( String str );
  public void range( String str, int min, int max )
    throws BadLimitsException
  {
    precalcRanges( str, min, max );
  }
  public abstract void or( String str );
  public abstract void and( String str );
  public abstract void all( String str );
  public abstract void neutral( String str );
  public abstract void symbol( String str );


  public void parse( String str )
    throws BadLimitsException
  {
    String s;
    for( int n = 0; n < str.length(); n++ )
    {
      char c = str.charAt( n );

      switch( c )
      {
        case '(':
          push();
          break;
        case ')':
          pop();
          break;
        case '^':
          pop();
          break;
        case '*':
          star();
          break;
        case '+':
          plus();
          break;
        case '?':
          opt();
          break;
        case ',':
          and();
          break;
        case '|':
          or();
          break;
        case '&':
          all();
          break;
        case ' ':
        case '\t':
        case '\n':
        case '\r':
          // ignore white space
          break;
        case '[':
          n++;
          n = makeRange( str, n );
          break;
        default:
          n = makeSymbol( str, n );
          break;
      }
    }
  }

  private int makeSymbol( String str, int n )
  {
    String s = new String();
    boolean f = true;
    for( ; n < str.length() && f; n++ )
    {
      char ch = str.charAt( n );
      switch( ch )
      {
        case '(':
        case ')':
        case '*':
        case '+':
        case '?':
        case '[':
        case ',':
        case '|':
        case '&':
        case ' ':
        case '^':
        case '\t':
        case '\n':
        case '\r':
          symbol( s );
          return --n;
        default:
          s += str.substring( n, n+1 );
          break;
      }
    }
    symbol( s );
    return n;
  }

  /**
   * makeRange calculates the numeric range based on the range expression:
   * [min,max], where max can be '*', indicating no max limit. The format of 
   * the expression needs to be exactly this, or the method will fail.
   * The method is passed a string and the location in the string where "min" starts.
   * 
   * @param str    The string containing the range expression
   * @param n      The location in the string where "min" starts.
   * @return 
   * @exception BadLimitsException
   */
  private int makeRange( String str, int n )
    throws BadLimitsException
  {
    String s;
    int min;
    int max;

      // position in the string of the separator comma
    int m = str.indexOf( ',', n );
      // s is the string containing min
    s = str.substring( n, m );
      // get the numeric value of min
    min = Integer.parseInt( s );

      // move the start past the comma separator
    n = ++m;
      // m points to the closing ']'
    m = str.indexOf( ']', n );
      // s is the string containing max
    s = str.substring( n, m );
      // if the max value is '*', then max is -1, indicating no max limit 
    if( s.equals( "*" ) )
      max = -1;
    else
        //else max is the numeric value of the string
      max = Integer.parseInt( s );
    // in case we are in one of these special cases, reduce the ranges
    // to their simpler equivalent (*, +, ? or none, in case it is 1,1)
    precalcRanges( "", min, max );
      // return the position in the string where the content model expression will continue parsing
    return m;
  }

  private void precalcRanges( String str, int min, int max )
    throws BadLimitsException
  {
    if( min == 0 && max == 1 )
      opt( str );
    else if( min == 0 && max == -1 )
      star( str );
    else if( min == 1 && max == -1 )
      plus( str );
    else if( min != 1 || max != 1 )
      realRange( str, min, max );
  }

  protected abstract void realRange( String str, int min, int max )
    throws BadLimitsException;
}