Table of Contents | Prev | Next | Bottom |
Quick Table of Contents |
---|
D Recalculation Sequence Algorithm (Non-Normative) D.1 Details on Creating the Master Dependency Directed Graph D.2 Details on Creating the Pertinent Dependency Subgraph D.3 Details on Computing Individual Vertices D.4 Example of Calculation Processing |
XForms Processors are free (and encouraged) to skip or optimize any steps in this algorithm, as long as the end result is the same. The XForms recalculation algorithm considers model items and model item properties to be vertices in a directed graph. Edges between the vertices represent computational dependencies between vertices.
Following is the default handling for a recalculate
action. Action recalculate
is defined in 9.3 recalculate.
A master dependency directed graph is created as detailed in D.1 Details on Creating the Master Dependency Directed Graph.
To provide consistent behavior, implementations must reduce the number of vertices to be processed by computing a pertinent dependency subgraph consisting only of vertices and edges that are reachable from nodes that require recomputation. This is detailed in D.2 Details on Creating the Pertinent Dependency Subgraph. Note that on a first recomputation (such as on form load), the pertinent dependency subgraph will be the same as the master dependency directed graph.
A topological sort is performed on the vertices of the pertinent dependency subgraph, resulting in an order of evaluation in which each vertex is evaluated only after those vertices on which it depends and before all vertices which depend on it. This is detailed in D.3 Details on Computing Individual Vertices.
The recalculate
process completes.
The master dependency directed graph can be considered an array with one record for each vertex, each having the following fields:
InstanceNode: a reference to the associated instance data node
type: indicates the aspect of the instance node represented by the vertex (the text content or a model item property such as readOnly or required)
depList: a list of vertices that refer to this vertex
inDegree: the number of vertices on which this vertex depends
visited: a flag used to ensure vertices are not added to a subgraph multiple times
index: an association between vertices in the master dependency directed graph and a subgraph
The depList
for each vertex is assigned to be the referenced XML nodes of a given
instance node, which are obtained by parsing the computed expression in the
node (e.g., the calculate, relevant, readOnly, or required property). Any
expression violating any Binding Expression Constraint causes a fatal
exception, terminating the recalculate
process.
The depList
for a vertex v
is assigned to be the vertices other than v
whose computational expressions reference v
(described below). Vertex v
is excluded from its own depList
to allow self-references to occur without causing a circular reference
exception.
A computational expression appearing in a calculate
attribute controls the text content (value) of one or more instance
nodes. A vertex exists for each instance node to represent the expression in
the context of the node. Likewise, computational expressions for model item
properties such as readOnly
and required
are applied to one or more instance nodes, and vertices are created to
represent such expressions in the context of each applicable node. The
computational expression of each vertex must be examined to determine the XML
nodes to which it refers. Any expression violating any Binding Expression
Constraint causes a fatal exception, terminating the recalculate
process. A computation expression refers to a vertex v
if a subexpression indicates the InstanceNode for v
and v
represents the instance node text content (its value). In this version
of XForms, model item properties such as readOnly
and required
cannot be referenced in an expression.
If all calculations must be performed, which is the case on form load, then the pertinent dependency subgraph is simply a duplicate of the master dependency directed graph. If the recalculation algorithm is invoked with a list of changed instance data nodes since the last recalculation, then the pertinent dependency subgraph is obtained by exploring the paths of edges and vertices in the computational dependency directed graph that are reachable from each vertex in the change list. The method of path exploration can be depth first search, a suitable version of which appears in the pseudo-code below.
This algorithm creates a pertinent dependency subgraph S
from
a list of changed instance data nodes L<sub>c</sub>
. Variables such as v
and w
represent vertices in the master dependency directed graph. The same
variables ending with S
indicate
vertices in the pertinent dependency subgraph
S
.
// Use depth-first search to explore master digraph subtrees rooted at // each changed vertex. A 'visited' flag is used to stop exploration // at the boundaries of previously explored subtrees (because subtrees // can overlap in directed graphs). for each vertex r in Lc if r is not visited { Push the pair (NIL, r) onto a stack while the stack is not empty { (v, w) = pop dependency pair from stack if w is not visited { Set the visited flag of w to true Create a vertex wS in S to represent w Set the index of w equal to the array location of wS Set the index of wS equal to the array location of w Set the InstanceNode of wS equal to the InstanceNode of w Set the type of wS equal to the type of w For each dependency node x of w Push the pair (w, x) onto the stack } else Obtain wS from index of w if v is not NIL { Obtain vS from index of v Add dependency node for wS to vS Increment inDegree of wS } } } // Now clear the visited flags set in the loop above for each vertex vS in S { Obtain v from index of vS Assign false to the visited flag of v }
Note that the number of vertices and dependency nodes in the pertinent
dependency subgraph is not known beforehand, but a method such as array
doubling (see [DDJ-ArrayDoubling]) can be used to ensure that building the
subgraph is performed in time linear in the size of S
.
The following steps process vertices, resulting in a recalculated form:
A vertex with inDegree of 0 is selected for evaluation and removed from the pertinent dependency subgraph. In the case where more than one vertex has inDegree zero, no particular ordering is specified. If the pertinent dependency subgraph contains vertices, but none have an inDegree of 0, then the calculation structure of the form has a loop, and a fatal exception must be thrown, terminating the recalculate event.
If the vertex corresponds to a computed item, computed expressions are evaluated as follows:
calculate
: If the value of the model item changes, the corresponding instance
data is updated and the dirty flag is set.
relevant
, readOnly
, required
, isValid
: If any or all of these computed properties change, the new settings
are immediately placed into effect for associated form controls.
For each vertex in the depList
of the removed vertex, decrement the inDegree by 1.
If no vertices remain in the pertinent dependency subgraph, then the calculation has successfully completed. Otherwise, repeat this sequence from step 1.
For example, consider six vertices a
, b
, v
, w
, x
, and y
. Let a
and b
represent the text content of instance nodes that will be set by a
binding from user input controls. Let v
and w
be vertices representing the calculated value and the validity property
of a third instance node c
. These vertices would result from a bind
element B
with calculate
and isValid
attributes and a ref
attribute that indicates c
. Suppose that the value of c
is the product of a
and b
and that the value is only valid if it does not exceed 100. Likewise,
suppose x
and y
are vertices representing the calculated value and the validity
property of a fourth instance node d
. Let the value of d
be the sum of a
and b
, and let d
be valid if the value does not exceed 20. The figure below depicts the
dependency digraph for this example.
Vertices a
and b
have edges leading to v
and x
because these vertices represent the calculate expressions of c
and d
, which reference a
and b
to compute their product and sum, respectively. Similarly, v
and x
have directed edges to w
and y
, respectively, because w
and y
represent the isValid expressions of c
and d
, which reference the values of c
and d
to compare them with boundary values.
If a
and b
are initially equal to 10, and the user changes a
to 11, then it is necessary to first recalculate v
(the value of c
) then recalculate w
(the validity property of the value of c
). Likewise, x
(the value of d
) must be recalculated before recalculating y
(the validity property of the value of d
). In both cases, the validity of the value does not change to false
until after the new product and sum are computed based on the change to
a
. However, there are no interdependencies between v
and x
, so the product and sum could be computed in either order.
The pertinent subgraph excludes b
and only vertex a
has in-degree of zero. The vertex a
is processed first. It is not a computed vertex, so no recalculation
occurs on a
, but its removal causes v
and x
to have in-degree zero. Vertex v
is processed second. Its value changes to 121, and its removal drops
the in-degree of vertex w
to zero. Vertex x
is processed next, changing value to 21. When x
is removed, its neighbor y
drops to in-degree zero. The fourth and fifth iterations of this
process recalculate the validity of w
and y
, both of which change to false.
Table of Contents | Top |