Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers 10g (10.1.3.1.0) Part Number B25947-01 |
|
|
View PDF |
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:
Bases his entity on the framework extension class containing this additional code,
Defines one or more entity-level custom properties that follow a particular naming pattern. These indicate to the generic code which attributes should get recalculated when a particular other attribute changes.
To indicate that "when attribute A
changes, recalculate attributes X
, Y
, and Z
" he would add a custom property named Recalc_A
with the comma-separated value "X,Y,Z
" to indicate that.
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:
int[]
of attribute index numbers whose values have changed
Object[]
containing the new values for those attributes
The code does the following basic steps:
Iterates over the set of custom entity properties
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.
Determines the index of the recalc-triggering attribute.
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.
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.
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.