Listing of Source ../source/GOF/GofCheckBoxIdentifier.java
package se.entra.phantom.server;

import java.util.StringTokenizer;
import java.util.Vector;

/**
 * This class identifies check box controls for the Gui-on-the-fly, from unused GofHostFields.
 * @author J. Bergström
 */
public class GofCheckBoxIdentifier extends GofControlIdentifierAdapter implements PhantomControlType
{
  // ------------------
  // INSTANCE VARIABLES
  // ------------------

  /**
   * The start indicator for a list of alternatives.
   * <p>
   * Defaults to "(".
   */
  private String checkListStart = "(";

  /**
   * The end indicator for a list of alternatives.
   * <p>
   * Defaults to ")".
   */
  private String checkListEnd = ")";

  /**
   * The separator between the alternatives in the list.
   * <p>
   * Defaults to "/".
   */
  private String checkListSep = "/";

  /**
   * Flag indicating where to search for the check box indicator, and in what order if multiple search.
   * <p>
   * The following values are valid:
   * <pre>
   *   0  Do not search for combo-list.
   *   1  Only search the preceding lead text.
   *   2  Only search the trailing lead text.
   *   3  First search the preceding, then the trailing lead text.
   *   4  First search the trailing, then the preceding lead text.
   * </pre>
   */
  private int checksearch = 0;

  /**
   * Filler character if there is one. Will be <code>null</code> otherwise.
   */
  private String checkFieldFiller;

  /**
   * A list with acceptable selection strings.
   */
  private Vector<String> selList = new Vector<String>( );

  /**
   * A list with acceptable unselection strings.
   */
  private Vector<String> unselList = new Vector<String>( );

  /**
   * Flag indicating that the check box should get it's text from the indicator.
   * Default is false.
   */
  private boolean useTextFromIndicator = false;

  /**
   * Indicates the way the check box's layout will be created.
   * Valid values are:
   * <pre>
   *    DEFAULT
   *    FONT
   *    COLOR
   *    FONTANDCOLOR
   * </pre>
   */
  private String layout;

  // ----------------
  // INSTANCE METHODS
  // ----------------

  /**
   * Loads setting for the control from the server ini-file.
   * <p>
   * For the identification of check boxes, the following settings are used:
   * <pre>
   *   checkstart       The start indicator for a list of alternatives.
   *   checkend         The end indicator for a list of alternatives.
   *   checksep         The separator between the alternatives in the list.
   * </pre>
   * <p>
   * There are also several possibilities of where the check box can be placed
   * relative to the entry field that will be replaced by the check box.
   * The check box can either be in the preceding lead text or in the lead text following.
   * <p>
   * The setting <code>checksearch</code> will tell this class where to look for
   * the check box indicator, and in what order to search if multiple places are to be searched.
   * <p>
   * Valid values are:
   * <pre>
   *   pre    Search the preceding leadtext.
   *   post   Search the trailing leadtext.
   * </pre>
   * <p>
   * There is also a setting that specifies if there is a filler character to use, and what 
   * character this should be. This setting as called checkFieldFiller, and if the filler 
   * character is the underscore character, it would look like this:
   * <pre>
   *    checkFieldFiller=_
   * </pre>
   * To make it possible for the GofCheckBoxIdentifier to know which of the two text strings in 
   * the indicator is the select string, and which is the unselect string, there are two settings 
   * where all the valid select strings and unselect strings can be listed. These two settings 
   * look like this:
   * <pre>
   *   checkselect=J Y
   *   checkunselect=N
   * </pre>
   * This means that if there are check boxes where there are presumptive checkboxes, where some have 
   * Yes as checked, and other have No as checked, check boxes can only be used for one of these cases.
   * <p>
   * It is also possible to either let the lead text where the check box indicator was found be 
   * created as a separate output text/static text, or to let it be moved to the check box's text.
   * <p>
   * Which alternative that should be used is specified in the checktextfromindicator setting. 
   * To move the text to the checkbox, set this setting to 1, otherwise to 0.
   * <pre>
   *   checktextfromindicator=1
   * </pre>
   * This class also uses a setting that affects the look of the check boxes. This is the checklayout 
   * setting. Valid values for this setting are:
   * <pre>
   *   checklayout=DEFAULT
   *   checklayout=FONT
   *   checklayout=COLOR
   *   checklayout=FONTANDCOLOR
   * </pre>
   * DEFAULT means that no settings are taken from the template panel; default values will be used instead. 
   * <p>
   * FONT means that the font is taken from template panel, from an check box with the id=CHK. If this 
   * control cannot be found, or if it is not an check box control, default values will be used.
   * <p>
   * COLOR means that the color is taken from template panel, from an check box with the id=CHK. If this 
   * control cannot be found, or if it is not an check box control, default values will be used.
   * <p>
   * FONTANDCOLOR means that the font and color are taken from template panel, from an check box with the 
   * id=CHK. If this control cannot be found, or if it is not an check box control, default values will be 
   * used.
   * <p>
   * Any other value will be treated as DEFAULT.
   * @param confFile The server ini file.
   */
  @Override
  public void getControlSettings( IniFile confFile, String subsection )
  {
    String setting;
    setting = confFile.getData( subsection, "checkstart" );
    if( setting != null )
      checkListStart = setting;
    setting = confFile.getData( subsection, "checkend" );
    if( setting != null )
      checkListEnd = setting;
    setting = confFile.getData( subsection, "checksep" );
    if( setting != null )
      checkListSep = setting;

    setting = confFile.getData( subsection, "checksearch" );
    checksearch = parsechecksearch( setting );

    setting = confFile.getData( subsection, "checkfieldfiller" );
    if( setting != null && setting.equals( "" ) == false )
      checkFieldFiller = setting;
    else
      checkFieldFiller = null;

    setting = confFile.getData( subsection, "checkselect" );
    if( setting != null && setting.equals( "" ) == false )
      parseChkString( setting, selList );

    setting = confFile.getData( subsection, "checkunselect" );
    if( setting != null && setting.equals( "" ) == false )
      parseChkString( setting, unselList );

    setting = confFile.getData( subsection, "checktextfromindicator" );
    if( setting != null && setting.equals( "1" ) == true )
      useTextFromIndicator = true;
    else
      useTextFromIndicator = false;

    layout = confFile.getData( subsection, "checklayout" );
    if( layout == null )
      layout = "DEFAULT";
  }

