6 Define Defaulting and Derivation Logic

This chapter describes how to define your defaulting and derivation logic, how to use Groovy (a Java-like scripting language), and how to use Oracle Application Development Framework (Oracle ADF) validators and convertor hints instead of using messages.

This chapter includes the following sections:

6.1 Understanding Entity Object Defaulting and Derivation Logic

Defaulting logic means assigning attribute values when a row or entity object is first created or refreshed. (The logic is not re-applied when the entity object is changed.) Defaulting is achieved either declaratively in the default field of the attribute or programmatically by adding code to the EOImpl.

Derivation logic means assigning attribute values when some other attributes have changed. Derivation is achieved either declaratively in the default field of the transient attribute or by using a validator, or programmatically by adding code to the EOImpl.

Figure 6-1, illustrates what you need to consider when determining whether to implement defaulting or derivation logic.

Figure 6-1 Defaulting and Derivation — Decision Tree

Described in the surrounding text

When implementing defaulting or derivation logic, you should also consider the following factors:

  • Always assign a valid value to an attribute.

    You should know what the valid values are and there is no reason why you would want to assign invalid values. The end users do not set these values and would have no idea why they would be invalid.

  • Always use initDefaultExpressionAttribute for calculations that cross containerships. Use initDefault for literal or statically computed values.

  • Instead of writing code in one of the triggering points during validation or the posting cycle to achieve derivation logic, you can use a method validator or an expression validator.

    When you want the derivation logic to be customizable, the validator approach is preferable. When using this approach the validation result should always be true because this is not really a validation logic. You should also make sure that the attribute avoids an infinite loop due to validation.

  • You can call either setAttribute(setter) or populateAttribute to assign the default or derived value to an attribute.

    When you call setAttribute(setter), the logic in the setter is fired and the validation logic is also executed. This does not happen when you call populateAttribute.

    In most cases, using populateAttribute is sufficient because you should always assign a valid value and therefore do not need to fire validation logic. However, you may want to call the setter if there is additional logic such as cascading derivation in the setter.

    Tip:

    When you call setAttribute(setter) make sure that you do not cause an infinite loop. This may happen due to the attribute and the entity becoming invalid and causing the validation logic to refire.

  • You can call beforeCommit as well as setAttribute(setter), validateEntity, and prepareForDML if your derivation logic involves multiple entities that are not composite.

    For composite object, you can just put your logic either in validateEntity or prepareForDML of the parent EOImpl.

  • Oracle ADF handles the propagation of the foreign key ID if there is an association between two entities. This is where the association is defined from the parent entity object to the child entity object, and when the detail entity object is created from the association accessor of the parent entity object. For example:

    Row parentRow = ....RowIterator ri = (RowIterator)parentRow.getAttribute("<childEOAccessorName>");
     Row childRow = r1.createRow();
    

    Similarly, if there is a view link between two view objects, the framework also handles the foreign key propagation when the child view row is created via the view link accessor of the parent view row.

  • List of Values (LOVs) also perform derivation. However, this is at the view object level and you should not place business logic (including derivation) at this level.

    A LOV should only be used on the user interface (UI) to show a list of valid values or as a service to derive the foreign key ID based on the foreign alternate key.

6.2 About Using Groovy Scripting Language

ADF Business Components now provide integrated support for Groovy (a Java-like scripting language), which is dynamically compiled and evaluated at run-time. Because it is dynamically compiled, Groovy script can be stored inline in the XML and is eligible for customization. Groovy also supports object access via dot-separated notation, which means you can now use syntax such as empno instead of getAttribute(EMPNO).

You can embed Groovy script into various declarative areas, including:

  • Validation - Use a Groovy script that returns true or false for declarative validation.

  • Validation Error Messages - Use Groovy expressions to substitute the tokens in the error message.

  • Bind Variables - Define the value for a bind variable using a Groovy script expression.

  • View Accessor Bind Variable Values - Supply bind variable values in a view accessor using Groovy script.

  • Attributes - Base a transient attribute on a Groovy script. (Currently no UI support).

  • Attribute Default Values - Define a default value expression on an attribute using Groovy script. (Currently no UI support).

  • Variables - Define a variable on an entity whose value is computed using Groovy script. (Currently no UI support).

