A very generic and powerful data driven coding and testing framework. A whole new paradigm enabling true data driven programming.
@Creator: Sidharth Yadav, Page updated on: 28th Sep 2019
Compilation Units and their categorization: Compilation units (aka cus) are the building blocks of this cu-framework. These represent processing blocks that carry out specific tasks in a generic manner which when intertwined with each other can easily build complex programming logic. The compilation units have associated xml tags and the entire processing logic can be defined using a simple xml structure. The xml can be stored inside a database, an external file, on cloud, or even constructed on the fly which means the processing logic can now accompany the data and even picked/switched/built at runtime. The cu framework also supports inheritance where in the logic residing inside one or more base xml files can directly be imported and auto-merged to build a new one. The framework also provides a revolutionary data driven testing framework.
Categorization:
The following compilation units can be categorized as first class units that logically can have an independent existance.
The following compilation units are meaningful only when used inside their parent cus. Their independent existance is of not much use (unless they are directly referenced and processed by the consuming application).
Java code
java.util.Map<String, java.util.Map<String, Object>> mapOfMaps = new java.util.HashMap<>();
mapOfMaps.put("CONTEXT-MAP", new java.util.HashMap<String, Object>()); //code to add key-value pair of 'abc' inside CONTEXT-MAP is deliberately skipped
Object id1 = mapOfMaps.get("CONTEXT-MAP").get("abc");
cu equivalent
<valueof id="id1" key="abc">
<map name="CONTEXT-MAP"/>
</valueof>
Note: ‘get’ is an alias tag of ‘valueof’ and can simply replace the later in the above cu snippet.
Java code
java.util.Map<String, java.util.Map<String, Object>> mapOfMaps = new java.util.HashMap<>();
mapOfMaps.put("CONTEXT-MAP", new java.util.HashMap<String, Object>()); //code to add key-value pair of 'abc' inside CONTEXT-MAP is deliberately skipped
Object id1 = mapOfMaps.get("CONTEXT-MAP").get("abc");
return id1.getClass().getName(); //if id1 represented a String value object then java.lang.String would be returned.
cu equivalent
<typeof>
<valueof id="id1" key="abc">
<map name="CONTEXT-MAP"/>
</valueof>
</typeof>
<!-- the above would return 'java.lang.String' assuming 'id1' evaluated to a String value object.
Scenario 1 Java code
Object id1 = "test-value";
cu equivalent
<set attribute="id1">
<valueof default="test-value"/>
</set>
Scenario 2 Java code
java.util.Map<String, java.util.Map<String, Object>> mapOfMaps = new java.util.HashMap<>();
mapOfMaps.put("CONTEXT-MAP", new java.util.HashMap<String, Object>());
mapOfMaps.get("CONTEXT-MAP").put("id1", "test-value");
cu equivalent
<set attribute="id1" in="CONTEXT-MAP">
<valueof default="test-value"/>
</set>
Scenarion 1 Java Code
//initialization code
java.util.Map<String, java.util.Map<String, Object>> mapOfMaps = new java.util.HashMap<>();
mapOfMaps.put("CONTEXT-MAP", new java.util.HashMap<String, Object>());
mapOfMaps.get("CONTEXT-MAP").put("id1", "test-value");
mapOfMaps.get("CONTEXT-MAP").put("id2", "test-value2");
//demonstration code
mapOfMaps.get("CONTEXT-MAP").remove("id1");
//context-map now doesn't contain the key 'id1'
cu equivalent
<unset attribute="id1">
<map name="CONTEXT-MAP"/>
</unset>
Scenarion 2 Java Code
//initialization code
java.util.Map<String, java.util.Map<String, Object>> mapOfMaps = new java.util.HashMap<>();
mapOfMaps.put("CONTEXT-MAP", new java.util.HashMap<String, Object>());
mapOfMaps.get("CONTEXT-MAP").put("id1", "test-value");
mapOfMaps.get("CONTEXT-MAP").put("id2", "test-value2");
//demonstration code
mapOfMaps.get("CONTEXT-MAP").clear();
//context-map is now empty
cu equivalent
<unset>
<map name="CONTEXT-MAP"/>
</unset>
Java Code
Object testInputObj = <assume some value assigned to it>;
Object returnValue = null;
if ("123".equals(testInputObj)) {
returnValue = "out-123";
} else if ("abc".equals(testInputObj)) {
returnValue = "out-abc";
} else if ("xyz".equals(testInputObj)) {
returnValue = "out-xyz";
} else {
returnValue = "out-default-value";
}
return returnValue;
cu equivalent
<select>
<using>
<valueof id="testInputObj" default="xyz"/> . <!-- instead of statically assigning a value of 'xyz' the valueof tag can also lookup and use value of a key inside a map. Refer [Valueof](#valueof) for example.-->
</using>
<conditional expression="1" value="out-123">
<condition id="1" expression="123">
<valueof>testInputObj</valueof>
</condition>
</conditional>
<conditional expression="1" value="out-abc">
<condition id="1" expression="abc">
<valueof>testInputObj</valueof>
</condition>
</conditional>
<conditional expression="1" value="out-xyz">
<condition id="1" expression="xyz">
<valueof>testInputObj</valueof>
</condition>
</conditional>
<conditional value="out-default-value"/>
</select>
Java Code
Object attr1 = "attribute1 value";
StringBuilder strBuilder = new StringBuilder();
strBuilder.append("'wrapper':{");
strBuilder.append("'attr1':'");
strBuilder.append(attr1);
strBuilder.append("'");
strBuilder.append("}");
return strBuilder.toString(); //this would return the json 'wrapper':{'attr1':'attribute1 value'}
cu equivalent
<group name="wrapper">
<set attribute="attr1">
<valueof default="attribute1 value"/>
</set>
</group>
Note: group can have any number of sub groups and set units inside it. Also nesting upto nth level is supported.
Java Code
Object attr1 = "attribute1 value";
StringBuilder strBuilder = new StringBuilder();
//strBuilder.append("'wrapper':{"); //note that this is commented
strBuilder.append("'attr1':'");
strBuilder.append(attr1);
strBuilder.append("'");
//strBuilder.append("}"); //this is also commented
return strBuilder.toString(); //this would return the json representation 'attr1':'attribute1 value'
cu equivalent
<headless-group name="wrapper">
<set attribute="attr1">
<valueof default="attribute1 value"/>
</set>
</headless-group>
Note: headless-group is also a type of group.
executable-group is also a type of group - the only difference being how it gets processed at runtime. Conceptually a group is evaluable but an executable group is evaluable as well as executable. In Java terms what this means is that the group implements an IEvaluable interface but an executable group implements both IEvaluable and IExecutable interfaces. For detailed documentation refer the link TBD
Java Code
Same as that provided for group earlier.
cu equivalent
Same as that provided for group earlier with the difference that in place of 'group' the tag to use would be 'executable-group' (quotes excluded).
headless-executable-group is also a type of group - the only difference being how it gets processed at runtime. Conceptually a group is evaluable but an executable group is evaluable as well as executable. In Java terms what this means is that the group implements an IEvaluable interface but an executable group implements both IEvaluable and IExecutable interfaces. For detailed documentation refer the link TBD
Java Code
Same as that provided for headless-group earlier.
cu equivalent
Same as that provided for headless-group earlier with the difference that in place of 'headless-group' the tag to use would be 'headless-executable-group' (quotes excluded).
A condition cu would evaluate to either true or false. The evaluation would be done by matching a value against a regular expression. If no expression is specified then the condition would evaluate to true. A condition cu can be used inside an ‘on’ or ‘conditional’ cu.
Java Code
Object valueToCheck = "abc";
boolean isConditionSatisfied = "xyz".equals(valueToCheck); //this obviously would evaluate to false
return isConditionSatisfied;
cu equivalent
<!-- the below condition would evaluate to false -->
<condition expression="xyz">
<valueof id="valueToCheck" default="abc"/>
</condition>
A conditional cu would evaluate to either true or false and can also return a value if the condition is satisfied. Also it can logically collate the boolean outcome of one or more condition(s) (using and, or, not) to arrive at a final true or false value. If no collating expression is specified then the conditional would evaluate to true. A conditional cu can be used inside an ‘on’, ‘select’, ‘extends’ or ‘break’ cu.
Java Code
Object valueToCheck = "abc";
boolean condition1 = valueToCheck.indexOf("a") != -1;
boolean condition2 = valueToCheck.indexOf("b") != -1;
boolean condition2 = valueToCheck.indexOf("c") != -1;
boolean isConditionalSatisfied = condition1 && condition2 && condition3; //this would evaluate to true
return isConditionalSatisfied? "Oh Yeah!!!": null;
cu equivalent
<!-- the below conditional would evaluate to true and return Oh Yeah!!! -->
<conditional expression="c1 and c2 and c3" value="Oh Yeah!!!">
<condition id="c1" expression="[a]">
<valueof evalIfNull="true">'abc'</valueof>
</condition>
<condition id="c2" expression="[b]">
<valueof evalIfNull="true">'abc'</valueof>
</condition>
<condition id="c3" expression="[c]">
<valueof evalIfNull="true">'abc'</valueof>
</condition>
</condition>
A loop cu can be used to implement unbounded loops (endless loops breaking on specific condition(s)), bounded loops (fixed number of times) and iterables (arrays, collections, maps).
Java Code (Unbounded loop)
int index = 0;
StringBuilder strBuilder = new StringBuilder();
while (true) {
if (index == 100) {
break;
}
if (index != 0) {
strBuilder.append(",");
}
strBuilder.append("'");
strBuilder.append(index);
strBuilder.append("'");
strBuilder.append(":");
strBuilder.append("'");
strBuilder.append("item-" + index);
strBuilder.append("'");
index++;
}
return strBuilder.toString();
cu equivalent
<loop name="endless-loop">
<using>
<valueof id="start" default="0"/>
</using>
<break>
<condition expression="true">
<valueof>index = 100</valueof>
</condition>
</break>
<set attribute="$index">
<valueof>concat('item-', index)</valueof>
</set>
</loop>
Java Code (Bounded loop)
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < 100; i++) { //loop 100 times
if (i != 0) {
strBuilder.append(",");
}
strBuilder.append("'");
strBuilder.append(index);
strBuilder.append("'");
strBuilder.append(":");
strBuilder.append("'");
strBuilder.append("item-" + index);
strBuilder.append("'");
}
return strBuilder.toString();
cu equivalent
<loop name="fixed-times-loop">
<using>
<valueof id="start" default="0"/>
<valueof id="times" default="100"/>
</using>
<set attribute="$index">
<valueof>concat('item-', index)</valueof>
</set>
</loop>
Java Code (Iterable loop for list)
StringBuilder strBuilder = new StringBuilder();
java.util.List<String> listItems = new java.util.ArrayList<>();
listItems.add("l1");
listItems.add("l2");
listItems.add("l3");
int index = 0;
for (String listItem : listItems) {
if (index != 0) {
strBuilder.append(",");
}
strBuilder.append("'");
strBuilder.append(index);
strBuilder.append("'");
strBuilder.append(":");
strBuilder.append("'");
strBuilder.append(listItem);
strBuilder.append("'");
index++;
}
return strBuilder.toString();
cu equivalent
<headless-group name="wrapper">
<!- initializer block to initialize list items for the purpose of this demo -->
<init>
<set attribute="list-items-as-json" in="TMP-MAP" createMapIfMissing="true"> <!-- tmp allocation -->
<valueof default="{'data':['l1', 'l2', 'l3']}"/>
</set>
<!-- let's make the list items available inside CONTEXT-MAP against the key 'list-items' -->
<set attribute="list-items" in="CONTEXT-MAP">
<valueof key="data">
<json container="TMP-MAP" name="list-items-as-json"/>
</valueof>
</set>
</init>
<!-- this is the core loop definition -->
<loop name="list-items-loop">
<using>
<valueof id="iterable" key="list-items">
<map name="CONTEXT-MAP"/>
</valueof>
</using>
<set attribute="$index">
<!-- items values are accessible inside the loop using 'item-value' variable. As the loop iterates
it would hold values l1, l2, l3 as initialized in the list earlier. -->
<valueof key="item-value">
<internal-map/>
</valueof>
</set>
</loop>
<!-- this is the finally block where we want to carry out some cleanups -->
<finally>
<!-- clear the objects that were initialized inside init block -->
<unset attribute="list-items-as-json">
<map name="TMP_MAP"/>
</unset>
</finally>
</headless-group>
Java Code (Iterable loop for map)
StringBuilder strBuilder = new StringBuilder();
java.util.Map<String, Object> map = new java.util.HashMap<>();
map.put("k1", "v1");
map.put("k2", "v2");
map.put("k3", "v3");
int index = 0;
for (java.util.Map.Entry<String, Object> entry : map.entrySet()) {
if (index != 0) {
strBuilder.append(",");
}
String key = entry.getKey();
Object value = entry.getValue();
strBuilder.append("'");
strBuilder.append(key);
strBuilder.append("'");
strBuilder.append(":");
strBuilder.append("'");
strBuilder.append(value);
strBuilder.append("'");
index++;
}
return strBuilder.toString();
cu equivalent
<headless-group name="wrapper">
<!- initializer block to initialize map for the purpose of this demo -->
<init>
<load-properties id="map-input">
<using>
<valueof id="stream" default='k1=v1;k2=v2;k3=v3'/>
<valueof id="stream-delimiter" default=";"/>
</using>
<init>
<set attribute="map-obj" in="CONTEXT-MAP" createMapIfMissing="true">
<valueof key="$_$execution-result-map">
<internal-map/>
</valueof>
</set>
</init>
</load-properties>
</init>
<!-- this is the core loop definition -->
<loop name="map-items-iterator">
<using>
<valueof id="iterable" key="map-obj">
<map name="CONTEXT-MAP"/>
</valueof>
</using>
<!-- For the current iteration, map key is accessbile using 'item-key' and its value is accessible using 'item-value'.
As the loop iterates it would hold pairs like (k1, v1), (k2, v2), (k3, v3). -->
<set attribute="$item-key">
<valueof key="item-value">
<internal-map/>
</valueof>
</set>
</loop>
<!-- this is the finally block where we want to carry out some cleanups -->
<finally>
<!-- clear the objects that were initialized inside init block -->
<unset attribute="map-obj">
<map name="CONTEXT-MAP"/>
</unset>
</finally>
</headless-group>
A log cu can be used to log messages to console, file or socket.
Java Code
//Using a logging framework like java.util.logging, log4j etc. to log the messages. Skipping the code.
cu equivalent
<log id="logging-test" target="console"> <!-- supported target types are console, file and socket -->
<valueof default="test-message"/>
</log>
An assert cu can be used to assert values inside cunit tests. Cunit testing framework is a revolutionary new way of writing pure data driven tests.
Java Code
//Using a framework like junit for doing the assertions. Skipping the code.
cu equivalent
<assert id="string-equals-ignore-case-abc" expression="condition1 or condition2">
<condition id="condition1" expression="abc">
<valueof key="string-to-check"> <!-- assume the strign value to assert is available as the key 'string-to-check' inside the map named TEST-DATA-MAP -->
<map name="TEST-DATA-MAP"/>
</valueof>
</condition>
<condition id="condition2" expression="ABC">
<valueof key="string-to-check"> <!-- assume the strign value to assert is available as the key 'string-to-check' inside the map named TEST-DATA-MAP -->
<map name="TEST-DATA-MAP"/>
</valueof>
</condition>
</assert>
Since the Compilation Units Framework extensively use properties map, the ‘load-properties’ cu is a convenience cu that loads and initializes properties map for consumption by other compilation units (group, condition, conditional, set etc.). It can load properties from a file and also from an in-memory stream.
Java Code
java.util.Properties props = new java.util.Properties();
props.setProperty("p1", "v1");
props.setProperty("p2", "v2");
props.setProperty("p3", "v3");
//use the loaded properties
cu equivalent
<load-properties id="props-from-stream">
<using>
<valueof id="stream" default='p1=v1;p2=v2;p3=v3'/>
<valueof id="stream-delimiter" default=";"/>
</using>
<!-- define the cus here that will consume the properties -->
</load-properties>
Java Code (load properties from file)
java.util.Properties props = new java.util.Properties();
props.load(new java.io.FileInputStream("./propsFilePath"));
//use the loaded properties
cu equivalent
<load-properties id="props-from-file">
<using>
<valueof id="src" default="./propsFilePath"/>
</using>
<!-- define the cus here that will consume the properties -->
</load-properties>
A ‘using’ cu initializes variables for use within the scope of its parent cus. The initialized variables are disposed off when the control comes out of the scope of the parent cu. The parent cus that can contain a ‘using’ cu are ‘select’, ‘executable-group’, ‘headless-executable-group’, ‘loop’ and ‘log’. Further, any custom tags defined in future are also free to use ‘using’ in their implementation. As for the cus that can be added as child of the ‘using’ cu - any tag which is of type org.cuframework.core.CompilationUnits.IEvaluable can be added as its child. Some examples of tags which are of type IEvaluable and can be added as child of ‘using’ cu are ‘valueof’, ‘get’, ‘select’, ‘group’, ‘headless-group’, ‘executable-group’, ‘headless-executable-group’ and ‘conditional’. ‘valueof’ is the most frequently used tag inside using.
examples
<loop id="demo-loop">
<using>
<!-- we are going to define 4 variables below which would be accessible within the
scope of the parent cu i.e. 'demo-loop' -->
<valueof id="start" default="0"/>
<valueof id="end" default="10"/>
<valueof id="custom-var1" default="value1"/>
<valueof id="custom-var2" default="value2"/>
</using>
<!-- define the loop body cus -->
</loop>
An ‘init’ cu can be used to perform initialization tasks for its parent cus i.e. group, headless-group, executable-group and headless-executable-group before the body gets executed. It can contain tags (cus) which are of type org.cuframework.core.CompilationUnits.IExecutable. Some examples of tags which are of type IExecutable and can be added as child of ‘init’ cu are ‘executable-group’, ‘headless-executable-group’, ‘loop’, ‘log’, ‘assert’ and ‘load-properties’.
examples
<group id="init-demo-group">
<init>
<log target="console">
<valueof id="enter-message" default="Entered init block."/>
</log>
<loop>
<using>
<!-- we are going to define 4 variables below which would be accessible within the
scope of the parent cu i.e. 'demo-loop' -->
<valueof id="start" default="0"/>
<valueof id="end" default="10"/>
<valueof id="custom-var1" default="value1"/>
<valueof id="custom-var2" default="value2"/>
</using>
<!-- define the loop body -->
</loop>
<log target="console">
<valueof id="exit-message" default="Exiting init block."/>
</log>
</init>
<!-- group body goes here -->
</group>
A ‘finally’ cu can be used to perform finalization tasks for its parent cus i.e. group, headless-group, executable-group and headless-executable-group after the body has executed. It can contain tags (cus) which are of type org.cuframework.core.CompilationUnits.IExecutable. Some examples of tags which are of type IExecutable and can be added as child of ‘finally’ cu are ‘executable-group’, ‘headless-executable-group’, ‘loop’, ‘log’, ‘assert’ and ‘load-properties’.
examples
<group id="finally-demo-group">
<!-- group body goes here -->
<finally>
<log target="console">
<valueof id="enter-message" default="Body execution is complete. Entered finally block."/>
</log>
<assert id="this-is-always-true" expression="true()"/>
<log target="console">
<valueof id="exit-message" default="Exiting finally block."/>
</log>
</finally>
</group>
The ‘extends’ cu is the hallmark of inheritance support inside the Compulation Units Framework. Any extensible CU can use it to inherit functionality from a base cu of the same type. The framework supports static inheritance as well as conditional inheritance. Also inheritance from multiple sources is supported. Merge as well as Replace strategies are supported. Examples of extensible cus available made available by the framework are group cus (i.e. group, headless-group, extensible-group, headless-extensible-group). New extensible units can also be created by implementing org.cuframework.core.CompilationUnits.IExtensible interface or by extending the org.cuframework.core.CompilationUnits.ExtensibleCompilationUnit class.
cu inheritance example
//--- assume the following group definition is saved inside an xml file called baseUnits.xml ---
<group id="base-demo-group">
<group id="base-subgroup">
<set attribute="base-attr">
<valueof default="base-attr-value"/>
</set>
</group>
<set attribute="base-attr1">
<valueof default="base-attr1-value"/>
</set>
<set attribute="to-be-overridden">
<valueof default="to-be-overridden-base"/>
</set>
</group>
//--- assume the following group definition is saved in a file other than baseUnits.xml ---
<group id="super-demo-group">
<extends default="./baseUnits.xml#base-demo-group"/> <!-- this would inherit the group and set cus from base file -->
<group id="new-group-in-super">
<set attribute="super-attr">
<valueof default="super-attr-value"/>
</set>
</group>
<set attribute="to-be-overridden">
<valueof default="to-be-overridden-super"/>
</set>
</group>
//--- the super-demo-group defined above would finally look like as follows ---
<group id="super-demo-group">
<group id="base-subgroup">
<set attribute="base-attr">
<valueof default="base-attr-value"/>
</set>
</group>
<set attribute="base-attr1">
<valueof default="base-attr1-value"/>
</set>
<group id="new-group-in-super">
<set attribute="super-attr">
<valueof default="super-attr-value"/>
</set>
</group>
<set attribute="to-be-overridden"> <!-- Note: The super group cu has overridden its base group counterpart -->
<valueof default="to-be-overridden-super"/>
</set>
</group>
The ‘break’ cu is used to break out of the loop iteration based on specific condition(s). It can have ‘condition’ and ‘conditional’ cus as it’s children. It can be added as a child only of the ‘loop’ cu. Added elsewhere would simply be ignored.
Java Code
for (int i = 0; i < 100; i++) {
if (i == 10) {
break;
}
}
cu equivalent
<loop id="break-demo">
<using>
<valueof id="start" default="0"/> <!-- loop starts with index value 0 -->
<valueof id="times" default="100"/> <!-- loop iterates for 100 times -->
</using>
<break> <!-- break condition -->
<condition expression="true">
<valueof>index = 10</value> <!-- check if index value equals 10 -->
</condition>
</break>
</loop>
The ‘on’ cu is used to control execution of a cu basis satisfaction of specific condition(s). If ‘on’ evaluates to true then the processing of the parent cu would take place, else skipped. ‘on’ can be added as a child of ‘valueof’, ‘get’, ‘set’, ‘unset’, ‘group’, ‘headless-group’, executable-group’, ‘headless-executale-group’, ‘map’, ‘internal-map’, ‘json’, ‘log’ and ‘load-properties’ cus and control their execution. ‘on’ can contain ‘condition’ and ‘conditional’ cus as it’s children.
Java Code
//assume dataType is of type String and is assigned a value from among "int", "boolean" or "char"
Object result = null;
if ("boolean".equals(dataType)) {
result = "Found boolean dataType";
}
return result != null? "'message':'" +result+ "'": "";
cu equivalent
<headless-group id="result">
<set attribute="message">
<on> <!-- only when this on condition is satisfied the attribute 'message' would be set inside the group -->
<condition expression="boolean">
<valueof>dataType</valueof>
</condition>
</on>
<valueof default="Found boolean dataType"/>
</set>
</headless-group>
//execution of the above headless-group would return the json {'message': 'Found boolean dataType'} if the variable dataType holds the string 'boolean'. Else execution would just result in an empty string.
The ‘map’ cu is used to perform key-value lookups inside a specified map. Also it supports removal of existing key-value pairs from inside the specified map. It can be contained inside the ‘valueof’ and ‘get’ cus as their child. It can have ‘on’ cu as its own child. The ‘internal-map’ cu is also a type of map which holds the internal execution state of the cu being processed (including its scoped variables).
Java Code
//demo initialization code
java.util.Map<String, Object> map = new java.util.HashMap<>();
map.put("k1", "v1");
//get the value of k1 from the map
Object val = map.get("k1");
//remove k1 from map
map.remove("k1");
cu equivalent
<!-- key lookup example -->
<valueof id="val" key="k1">
<map name="CONTEXT-MAP"/> <!-- here it is assumed that CONTEXT-MAP holds the runtime context required to process the cus -->
</valueof>
<!-- key removal example -->
<unset attribute="k1">
<map name="CONTEXT-MAP"/>
</unset>
<!-- internal-map example -->
<valueof key="some-runtime-property">
<internal-map/> <!-- Note: internal-map tag is sufficient to access the internal map -->
</value>
The ‘json’ cu is used to return attribute values from inside a json structure. If no attribute/key is specified it would convert the json into a Map representation and returns the entire map. Nested attributes can be accessed by using a field delimiter like dot(.) when specifying the attribute key. For example ‘a.b.c’ would return ‘c-value’ from inside the following json structure "{a: {b: {c: ‘c-value’}}}". ‘json’ cu can be contained inside the ‘valueof’ and ‘get’ cus as their child. It can have ‘on’ cu as its own child.
example
<!-- json lookup example -->
<valueof key="json-attribute">
<json container="CONTEXT-MAP" name="json-string-obj"/> <!-- here it is assumed that CONTEXT-MAP holds the runtime context required to process the cus. Also it is assumed that json-string-obj contains a json string e.g. {'json-attribute':'json-attribute-value'} -->
</valueof>
<!-- the above valueof cu would return the string value 'json-attribute-value' -->
Compilation Unit Tag | Tag Type(s) | Key Attributes | Child CUs | Key Parent Container CUs |
---|---|---|---|---|
valueof or get | evaluable | |||
set | evaluable | group | ||
select | evaluable | |||
group and headless-group | evaluable | |||
executable-group and headless-executable-group | evaluable, executable | <same as that of group> |