After you have added the condition to the scenarioManager.xml file, you extend the abstract class atg.process.filter.ExpressionFilter for your new condition. The ExpressionFilter class evaluates the arguments in the condition and returns true or false.

The following code sample shows how you would extend this class for the custom condition in our example:

package astroweb;

import atg.process.ProcessException;
import atg.process.ProcessExecutionContext;
import atg.process.filter.Filter;
import atg.process.filter.ExpressionFilter;
import atg.process.expression.Expression;

import atg.beans.*;

import java.text.*;
import java.util.*;

/**
 * Filter example for testing the moon's phase as of a specific date.
 */
public class MoonFilter extends ExpressionFilter
{
  //-------------------------------------
  // Constants
  //-------------------------------------

  // calendar test type enumeration
  public static final int NEW = 0;
  public static final int WAXING = 1;
  public static final int FULL = 2;
  public static final int WANING = 3;

  //-------------------------------------
  // Fields
  //-------------------------------------

  /** The type of calendar test being performed, as per above constants */
  private int mTestType;

  //-------------------------------------
  // ExpressionFilter overrides
  //-------------------------------------

  //-------------------------------------
  /**
   * Initializes this ExpressionFilter, given its operator and
   * operands.  The default implementation of this method simply sets
   * the operator and operands properties.
   *
   * @param pOperator the filter operator, not needed in this example
   * @param pOperands the operands to the filter.  pOperands[0] is an
   * Integer giving the test type, and pOperands[1] is a Date-typed
   * expression giving the date on which the moon's phase is to be
   * tested.
   *
   * @exception ProcessException if the operands argument is invalid
   **/

  public void initialize(String pOperator, Expression[] pOperands)
    throws ProcessException
  {
    super.initialize(pOperator, pOperands);

    // Verify that we have the expected number of operands: two
    int nOperands = (pOperands == null ? 0 : pOperands.length);
    if (nOperands != 2) {
      throw new ProcessException("Wrong number of operands: " + nOperands);
    }

    // Precompute our test type operand for efficiency, since it's a
    // constant.  First verify that we can get the values without an
    // execution context
    Expression exTestType = pOperands[0];
    if (!exTestType.canGetValue(null))
      throw new ProcessException("Unable to precompute test type");

    // Now look up the values, cast them to the correct types, and
    // cache values in data members.
    //
    // The rigorous type checking isn't strictly necessary since the
    // scenario editor grammar should constrain the types of our operands,
    // but a little extra sanity checking never hurts.
    Object value = null;
    try
    {
      value = exTestType.getValue(null);
      Integer testType = (Integer)value;

      if (testType == null)
        throw new ProcessException("Test type is null");

      mTestType = testType.intValue();
    }
    catch (ClassCastException cce) {
      throw new ProcessException("Test type had unexpected type: " +
      value.getClass().getName());
    }

    System.out.println("Cached mTestType = " + mTestType);
  }


  //-------------------------------------
  /**
   * Evaluates this filter in the given process execution context.
   * The context may not yet contain all of the information necessary
   * to evaluate the filter - specifically, it may be missing the
   * particular scenario instance and/or profile that the scenario is
   * being executed on.  If that is the case, the filter is evaluated
   * as much as possible, and the simplified filter is returned.
   *
   * <p>The possible return values of this method are as follows:
   * <ul>
   * <li><code>Filter.TRUE</code> - if the filter can be fully
   * evaluated, and is satisfied in the given context</li>
   * <li><code>Filter.FALSE</code> - if the filter can be fully
   * evaluated, and is not satisfied in the given context</li>
   * <li><code>null</code> - if the filter cannot be evaluated because
   * of a null expression encountered during evaluation (e.g., filter
   * refers to a profile property which evaluates to null)</li>
   * </ul>
   *
   * @exception ProcessException if there is a problem evaluating the
   * filter (other than information missing from the context)
   **/
  protected Filter evaluate(ProcessExecutionContext pContext)
    throws ProcessException
  {
    Expression[] operands = getOperands();

    // Verify that all variable operand values are available

    Expression exDate = operands[1];
    if (!exDate.canGetValue(pContext))
      return this;

    // Get dynamic operand values and convert them to the expected
    // types, as above.
    Object value;
    Date dateValue;

    if ((value = exDate.getValue(pContext)) == null)
      return null;

    try {
      dateValue = (Date) value;
    }
    catch (ClassCastException cce) {
      throw new ProcessException("Date value was of wrong type: " +
      value.getClass().getName());
    }

    MoonCalendar mc = new MoonCalendar(dateValue);
    boolean result = false;

    System.out.println("Age of moon = " + mc.getAge());

    switch(mTestType)
    {
    case NEW:
      result = mc.isNew();
      break;
    case WAXING:
      result = mc.isWaxing();
      break;
    case FULL:
      result = mc.isFull();
      break;
    case WANING:
      result = mc.isWaning();
      break;
    default:
      throw new ProcessException("Unknown test type value: " + mTestType);
    }

    return result ? Filter.TRUE : Filter.FALSE;
  }
}

The next code sample is included for completeness. It constructs a JavaBean representing the calculator that determines the phase of the moon for any given date.

package astroweb;

import java.util.Date;

/**
 * A simple calendar calculator that determines lunar phase
 * information.
 **/

public class MoonCalendar
{
  // Calendrical and astronomical constants
  private static final long MILLIS_PER_DAY = 86400000;
  private static final long JULIAN_EPOCH = 2440588; // Jaunary 1, 1970
  private static final double LUNAR_PERIOD = 29.530588853;
  private static final double NEW_MOON_BASELINE = 2451550.1;

  /**
   * The age of the moon, expressed as a number of days, based on the
   * given date, normalized to lie in the range 0..LUNAR_PERIOD.
   **/
  private double mAge;


  //----------------------------------------
  /**
   * Construct a MoonCalendar for some given date, precalculating the
   * moon's age.
   */
  public MoonCalendar(Date date)
  {
    long julianDate = JULIAN_EPOCH + (date.getTime() / MILLIS_PER_DAY);
    double tempVal = (julianDate - NEW_MOON_BASELINE) / LUNAR_PERIOD;
    tempVal -= Math.floor(tempVal);
    if (tempVal < 0)
      tempVal += 1;
    mAge = tempVal * LUNAR_PERIOD;
  }

  //----------------------------------------
  /**
   * Construct a MoonCalendar for the current time
   */
  public MoonCalendar()
  {
    this(new Date());
  }

  //----------------------------------------
  /**
   * Return the age of the moon in days.
   */
  public double getAge()
  {
    return mAge;
  }

  //----------------------------------------
  /**
   * Determine if the moon is waxing
   */
  public boolean isWaxing()
  {
    return mAge > 0.5 && mAge < 14.0;
  }

  //----------------------------------------
  /**
   * Determine if the moon is waning
   */
  public boolean isWaning()
  {
    return mAge > 15.0 && mAge < 29.0;
  }

  //----------------------------------------
  /**
   * Determine if the moon is full
   */
  public boolean isFull()
  {
    return mAge > 14.0 && mAge < 15.0;
  }

  //----------------------------------------
  /**
   * Determine if the moon is new
   */
  public boolean isNew()
  {
    return mAge > 29.0 || mAge < 0.5;
  }
}
 
loading table of contents...