6.2.1 About Keywords and Available Names

As with the original Script implementation, the current object is passed into the script as "this" object. Therefore, to refer to any attribute inside the current object simply use the attribute name. For example, in an attribute or validator expression for an entity, to refer to an attribute named Ename, the script may say return Ename.

There is one top-level reserved name, adf, which is used to get to objects that the framework makes available to the Groovy script. Currently, these objects are:

  • ADFContext (adf.context)

  • Object on which the expression is being applied (adf.object)

  • Error handler that lets the validator generate exceptions or warnings (adf.error)

All other names come from the context in which the script is applied:

  • Variable - gets the Variable, structureDef in which it is contained via getStructureDef method on VariableImpl.

  • Transient Attribute - gets the Entity or ViewRow as its context so that all attributes in the entity are accessible by name. Any method on the entity may be invoked by directly calling the entity method as if you were writing a method in the entity subclass.

    Tip:

    Only public methods on the entity are available to call.

    You also need to call the method using the "object" keyword, such as adf.object.createUnqualifiedRowSet(). The "object" keyword is equivalent to the "this" keyword in Java. Without it, in transient expressions, the method is assumed to exist on the script object itself, which it does not.

  • Validator - gets the Validator context JboValidatorContext merged with the Entity on which the validator is applied. This is done so that you can use:

    • newValue and oldValue to get to the values being validated

    • sourceRow to get to the Entity or ViewRow on which the validator is applied

    • All attribute names in the Entity or ViewRow as top-level names

6.2.2 About Scripting Logic

Groovy scripting logic is similar to Expression Language (EL) because you can use a "." separated path to get to a value inside an object. Note that if a Java object implements Map, only the map lookup is performed instead of the bean style property lookup. However, for Maps that extend JboAbstractMap you get the same EL behavior, which is map first followed by bean lookup. This is due to the implementation of get in JboAbstractMap.