  /**
   * Identifies all the combination box controls from the <code>GofHostFields</code>.
   * <p>
   * The identification of combination boxes are done by searching for a lead text that 
   * indicates an entry field that could be replaced by a combination box. A lead text 
   * indicating a combination box should have a start indicator, an end indicator and 
   * two or more values separated by a separator. The start indicator is usually a start 
   * parenthesis, the end indicator is usually an end parenthesis, and the separator is 
   * usually a slash. But these are configurable in the configuration file.
   * <p>
   * After a lead text has been identified to hold a combination box indicator, it checks 
   * if the next or previous field is an entry field, and that it is on the same line. There 
   * is a configuration that determines if the lead text should be before the entry field, 
   * or after, or if both are accepted, and if so, in which order to search.
   * @param gofRuntime        The GuiOnTheFlyRuntime instance.
   * @param areaIdentifier    The areaIdentifier in which the controls should be identified.
   * @param phantomHostScreen The Phantom host screen corresponding to the host screen.
   * @param hostScreen        The host screen that we are trying to build a GOF panel for.
   * @param newPanel          The newly created Gui-on-the-fly runtime panel.
   * @param offsetX           Offset in columns used for popup windows.
   * @param offsetY           Offset in lines used for popup windows.
   */
  @Override
  public void identifyCtrls( GuiOnTheFlyRuntime gofRuntime,
                             GofHostAreaIdentifier areaIdentifier, 
                             PhantomHostScreen phantomHostScreen, 
                             HostScreen hostScreen,
                             PhantomPanelData templPanel, 
                             PhantomPanelData newPanel,
                             int offsetX,
                             int offsetY )
  {
    this.templPanel = templPanel;

    Vector<GofHostField> gofHostFields = areaIdentifier.getAreasGofHostFields( );
    Vector<String> chkList = new Vector<String>( );

    for( int i = 0, s = gofHostFields.size( ); i < s; i++ )
    {
      GofHostField gofHostField = gofHostFields.elementAt( i );
      if( gofHostField.hasBeenProcessed == false && gofHostField.isProtected( ) == true && gofHostField.isEmpty( ) == false )
      {
        String text = gofHostField.getText( );
        int sp = text.indexOf( checkListStart );
        int ep = text.indexOf( checkListEnd, sp + 1 );
        if( sp > -1 && ep > -1 )
        {
          String chktext = text.substring( sp + 1, ep );
          int oldseppos = -1;
          int seppos = chktext.indexOf( checkListSep );
          while( seppos > -1 )
          {
            String item = chktext.substring( oldseppos + 1, seppos );
            chkList.addElement( item );
            oldseppos = seppos;
            seppos = chktext.indexOf( checkListSep, oldseppos + 1 );
            if( seppos == -1 )
            {
              item = chktext.substring( oldseppos + 1 );
              chkList.addElement( item );
            }
          }

          if( chkList.size( ) == 2 )
          {
            // Check to se if chkList elements are in select or unselect lists;
            boolean isValid = true;
            int selitem = -1;
            String item0 = chkList.elementAt( 0 );
            String item1 = chkList.elementAt( 1 );
            if( isStrInList( item0, selList ) == true && isStrInList( item1, unselList ) == true )
              selitem = 0;
            else if( isStrInList( item1, selList ) == true && isStrInList( item0, unselList ) == true )
              selitem = 1;
            else
              isValid = false;

            if( isValid == true )
            {
              boolean wasFound;
              boolean doLoop = true;
              int curchecksearch = checksearch;
              int j = 0;
              while( doLoop )
              {
                switch( curchecksearch )
                {
                  case 1:
                  case 3:
                    j++;
                    break;
                  case 2:
                  case 4:
                    j--;
                    break;
                  default:
                    doLoop = false;
                }
              
                int curElement = i + j;
                if( gofHostFields.size( ) > curElement && curElement > 0 )
                {
                  GofHostField nxtGofHostField = gofHostFields.elementAt( curElement );
                  if( gofHostField.getY( ) == nxtGofHostField.getY( ) )
                  {
                    if( nxtGofHostField.isProtected( ) == false && nxtGofHostField.isEmpty( ) == false )
                    {
                      wasFound = convertFieldToCheck( chkList, 
                                                      nxtGofHostField, 
                                                      areaIdentifier, 
                                                      phantomHostScreen, 
                                                      hostScreen, 
                                                      newPanel, 
                                                      offsetX, 
                                                      offsetY,
                                                      selitem, 
                                                      gofHostField );
                      if( wasFound == true )
                        doLoop = false;
                      else
                      {
                        switch( curchecksearch )
                        {
                          case 3:
                            j = 0;
                            curchecksearch = 2;
                            break;
                          case 4:
                            j = 0;
                            curchecksearch = 1;
                            break;
                          default:
                            doLoop = false;
                        }
                      }
                    }
                    else
                    {
                      switch( curchecksearch )
                      {
                        case 3:
                          j = 0;
                          curchecksearch = 2;
                          break;
                        case 4:
                          j = 0;
                          curchecksearch = 1;
                          break;
                      }
                    }
                  }
                  else
                    doLoop = false; // We have switched line, don't continue.
                }
                else
                  doLoop = false;
              }
            }
          }
          chkList = new Vector<String>( );
        }
      }
    }
  }

