Skip Headers
Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers
10g (10.1.3.1.0)

Part Number B25947-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Master Index
Master Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

26.8 Implementing Automatic Attribute Recalculation

Section 6.10, "Adding Transient and Calculated Attributes to an Entity Object" explained how to add calculated attributes to an entity object. Often the formula for the calculated value will depend on other attribute values in the entity. For example, consider a LineItem entity object representing the line item of an order. The LineItem might have attributes like Price and Quantity. You might introduce a calculated attributed named ExtendedTotal which you calculate by multiplying the price times the quantity. When either the Price or Quantity attributes is modified, you might expect the calculated attribute ExtendedTotal to be updated to reflect the new extended total, but this does not happen automatically. Unlike a spreadsheet, the entity object does not have any built-in expression evaluation engine that understands what attributes your formula depends on.

To address this limitation, you can write code in a framework extension class for entity objects that add a recalculation facility. The SREntityImpl framework extension class in the SRDemo application contains the code shown in Example 26-17 that does this. It does not try to implement a sophisticated expression evaluator. Instead, it leverages the custom properties mechanism to allow a developer to supply a declarative "hint" about which attributes (e.g. X, Y, and Z) should be recalculated when another attribute like A gets changed.

To leverage the generic facility, the developer of an entity object:

To implement the functionality the SREntityImpl class overrides the notifyAttributesChanged()method. This method gets invoked whenever the value of entity object attributes change. As arguments, the method receives two arrays:

The code does the following basic steps:

  1. Iterates over the set of custom entity properties

  2. If property name starts with "Recalc_" it gets the substring following this prefix to know the name of the attribute whose change should trigger recalculation of others.

  3. Determines the index of the recalc-triggering attribute.

  4. If the array of changed attribute indexes includes the index of the recalc-triggering attribute, then tokenize the comma-separated value of the property to find the names of the attributes to recalculate.

  5. If there were any attributes to recalculate, add their attribute indexes to a new int[] of attributes whose values have changed.

    The new array is created by copying the existing array elements in the attrIndices array to a new array, then adding in the additional attribute index numbers.

  6. Call the super with the possibly updated array of changed attributes.

Example 26-17 Entity Framework Extension Code to Automatically Recalculate Derived Attributes

// In SREntityImpl.java
protected void notifyAttributesChanged(int[] attrIndices, Object[] values) {
  int attrIndexCount = attrIndices.length;
  EntityDefImpl def = getEntityDef();
  HashMap eoProps = def.getPropertiesMap();
  if (eoProps != null && eoProps.size() > 0) {
    Iterator iter = eoProps.keySet().iterator();
    ArrayList otherAttrIndices = null;
    // 1. Iterate over the set of custom entity properties
    while (iter.hasNext()) {
      String curPropName = (String)iter.next();
      if (curPropName.startsWith(RECALC_PREFIX)) {
        // 2. If property name starts with "Recalc_" get follow attr name
        String changingAttrNameToCheck = curPropName.substring(PREFIX_LENGTH);
        // 3. Get the index of the recalc-triggering attribute
        int changingAttrIndexToCheck =
            def.findAttributeDef(changingAttrNameToCheck).getIndex();
        if (isAttrIndexInList(changingAttrIndexToCheck,attrIndices)) {
          // 4. If list of changed attrs includes recalc-triggering attr,
          //    then tokenize the comma-separated value of the property
          //    to find the names of the attributes to recalculate
          String curPropValue = (String)eoProps.get(curPropName);
          StringTokenizer st = new StringTokenizer(curPropValue,",");
          if (otherAttrIndices == null) {
            otherAttrIndices = new ArrayList();
          }
          while (st.hasMoreTokens()) {
            String attrName = st.nextToken();
            int attrIndex = def.findAttributeDef(attrName).getIndex();
            if (!isAttrIndexInList(attrIndex,attrIndices)) {
              Integer intAttr = new Integer(attrIndex);
              if (!otherAttrIndices.contains(intAttr)) {
                otherAttrIndices.add(intAttr);
              }
            }
          }
        }
      }
    }
    if (otherAttrIndices != null && otherAttrIndices.size() > 0) {
      // 5. If there were any attributes to recalculate, add their attribute
      //    indexes to the int[] of attributes whose values have changed
      int extraAttrsToAdd = otherAttrIndices.size();
      int[] newAttrIndices = new int[attrIndexCount + extraAttrsToAdd];
      Object[] newValues = new Object[attrIndexCount + extraAttrsToAdd];
      System.arraycopy(attrIndices,0,newAttrIndices,0,attrIndexCount);
      System.arraycopy(values,0,newValues,0,attrIndexCount);
      for (int z = 0; z < extraAttrsToAdd; z++) {
        newAttrIndices[attrIndexCount+z] =
         ((Integer)otherAttrIndices.get(z)).intValue();
        newValues[attrIndexCount+z] =
         getAttribute((Integer)otherAttrIndices.get(z));
      }
      attrIndices = newAttrIndices;
      values = newValues;
    }
  }
  // 6. Call the super with the possibly updated array of changed attributes    
  super.notifyAttributesChanged(attrIndices, values);
}

The ServiceHistory entity object in the SRDemo application uses this feature by setting a custom entity property named Recalc_SvhType with the value of Hidden. This way, anytime the value of the SvhType attribute is changed, the value of the calculated Hidden attribute is recalculated.