Consider the following information:

  • All Java methods, language constructs, and Groovy language constructs are available in the script.

  • Aggregates are implemented by calling sum(expr), count(expr), or avg(expr) on a RowSet object where expr can be any Groovy expression that returns a numeric value or number domain.

  • The defaultRowSet reserved keyword has been removed. The method EntityImpl.createUnqualifiedRowSet() replaces EntityImpl.getDefaultRowSet() and can be accessed like any other public method in EntityImpl.

  • Use the return keyword as you would in Java to return a value. That is, unless it is a single-line expression where the return is assumed to be the result of the expression itself. For example, "Sal + Comm" or "Sal > 0".

  • Do not use {} to surround the entire script because Groovy interprets { to be the beginning of a Closure object.

  • Any object that implements oracle.jbo.Row, oracle.jbo.RowSet, or oracle.jbo.ExprValueSupplier is wrapped into a Groovy Expando object. This is to extend the properties available for those objects to beyond the bean properties and also as a way to avoid introspection for most used names.

6.2.3 Groovy Expression Examples

The following are some examples of Groovy.

6.2.3.1 Querying Based on the Current Locale

Instead of using the following SQL to achieve this:

SELECT C.ISO_COUNTRY_CODE
,C.COUNTRY_NAME
FROM COUNTRY_CODES C
WHERE LANGUAGE = SYS_CONTEXT('USERENV', 'LANG')
ORDER BY C.COUNTRY_NAME

Create a bind variable and base its default value on the adf.context.locale.language expression:

6.2.3.2 Error Message Tokens

To get the attribute new value and label:

Example of using Groovy to get attr new value & label.

The above example uses the following two Groovy expressions:

newValue  // This works because an attribute level validator has been created.
source.hints.ProductId.label

and

source.structureDef.name+" of type "+sourceFullName

6.2.3.3 Expression Validators

Example 6-1 is an example of an Object graph, custom error, and a warning:

Example 6-2 is an example of how to average a collection.

Example 6-3 is an example of a built-in or custom method call on the sourceObject of this validator (sourcObject being the Entity on which this validator is being run). isAttributeChanged(String) is a public method on the EntityImpl:

Example 6-4 is an example of getting to oldValue / newValue of an attribute on which this validator is applied:

Example 6-5 is an example of accessing the Entity state relative to the database and relative to the last post operation.

Use adf.object.entityState or adf.object.postState.

To get the old value of an attribute (this works in the context of a transient Entity Object attribute):

Example 6-6 is an example of the WHILE construct as well as calling an accessor (Emp):

Example 6-1 Object Graph, Custom Error, and a Warning

if (EmpSal >= 5000)
{
  // If EmpSal is greater than a property value set on the custom
  // properties on the root AM
  // raise a custom exception else raise a custom warning
  if (EmpSal >= source.DBTransaction.rootApplicationModule.propertiesMap.salHigh)
  {
    adf.error.raise("ExcGreaterThanApplicationLimit");
  }
  else
  {
    adf.error.warn("WarnGreaterThan5000");
  }
}
else if (EmpSal <= 1000)
  {
    adf.error.raise("ExcTooLow");
  }
return true;

Example 6-2 Averaging a Collection

attribute Number EmpSal : SAL
{
  expressionValidator(expression = 
    "newValue <= source.createUnqualifiedRowSet().avg(\"EmpSal\") * 1.2");
}

Example 6-3 Built-in or Custom method Call

if (source.isAttributeChanged("EmpSal") || source.isAttributeChanged("EmpComm"))
           {
              return true;
           }
           return false;

Example 6-4 Getting to Old Value and New Value of an Attribute

return (oldValue == null || newValue < olValue * 1.2);

Example 6-5 Getting the Old Value of a Transient Entity Object Attribute

index = object.getStructureDef().getAttributeIndexOf("Salary");
return object.getAttribute(index, oracle.jbo.server.EntityImpl.ORIGINAL_VERSION);

Example 6-6 While Construct and Calling an Accessor

emps = Emp;
boolean alreadyfound = false;
emps.reset();
while (emps.hasNext())
{
  if (emps.next().Job == "CLERK")
  {
    if (alreadyfound)
    {
      adfError.raise("alreadyfound");
    }
    alreadyfound = true;
  }
}
return true;

6.2.3.4 Attribute Defaulting and Calculation

Example 6-7, Example 6-8, and Example 6-9 are examples of a simple transient attribute, how to sum or count a collection, and how to create a complex calculation of a bind variable value.

Example 6-10 is of an entity-attribute XML fragment where a transient expression is used to provide a default value for that attribute. This expression is evaluated before the protected create method of the entity is called. Example 6-11 is an example of an attribute defaulting with a transient attribute calculation expression.

Example 6-7 Simple Transient Attribute

attribute transient Integer YearSal
{
  transientexpression = "EmpSal * 12";
}

Example 6-8 Sum or Count a Collection

attribute transient Integer TotalSal
{
  transientexpression = "object.createUnqualifiedRowSet().sum(\"EmpSal\")";
}
attribute transient Integer TotalCount
{
  transientexpression = "object.createUnqualifiedRowSet().count(\"EmpSal\")";
}

Example 6-9 Complex Calculation of a Bind Variable Value

query EmpView
{
  entity Emp EmpUsage \*;
  where "SAL > :avgSal"
  orderby "1"
  bindingstyle "OracleName"

variables
  {
    Double avgSal
    kind (where)
    {
      transientexpression
      {
        totSal = 0;
        empCount =0;
        fullVO = structureDef.getApplicationModule().createViewObject("_AvgSal", 
          testp.kava.VO7.si33mt.EmpAllView");
        empCount = 0;
        while (fullVO.hasNext())
        {
          row = fullVO.next();
          sal = row.EmpSal; totalSal = totSal + sal; empCount = empCount + 1;
        }
        fullVO.remove();
        if (empCount > 0)
        {
          return (int)(totalSal / empCount);
        }
        else
        {
          return 0;
        }
      }
    }
  }
}

Example 6-10 Attribute Value Defaulting

<Attribute
      Name="EmpComm"
      ColumnName="COMM"
      Type="oracle.jbo.domain.Number"
      ColumnType="NUMBER"
      SQLType="NUMERIC"
      TableName="EMP" >
<TransientExpression><![CDATA[

      if (EmpSal == null)
      {
        return null;
      }
      if (EmpDeptNum == null)
      {
        return 0;
      }
      if (EmpDeptNum > 40)
      {
        retune 500;
      }

]]></TransientExpression>

Example 6-11 Attribute Defaulting with a Transient Attribute Calculation Expression

<ViewAttribute
    Name="Total"
    IsUpdateable="false"
    AttrLoad="Each"
    IsSelected="false"
    IsPersistent="false"
    PrecisionRule="false"
    Type="java.lang.String"
    ColumnType="VARCHAR2"
    AliasName="View_ATTR"
    SQLType="VARCHAR">
<TransientExpression>
<![CDATA[
          if (Sal != null && Comm != null)
          {
          return Sal + Comm;
        }
        else
        {
          return Sal;
        }
        ]]>
</TransientExpression>

6.2.4 About Defining Expressions at Design Time

An expression for an attribute can be defined using either the Attribute Editor (see Figure 6-2) or the Expression Editor (see Figure 6-3).

Figure 6-2 Entity Object — Attribute Editor

Described in the surrounding text

If you want to define a string literal instead of a Groovy expression, select Literal as the Value Type and enter the value as "My Literal Value".

Figure 6-3 Entity Object — Expression Editor

Described in the surrounding text

To access the Expression Editor, click the Edit button located next to the Value text box.

Note:

Recalculate Expression is used to determine whether the expression must be recalculated as changes are made during run-time. The Recalculate option is hidden for persistent attributes. This is because Persistent attribute values are always updateable by the user and therefore, the expression of the attribute should only act as a default expression so recalculation is not necessary. For non-persistent attributes, the user can choose to always recalculate, never recalculate, or decide if recalculation is needed based on the evaluation of the recalculate expression.

6.3 Using Oracle ADF Validators and Convertor Hints

An Oracle ADF validator is appropriate for simple data entry errors that the user can easily correct immediately, on the same UI. In some situations, you should consider using these validators or converter hints instead of using messages.

However, errors such as those that require you to leave the UI to fix, require administrator intervention or an incident report to be filed, should be a Message Dictionary message instead.

Caution:

Oracle ADF validators and converter hints can only be used with messages stored in the Strings resource bundles. They cannot be used for messages stored in the Message Dictionary.

To ensure that the user has supplied the correct sort of value or a value in a valid range, input fields can be validated using an Oracle ADF validator. Values may be converted by a converter, for example to convert a string of input characters into a value of some other type such as a date or color.

To validate or convert an input value, you add the input component to the page and then add a validator or converter to that field. Each validator and converter has some messages associated with it:

  • Hints to display to the user details of what sort of value they need to enter.

  • Error messages to display if the user enters an invalid value.

For an individual component, you can explain the error to a user in terms relating to that specific input component by overriding the hints or by adding or overriding a detailed error message.

6.3.1 Override an Oracle ADF Validator or Converter Message with New Text

You may not see any messages when you follow these steps to select the Application Messages resource bundle:

  1. In JDeveloper, select the af validator tag in the UI page.
  2. Open the Property Inspector.
  3. Select the message attribute.
  4. Select the text resource.
  5. Select the Application Messages resource bundle.

In this case, you may need to override the default message from the validator. To do so, follow the procedure in "Displaying Hints and Error Messages for Validation and Conversion" in the Developing Web User Interfaces with Oracle ADF Faces.