  /**
   * Parses the check-search setting, and converts into a flag value.
   * @param setting The setting from the server.ini file.
   * @return The value for the check-search flag.
   */
  private int parsechecksearch( String setting )
  {
    if( setting == null || setting.equals( "" ) )
      return 0;

    int flag = 0;
    StringTokenizer st = new StringTokenizer( setting, ", " );
    while( st.hasMoreTokens( ) )
    {
      String s = st.nextToken( );
      switch( flag )
      {
        case 1:
          if( s.equals( "pre" ) )
            flag = 1;
          else if( s.equals( "post" ) )
            flag = 3;
          break;
        case 2:
          if( s.equals( "pre" ) )
            flag = 4;
          else if( s.equals( "post" ) )
            flag = 2;
          break;
        default:
          if( s.equals( "pre" ) )
            flag = 1;
          else if( s.equals( "post" ) )
            flag = 2;
      }
    }
    return flag;
  }

  /**
   * Tries to convert an entry field to a combination box.
   * <p>
   * Before the creation of the PhantomCComboBox, a check will be done to se if the text in the host field
   * is included in the list.
   * @param areaIdentifier    The areaIdentifier in which the controls should be identified.
   * @param phantomHostScreen The Phantom host screen corresponding to the host screen.
   * @param hostScreen        The host screen that we are trying to build a GOF panel for.
   * @param newPanel          The newly created Gui-on-the-fly runtime panel.
   * @param offsetX           Offset in columns used for popup windows.
   * @param offsetY           Offset in lines used for popup windows.
   * @return <code>true</code> if the GofHostField was converted to a combination box,
   *         <code>false</code> otherwise.
   */
  private boolean convertFieldToCheck( Vector<String> chkList, 
                                       GofHostField gofHostField,
                                       GofHostAreaIdentifier areaIdentifier, 
                                       PhantomHostScreen phantomHostScreen, 
                                       HostScreen hostScreen, 
                                       PhantomPanelData newPanel,
                                       int offsetX,
                                       int offsetY,
                                       int selitem,
                                       GofHostField indGofHostField )
  {
    int x;
    int y;
    int cx;
    String chkText;

    // Get the width of the widest string in the list.

    int maxWidth = 0;
    for( int i = 0, s = chkList.size( ); i < s; i++ )
    {
      String listElement = chkList.elementAt( i );
      if( listElement.length( ) > maxWidth )
        maxWidth = listElement.length( );
    }

    if( gofHostField.hasBeenProcessed == false && 
        gofHostField.isProtected( ) == false && 
        gofHostField.isEmpty( ) == false &&
        gofHostField.getCx( ) >= maxWidth )
    {
      HostField hf = gofHostField.getHostField( );

      if( useTextFromIndicator == true )
      {
        x = indGofHostField.getX( );
        y = indGofHostField.getY( );
        cx = indGofHostField.getCx( );
        chkText = indGofHostField.getText( );
        indGofHostField.hasBeenProcessed = true;
      }
      else
      {
        x = gofHostField.getX( );
        y = gofHostField.getY( );
        cx = gofHostField.getCx( );
        chkText = " ";
      }
      String orgtext = gofHostField.getText( );

         
      int font = -1;
      int foregroundColor = -1;
      PhantomControl templateControl = templPanel.getControlFromID( "CHK" );

      if( layout.equals( "FONT" ) )
      {
        if( templateControl != null && templateControl.controlBase.type == CTRLTYPE_CHECK )
          font = ( ( PhantomCCheckBox )templateControl ).font;
      }
      else if( layout.equals( "COLOR" ) )
      {
        if( templateControl != null && templateControl.controlBase.type == CTRLTYPE_CHECK )
          foregroundColor = ( ( PhantomCCheckBox )templateControl ).getForegroundColor();
      }
      else if( layout.equals( "FONTANDCOLOR" ) )
      {
        if( templateControl != null && templateControl.controlBase.type == CTRLTYPE_CHECK )
        {
          font = ( ( PhantomCCheckBox )templateControl ).font;
          foregroundColor = ( ( PhantomCCheckBox )templateControl ).getForegroundColor();
        }
      }

      // Create the base control.

      PhantomControlBase bc = new PhantomControlBase( GOF_MARGINX + ( x - offsetX ) * GOF_STEPX - 1,
                                                      GOF_MARGINY + ( y - offsetY ) * GOF_STEPY - 1,
                                                      cx * GOF_GUIUNITX + 15,
                                                      GOF_GUIUNITY,
                                                      CTRLTYPE_CHECK );

      // Create the PhantomHostField.

      PhantomHostField phf = new PhantomHostField( phantomHostScreen, hostScreen, hf );
      if( checkFieldFiller != null )
      {
        phf.filler = checkFieldFiller.charAt( 0 );
        phf.flags = PhantomHostField.FORMAT_STRIPEND;
      }
      phantomHostScreen.addHostField( phf );

      // Create the entry field.

      String selStr = "";
      String unselStr = "";
      if( selitem == 0 )
      {
        selStr = chkList.elementAt( 0 );
        unselStr = chkList.elementAt( 1 );
      }
      else
      {
        selStr = chkList.elementAt( 1 );
        unselStr = chkList.elementAt( 0 );
      }

      PhantomCCheckBox cb = new PhantomCCheckBox( newPanel, bc, phf, selStr, unselStr, orgtext.equals( selStr ) );
      cb.font = font;
      cb.setForegroundColor(foregroundColor);
      cb.text = chkText;

      // Add the entry field.

      newPanel.addControl( cb );
      areaIdentifier.addControl( cb );
      gofHostField.hasBeenProcessed = true;
      return true;
    }
    return false;
  }

  /**
   * Parses the configuration setting for the pushbutton identifications, and splits
   * it up into the identification strings, which are stored in a Vector.
   */
  private void parseChkString( String chkStr, Vector<String> chkList )
  {
    if( chkStr == null )
      return;

    StringTokenizer st = new StringTokenizer( chkStr, ", " );
    while( st.hasMoreTokens( ) )
    {
      String s = st.nextToken( ).trim( );
      if( s.equals( "" ) == false )
        chkList.addElement( s );
    }
  }

  /**
   * Checks if a String is in a list.
   * @param str  The String to check.
   * @param list The list to search in.
   * @return <code>true</code> if the String was found in the list,
   *         <code>false</code> otherwise.
   */
  private boolean isStrInList( String str, Vector<String> list )
  {
    boolean isInList = false;

    for( int i = 0, s = list.size( ); i < s; i++ )
    {
      String listStr = list.elementAt( i );
      if( str.equals( listStr ) == true )
      {
        isInList = true;
        break;
      }
    }
    return isInList;
  }
}