Structure Map composer - FML Notation
enTL;TR
Click here to view
- Find target objects.
- Flatmap target fields that have input connection.
given: Model: # AModel (target) - field-1 - field-2 - field-3 Rules: Rule.x -> field-1 Rule.y -> field-3 Expected output: [[AModel, field-1], [AModel, field-3]]
- Iterate target fields.
- Create sub FML for each field.
- Sort objects (topological sorting) result into the
list
. - Select the first and last element in the
list
, and begin “moving inwards”. - Every iteration is a FML rule.
- Every iteration MUST have source and target objects
- Create source and target objects
- Find rules in between.
- Nest iteration rules. Not the in between rules, but 7. rules
Preparation
The first step is to find all the target
objects, since the primary goal of FML transformations is to correctly fill the fields of the target objects with the transformed values
For each target
object, we find the fields to which connections are drawn, whether through direct copying, sub-object creation, or connections from a rule’s output. It’s important to note that not all fields of the target object necessarily have input connections from other objects.
For each such field, a sub-FML
is created, containing only the objects, rules, and connections necessary to populate the corresponding field of the target object. There are no unnecessary objects or rules in the sub-FML. It describes all the connections and objects required to compose the result and set it in the target
object field.
View sub-FML visualization
Next, with the sub-FML in hand, topological sorting is performed. Topological sorting builds a correct sequence of actions, taking into account the dependencies between them.
After sorting, two lists are produced:
- List
A
, consisting ofsource/element
objects, where the first element issource
, forming a chain of objects from left to right. - List
B
, consisting oftarget/object
objects, where the first element istarget
, forming a chain of objects from right to left.
In this way, we determine the order and the number of levels that need to be created/visited as a result of the algorithm’s operation. The maximum depth corresponds to the greater of the two lists’ lengths (max(A.length, B.length)
), and it equals the maximum number of iterations required to traverse all levels.
Composing the FML
With two lists and indices i
and j
that indicate positions in lists A
and B
respectively, we can start traversing and composing FML rules. Before each iteration, both indices are incremented. If there’s no element at the index in the list for which this index is used, the index decrements to the previous value. This ensures that while going through all levels, we always have the left/source
and right/target
values.
In this example, with each subsequent iteration, the source
object remains A
. Simultaneously, with an increase in the index j
, the target
object becomes the next object in list B
.
Objects
Each iteration to the next level, in the context of FML, means the creation of a new FML rule. Every FML rule requires source and target objects. As mentioned earlier, at each iteration, either a new element is selected, or the previous one is used.
If a new element is selected, in the FML context, we must either choose a sub-element (ctx.field as f1
) or create a new resource (create('Resource') as r1
) to which values will be assigned later.
Rules
Once the source
and target
objects have been created/selected, and we know which objects we are in the context of, we can find all the rules that can be created within this context.
At the moment we have two types of rules:
- Direct - copying a value from the source object to the target object.
- Regular - a square with the type
rule
in the FML editor, from which a connection is drawn to the field of thetarget
object.
Because there are two types of rules, they are created differently. Creating FML rules for direct copying is relatively straightforward. In the current context, all connections between the source
and target
objects are found, and the corresponding FML copy
rule is created.
For the other type of rule, a recursive search is run for each target
object field, and a chain of rules is created that lead to the creation or assignment of a value to that field. When the chain is found, a rule handler is selected for each rule within the chain.
There are quite a few rule handlers; there is a basic one, but if it is necessary to handle a rule in another way, for example append, to combine several source objects into one value, then a separate handler for append rule is created.
Example of different rule types
As a result, we get a list of all rules between objects in the current iterated context, that are added as a sub-rule to the main FML rule.