Struct Code Examples
You can copy and paste the code in this topic into a Uniface trigger or operation (such as the exec operation of a test component).
Most samples are free of context. Only the example
STRUCT_CONVERSIONS
is a larger example that assumes two (very small) painted
entities.
; The following ProcScript modules demonstrate the use of Structs. ; After testing the component, display the message frame to view the results. call STRUCT_BASICS() ; Creating, modifying and deleting Structs call MEMBER_NAME_CHARSET() ; Using reserved names and special characters in member names call COPY_STRUCT() ; Copying Struct members/references call NAME_INHERITANCE() ; How member names behave during a copy call TAGS_INHERITANCE() ; How tags behave during a copy call MOVE_STRUCT() ; Moving a Struct call REMOVE_MEMBER() ; Detaching a Struct member call STRUCT_AS_PARAMS ; Using Structs as parameters call IDENTICAL_MEMBER_NAMES(); Member names are not necessarily unique call MEMBER_REFERENCES() ; Using (old) references after member reassignment call STRUCT_COLLECTIONS() ; The Struct as a reference collection call STRUCT_LOOPS() ; Looping over a Struct call REMOVE_STRUCT_LEVEL() ; Removing a level from a Struct call INSERT_STRUCT_LEVEL() ; Inserting an additional level into a Struct call STRUCT_CONVERSIONS() ; Converting between XML/Struct/component ; ======================================================================= ; PrintHeader ; ======================================================================= ; This entry is called by other modules to display the entry header in ; the message frame entry PrintHeader params string pEntryName : IN endparams putmess "===============================" putmess " run of %%pEntryName%%%" putmess "===============================" end ;- entry printHeader ; ======================================================================= ; STRUCT_BASICS ; ======================================================================= ; This entry shows how: ; * Multiple references can give access to the same data ; * Struct data is deleted when there are no more struct variables ; that refer to it (or any of its parents) entry STRUCT_BASICS variables struct vStruct, vPerson ; Declare struct reference variables endvariables ; Display entry header in the message frame: call printHeader("STRUCT_BASICS") ; --- Build up a Struct --- ; Create a Struct: vStruct = $newstruct ; Note: A Struct is created on the fly when a member ; is assigned to the variable. This statement is ; therefore allowed, but not required: ; Create Struct members: vStruct->group = $newstruct vStruct->group->person = $newstruct vStruct->group->person->firstname = "John" vStruct->group->person->email = "john@home.com" ; Note: Struct members must be created explicitly; ; they are not created on the fly. ; Display the Struct in the message frame: putmess "Struct referred to by vStruct: " putmess vStruct->$dbgstring ; Result: ; [] ; [group] ; [person] ; [firstname] = "John" ; [email] = "john@home.com" ; --- Update the Struct --- ; Get a reference to the 'person' node: vPerson = vStruct->group->person putmess "Struct referred to by vPerson: " putmess vPerson->$dbgstring ; Result: ; [person] ; [firstname] = "John" ; [email] = "john@home.com" ; Update the Struct refered to by vPerson. ; This also updates vStruct->group->person: vPerson->email = "john.smith@home.com" putmess "Struct referred to by vStruct: " putmess vStruct->$dbgstring putmess "Note: 'email' changed with vPerson is also changed in vStruct. " ; Result: ; [] ; [group] ; [person] ; [firstname] = "John" ; [email] = "john.smith@home.com" ; Note: 'email' changed with vPerson is also changed in vStruct. ; --- Delete a Struct --- ; Display the parent of vPerson putmess "The parent of vPerson is: [%%(vPerson->$parent->$name)%%%]" ; Result: The parent of the 'person' member is: [group] ; Assign a different value to vStruct, and display the Structs: vStruct = $newstruct putmess "After assigning a new value to vStruct, vStruct points to empty Struct:" putmess vStruct->$dbgstring putmess "vPerson is still valid: [%%(vPerson->firstname)%%%]" putmess "but the parent of vPerson is now invalid: [%%(vPerson->$parent->$name)%%%]" putmess "resulting in a ProcScript error: %%$procerror: %%$item("DESCRIPTION", $procerrorcontext)" ; Result: ; After assigning a new value to vStruct, vStruct points to empty Struct: ; [] = "" ; vPerson is still valid: [John] ; but the parent of vPerson is now invalid: [] ; resulting in a ProcScript error: -84: Not a valid Handle or Struct specified end ;- entry STRUCT_BASICS ; ======================================================================= ; MEMBER_NAME_CHARSET ; ======================================================================= ; This entry demonstrates that you can include spaces, special characters, ; or reserved words as member names, by enclosing the name in quotation ; marks when assigning members to a Struct. entry MEMBER_NAME_CHARSET variables struct vStruct endvariables #comment Use DQ for double quotes to improve readability: #define DQ %%"%%% ; Display entry header in the message frame: call printHeader("MEMBER_NAME_CHARSET") ; Use a space in a member name: vStruct->"a name" = "abc" putmess "Member names can include spaces:" putmess vStruct->$dbgstring putmess $concat(" For Struct [a name], vStruct-><DQ>a name<DQ> returns: ", %\ vStruct->"a name") ; Result: ; Member names can include spaces: ; [] ; [a name] = "abc" ; ; For Struct [a name], vStruct->"a name" returns: abc ; Use names that conflict with, for example, Struct functions vStruct->membername = $newstruct vStruct->membername->"$name" = "dollarname" putmess "Member names can match Struct functions names:" putmess vStruct->membername->$dbgstring putmess " membername->$name = %%(vStruct->membername->$name)%%%" putmess " membername-><DQ>$name<DQ> = %%(vStruct->membername->"$name")%%%" ; Result: ; Member names can match Struct functions names: ; [membername] ; [$name] = "dollarname" ; ; membername->$name = membername ; membername->"$name" = dollarname end ;- entry MEMBER_NAME_CHARSET ; ======================================================================= ; COPY_STRUCT ; ======================================================================= ; This entry demonstrates what happens when ; copying references to Structs (copy by reference) and ; copying the Struct itself (copy by value). entry COPY_STRUCT variables struct vStruct1, vStruct2 endvariables ; Display entry header in the message frame: call printHeader("COPY_STRUCT") ; Copy by reference ; Build a Struct with two members vStruct1->a = "AAA" vStruct1->b = "BBB" ; Copy vStruct1 to vStruct2 (by reference): ; the left side of the assignment is a struct variable vStruct2 = vStruct1 ; Update using vStruct2: vStruct2->b = "BBB-updated" putmess "Although vStruct2 changed the Struct, vStruct1->b returns the change:" putmess "%%(vStruct1->b)%%%" ; Result: ; Although vStruct2 changed the Struct, vStruct1->b returns the change: ; BBB-updated ; Copy by value ; Rebuild the Struct from scratch: vStruct1= $newstruct vStruct2= $newstruct vStruct1->a = "AAA" vStruct1->b = "BBB" ; Copy vStruct1 to vStruct2 (by value): ; the left side of the assignment is a Struct member vStruct2->subnode = vStruct1 ; Update the copied struct vStruct2->subnode->b = "BBB-updated" ; Compare the resulting structs: putmess "Struct refered to by vStruct1:" putmess vStruct1->$dbgstring putmess "Struct refered to by vStruct2->subnode:" putmess vStruct2->subnode->$dbgstring ; Result: ; Struct refered to by vStruct1: ; [] ; [a] = "AAA" ; [b] = "BBB" ; ; Struct refered to by vStruct2->subnode: ; [subnode] ; [a] = "AAA" ; [b] = "BBB-updated" end ;- entry COPY_STRUCT ; ======================================================================= ; NAME_INHERITANCE ; ======================================================================= ; This entry demonstrates that when copying a Struct member, ; the name of the target member is determined by the name ; specified on the left side, if available, and otherwise by ; the name of the right-hand side. entry NAME_INHERITANCE variables struct vStruct1, vStruct2 endvariables ; Display entry header in the message frame: call printHeader("NAME_INHERITANCE") ; Inheritance of member names when left side of assignment has name vStruct1 = $newstruct vStruct2 = $newstruct vStruct1->R = "AAA" ; Copy one Struct to the other: vStruct2->L = vStruct1->R putmess "Left side of assignment has name, so it is used." putmess "Name of copied member is 'L':" putmess vStruct2->$dbgstring ; Result: ; Left side of assignment has name, so it is used. ; Name of copied member is 'L': ; [] ; [L] = "AAA" ; Inheritance of member names when left side of assignment has NO name vStruct1 = $newstruct vStruct2 = $newstruct vStruct1->R = "AAA" ; Copy one Struct to the other: vStruct2->*{-1} = vStruct1->R putmess "Left side of assignment has no name, so name of copied Struct it is used." putmess "Name of member is 'R':" putmess vStruct2->$dbgstring ; Result: ; Left side of assignment has no name, so name of copied Struct it is used. ; Name of copied member is 'R': ; [] ; [R] = "AAA" ; Inheritance of member names neither side of assignment specifies a name vStruct1 = $newstruct vStruct2 = $newstruct vStruct1->R = "AAA" vStruct2->L = "BBB" vStruct2->*{1} = vStruct1->*{1} ;- overwrites member L putmess "Neither side of assignment specifies a name, so name of right-hand Struct is used." putmess "Name of copied member is 'R':" putmess vStruct2->$dbgstring ; Result: ; Neither side of assignment specifies a name, so name of right-hand ; Struct is used. ; Name of copied member is 'R': ; [] ; [R] = "AAA" end ; entry NAME_INHERITANCE ; ======================================================================= ; TAGS_INHERITANCE ; ======================================================================= ; This entry demonstrates that annotations (tags) are also copied ; when copying a Struct. However, when assigning a scalar value to ; a Struct, source tags are not affected. entry TAGS_INHERITANCE variables struct vStruct1 endvariables ; Display entry header in the message frame: call printHeader("TAGS_INHERITANCE") ; Tags inheritance when copying a Struct: ; Build a Struct and assign annotations (tags) tag (annotation): vStruct1->member1 = "value A" vStruct1->member1->$tags->someTags = "tag value A" vStruct1->member2 = "value B" vStruct1->member2->$tags->someTags = "tag value B" putmess "Struct with annotations: " putmess vStruct1->$dbgstring ; Result: ; Struct with annotations: ; [] ; [member1] = "value A" ; [$tags] ; [someTags] = "tag value A" ; [member2] = "value B" ; [$tags] ; [someTags] = "tag value B" ; Overwrite member2 with a copy of member1; vStruct1->member2 = vStruct1->member1 putmess "The original tags are replaced when a new Struct is assigned:" putmess vStruct1->member2->$dbgstring ; Result: ; The original tags are replaced when a new Struct is assigned: ; [member2] = "value A" ; [$tags] ; [someTags] = "tag value A" ; Tags inheritance when assigning a scalar value ; Assign a new value to member2: vStruct1->member2 = "updated value" putmess "The tags are not affected when a scalar value is assigned:" putmess vStruct1->member2->$dbgstring ; Result: ; The tags are not affected when a scalar value is assigned: ; [member2] = "updated value" ; [$tags] ; [someTags] = "tag value A" end ;- entry TAGS_INHERITANCE ; ======================================================================= ; MOVE_STRUCT ; ======================================================================= ; This entry demonstrates how an existing struct can be ; inserted as a member of another struct. entry MOVE_STRUCT variables struct vStruct1, vStruct2, vSteadyRef endvariables ; Display entry header in the message frame: call printHeader("MOVE_STRUCT") ; Build Struct vStruct1 vStruct1->a = "AAA" vStruct1->c = "CCC" ; Build Struct vStruct2 vStruct2->b = "BBB" ; Make vStruct2->b a member of vStruct1, by updating its $parent: vStruct2->b->$parent = vStruct1 putmess "Struct vStruct2->b moved to vStruct1, and appended as member 'b': " putmess vStruct1->$dbgstring ; Result: ; Struct vStruct2->b moved to vStruct1, and appended as member 'b': ; [] ; [a] = "AAA" ; [c] = "CCC" ; [b] = "BBB" ; Reposition the newly-added member b (assuming this is relevant) ; NOTE: It is no longer possible to use vStruct2->b, because ; it has moved the Struct from vStruct2 to vStruct1. ; For a more elegant approach, see SUGGESTION below. vStruct1->b->$index = 2 putmess "Newly-added Struct 'b' has been repositioned:" putmess vStruct1->$dbgstring ; Result: ; Newly-added Struct 'b' has been repositioned: ; [] ; [a] = "AAA" ; [b] = "BBB" ; [c] = "CCC" ;- SUGGESTION : use a dedicated reference for manipulation ;- To prevent the awkward switch from vStruct2 to vStruct1 in these lines: ; vStruct2->b = "BBB" ; vStruct2->b->$parent = vStruct1 ; vStruct1->b->$index = vStruct1 ;- it is easier to use a separate reference for the struct manipulation: vStruct2->b = "BBB" vSteadyRef = vStruct2->b ;- vSteadyRef refers the b-struct vSteadyRef->$parent = vStruct1 vSteadyRef->$index = 2 ;- the end result is the same: vStruct1 is assigned the member 'b' at position 2 end ;- entry MOVE_STRUCT ; ======================================================================= ; REMOVE_MEMBER ; ======================================================================= ; This entry demonstrates how Struct members can be ; removed (detached) from their parent. ; Members are *deleted* only when there are no more ; references to the Struct. entry REMOVE_MEMBER variables struct vStruct endvariables ; Display entry header in the message frame call printHeader("REMOVE_MEMBER") ; Build a Struct with two members vStruct->a = "AAA" vStruct->b = "BBB" putmess vStruct->$dbgstring ; Result: ; [] ; [a] = "AAA" ; [b] = "BBB" ; Detach b from its parent: vStruct->b->$parent = "" putmess "Member 'b' has been removed:" putmess vStruct->$dbgstring ; Result: ; [] ; [a] = "AAA" end ; entry REMOVE_MEMBER ; ======================================================================= ; STRUCT_AS_PARAMS ; ======================================================================= ; This sentry shows the use of Structs as IN, OUT or INOUT parameters ; in entries and partner operations. ; When variables or parameters of type Struct are passed between ; entries or partner operations, a reference to that variable is ; passed. The size of a Struct does not matter because only the ; reference is passed, not a copy of the whole Struct. ; By way of contrast, when a string parameter is passed, a copy of ; the variable is created, which can be expensive if the string ; is very large. ; This example consists of four entries, that demonstrate how ; references to a Struct created in one entry are passed to ; another entry using IN, OUT or INOUT parameters. ; =========== STRUCT_PARAMS_IN_DO =========== ; This entry sets up the example, performs the calls to the ; other entries, and examines the result of the calls. entry STRUCT_AS_PARAMS variables struct vStructAsIn struct vStructAsOut struct vStructPitFall endvariables ; Display entry header in the message frame call printHeader("STRUCT_AS_PARAMS") ; Create the Struct vStructAsIn = $newstruct ; The Struct variable vStructAsIn is created and passed to the ; entry STRUCT_PARAMS_IN_DO() as an IN parameter. ; Entry STRUCT_PARAMS_IN_DO manipulates the Struct and returns ; without passing anything back. Both entries reference the same ; Struct, so the calling entry can examine the result of the ; second entry. putmess "(Empty) Struct before call to entry STRUCT_PARAMS_IN_DO:" putmess vStructAsIn->$dbgstring ; Result: ; (Empty) Struct before call to entry STRUCT_PARAMS_IN_DO: ; [] = "" ; Pass the Struct to entry STRUCT_PARAMS_IN_DO call STRUCT_PARAMS_IN_DO(vStructAsIn) ; Examine the result putmess "vStructAsIn after entry call:" putmess vStructAsIn->$dbgstring ; Result: ; vStructAsIn after entry call: ; [] = "" ; [aValue] = "1111" ; In STRUCT_PARAMS_OUT_DO, a Struct is created and ; passed back as a reference, pointing to the Struct ; it just created. ; Display the Struct referenced by vStructAsOut putmess "(Uninitialized, unassigned) Struct before entry call:" putmess " vStructAsOut->$dbgstring returns an empty string: %%(vStructAsOut->$dbgstring)%%%" ; Result: ; (Uninitialized, unassigned) Struct before entry call: ; vStructAsOut->$dbgstring returns an empty string: call STRUCT_PARAMS_OUT_DO(vStructAsOut) ; Examine the Struct putmess "vStructAsOut after entry call:" putmess vStructAsOut->$dbgstring ; Result: ; vStructAsOut after entry call: ; [] ; [aValue] = "2222" ; When passing a Struct as parameter, the Struct must be created ; by the module that passes the Struct. The following call shows ; what happens if the Struct is not created prior to passing a ; reference to that Struct. ; In the call to STRUCT_PARAMS_PITFALL_DO thestruct variable ; vStructPitFall is not initialized before calling entry ; STRUCT_PARAMS_PITFALL_DO ; This sample provides two solutions in the comments: ; Solution #1 is in the calling entry. ; Solution #2 is in the called entry. ; Solution #1: Explicitly create a Struct here ; vStructPitFall = $newstruct ; Problem: Call an entry with struct vStructPitFall IN parameter ; before creating a Struct call STRUCT_PARAMS_PITFALL_DO(vStructPitFall) ; Examine the result putmess "Struct manipulations done by the called module are not visible" putmess "if the Struct is not created in the calling module." putmess " vStructPitFall->$dbgstring: %%(vStructPitFall->$dbgstring)%%%" ; Result: ; Struct manipulations done by the called module are not visible ; if the Struct is not created in the calling module. ; vStructPitFall->$dbgstring: ; Result if solution #1 or #2 has been applied: ; Struct manipulations done by the called module are not visible ; if the Struct is not created in the calling module. ; vStructPitFall->$dbgstring: ; [] ; [aValue] = "3333" end ;- entry STRUCT_AS_PARAMS ; =========== STRUCT_PARAMS_IN_DO =========== ; This entry takes a Struct as IN parameter and sets its value entry STRUCT_PARAMS_IN_DO params struct pStruct: IN endparams pStruct->aValue = "1111" end ;- entry STRUCT_PARAMS_IN_DO ; =========== STRUCT_PARAMS_OUT_DO =========== ; This entry creates a Struct passes it back as an OUT parameter entry STRUCT_PARAMS_OUT_DO params struct pStruct: OUT endparams ; Create a Struct with one member. ; Note: The next instruction is redundant; the Struct is implicitly created in ; the subsequent statement pStruct = $newstruct pStruct->aValue = "2222" end ;- entry STRUCT_PARAMS_OUT_DO ; =========== STRUCT_PARAMS_PITFALL_DO =========== ; This entry demonstrates how to avoid the pitfall of ; forgetting to create a Struct before passing a reference to that ; Struct to another entry. entry STRUCT_PARAMS_PITFALL_DO params struct pStruct : IN ; Solution #2: Use INOUT parameters ; struct pStruct : INOUT endparams ; Add a member to the Struct (which is implicitly created if it doesn't exist) pStruct->aValue = "3333" end ;- entry STRUCT_PARAMS_PITFALL_DO ; ======================================================================= ; IDENTICAL_MEMBER_NAMES ; ======================================================================= entry IDENTICAL_MEMBER_NAMES ; This entry demonstrates that a Struct may contain multiple ; members with the same name. This makes it possible for it to ; reflect XML documents, which allow multiple elements with the same name. variables struct vStruct numeric i endvariables ; Display entry header in the message frame call printHeader("IDENTICAL_MEMBER_NAMES") ; Build a Struct with 3 members named 'a' vStruct->a = "A1" vStruct->a{2} = "A2" ; Update/add item at position 2 vStruct->a{-1} = "A3" ; Add item at the end ; vStruct->a returns a collection of 3 references: putmess "$collSize of vStruct->a is %%(vStruct->a->$collSize)%%%" putmess vStruct->$dbgstring ; Result: ; $collSize of vStruct->a is 3 ; [] ; [a] = "A1" ; [a] = "A2" ; [a] = "A3" ; For more information on dealing with collections, ; see the example STRUCT_COLLECTIONS() end ;- entry IDENTICAL_MEMBER_NAMES ; ======================================================================= ; MEMBER_REFERENCES ; ======================================================================= entry MEMBER_REFERENCES ; This example demonstrates how you can maintain a reference to ; original Struct members, even when they have been assigned a new value. variables struct vStruct1, vStruct2 string string1 endvariables ; Display entry header in the message frame call printHeader("MEMBER_REFERENCES") ; Multiple members ; Build a Struct in which multiple members have the same name, ; but different values: vStruct1->a = "A1" vStruct1->a{2} = "A2" vStruct1->a{3} = "A3" ; Save a reference to the collection of these members: vStruct2 = vStruct1->a ; Reference to members 'a' ; Update the complete member set: vStruct1->a = "A-updated" putmess "Updated vStruct1: " putmess vStruct1->$dbgString ; Result: ; [] ; [a] = "A-updated" ; However, vStruct2 still points to the original collection of 3 members: putmess "vStruct2 has not been updated:" putmess vStruct2->$dbgstring ; Result: ; [a] = "A1" ; [a] = "A2" ; [a] = "A3" ; Note: By using a reference to a collection, you can continue ; to access the original data. In example STRUCT_COLLECTIONS ; this reference technique is used to restore a Struct to ; its previous state. ; Single member nodes ; Build a Struct with one member: vStruct1->a = "A" ; Save a reference to the member vStruct2 = vStruct1->a ; Update the original Struct member vStruct1->a = "A-updated" ; This actually creates a new member that overwrites the existing member. ; (The new member get the tags the original member had; see TAGS_INHERITANCE) putmess "vStruct1 has been updated:" putmess vStruct1->a->$dbgstring putmess "vStruct2 points to the original member:" putmess vStruct2->$dbgstring ; Result: ; vStruct1->a has been updated: ; [a] = "A-updated" ; ; vStruct2 points to the original member: ; [a] = "A" end ;- entry MEMBER_REFERENCES ; ======================================================================= ; STRUCT_COLLECTIONS ; ======================================================================= entry STRUCT_COLLECTIONS ; This entry demonstrates that a struct variable is always ; a reference to a *collection* of structs, and shows how ; you can work with collections. ; Note: A collection of one looks and works like a ; reference to a single Struct, but it remains a ; collection for which $collSize returns 1. variables struct vStruct1, vStruct2, vSomeStructs, vOriginalCollection numeric i string vDescr endvariables ; Display entry header in the message frame call printHeader("STRUCT_COLLECTIONS") ; Prepare two structs vStruct1->$name = "Struct1" vStruct2->$name = "Struct2" ; Assign a member to the collection 'a', ; overwriting any existing members named 'a' vStruct1->a = "A1" vStruct1->a{2} = "A2" ; Add a member at position 2 vStruct1->a{-1} = "A3" ; Append new member at end vStruct1->b = "B" putmess vStruct1->$dbgstring ; Result: ; [Struct1] ; [a] = "A1" ; [a] = "A2" ; [a] = "A3" ; [b] = "B" ; A struct variable is a reference to any number of Structs. ; Thus this statement: structVar = aStruct->* ; assigns the references to all children of 'aStruct' ; to structVar. ; The collection size of vStruct1->a (=3) and vStruct1->* (=4 all children) putmess "The collection size of vStruct1->a is %%(vStruct1->a->$collsize)%%%" ; Use the collection operator ->* to get all references to all children of a Struct putmess "The collection size of vStruct1->* is %%(vStruct1->*->$collsize)%%%" ; Result: The collection size of vStruct1->a is 3 ; The collection size of vStruct1->* is 4 ; Looping over the collection vStruct1->a: putmess "%%^Loop over all members of vStruct1->a:" i = 1 while (i <= vStruct1->a->$collSize) putmess " vStruct1->a{%%i%%%} has value %%(vStruct1->a{i})%%%" i = i + 1 endwhile ; Result: ; Loop over all members of vStruct1->a: ; vStruct1->a{1} has value A1 ; vStruct1->a{2} has value A2 ; vStruct1->a{3} has value A3 ; Before reassigning Structs, save a reference to the original vOriginalCollection = vStruct1->a ; Keep reference to original collection ; Update one specific member: vStruct1->a{1} = "A1 - updated" putmess "%%^Updated vStruct1->a{%%1%%%}: %%(vStruct1->a{1})%%%" ; Result: Updated vStruct1->a{1}: A1 - updated ; A collection of multiple Structs has no value: putmess "%%^A collection of multiple Structs always returns an empty value" putmess " vStruct1->a = %%(vStruct1->a)%%%" ; Result: A collection of multiple Structs always returns an empty value ; vStruct1->a = ; A collection with only one Struct behaves like a single Struct: vStruct1->a = "A1 - collection reassigned" putmess "However, if the collection contains a single Struct," putmess " it is treated as a single Struct:" putmess " vStruct1->a = %%(vStruct1->a)%%%" putmess " vStruct1->a{1} = %%(vStruct1->a{1})%%%" ; Result: ; However, if the collection contains a single Struct, ; it is treated as a single Struct: ; vStruct1->a = A1 - collection reassigned ; vStruct1->a{1} = A1 - collection reassigned ; Restore the original collection vStruct1->a = vOriginalCollection ; ------------------------------------- ; Struct functions and assignments on vStruct1->a can take place ; on all Structs in a collection. $dbgstring is a good example: ; vStruct1->*->$dbgstring prints the collection of all children. ; ; Some other struct functions are meaningfull only under conditions: ; putmess "%%^Using Struct functions on collections:" putmess " All Structs vStruct1->a share the same name: [%%(vStruct1->a->$name)%%%]" reset $procerror putmess " but not all vStruct1->* : [%%(vStruct1->*->$name)%%%]" vDescr = $item("DESCRIPTION", $procerrorcontext) putmess " ProcScript error: %%$procerror%%%: %%vDescr%%%" putmess " All Structs vStruct1->* share their parent : [%%(vStruct1->*->$parent->$name)]" vSomeStructs{1} = vStruct1->* vSomeStructs{-1} = vStruct1 reset $procerror putmess " but not all Structs in any collection : [%%(vSomeStructs->$parent)]" vDescr = $item("DESCRIPTION", $procerrorcontext) putmess " ProcScript error: %%$procerror%%%: %%vDescr%%%" ; Result: ; Using struct functions on collections: ; All structs vStruct1->a share the same name: [a] ; but not all vStruct1->*->$name : [] ; ProcScript error: -1151: Structs do not have a common name or parent ; All structs vStruct1->* share their parent : [Struct1] ; but not all structs in any collection : [] ; ProcScript error: -1151: Structs do not have a common name or parent ; Manipulating collections using Struct functions: ; one line of code to move all members a from vStruct1 to vStruct2: putmess "%%^Manipulating structs in collections: Assign $parent:" vStruct1->a->$parent = vStruct2 putmess " The members 'a' have moved to vStruct2:" putmess vStruct1->$dbgstring putmess vStruct2->$dbgstring ; Result: ; Manipulating structs in collections, assign $parent: ; The members 'a' have moved to vStruct2: ; [vStruct1] ; [b] = "B" ; ; [vStruct2] ; [a] = "A1 - updated" ; [a] = "A2" ; [a] = "A3" putmess "%%^Manipulating Structs in collections: Assign $name:" putmess " Members 'a' renamed to 'AAA':" vStruct2->a->$name = "AAA" putmess vStruct2->$dbgstring ; Result: ; Manipulating Structs in collections: Assign $name: ; Members 'a' renamed to 'AAA': ; [vStruct2] ; [AAA] = "A1 - updated" ; [AAA] = "A2" ; [AAA] = "A3" ; ------------------------------------- ; Assign a subnode to each 'a' member: vStruct2->*->x = "xyz" putmess "%%^Member 'x' assigned to all Structs in a collection:" putmess vStruct2->$dbgstring ; Result: ; Member 'x' assigned to all Structs in a collection: ; [vStruct2] ; [AAA] ; "A1 - updated" ; [x] = "xyz" ; [AAA] ; "A2" ; [x] = "xyz" ; [AAA] ; "A3" ; [x] = "xyz" end ;- entry STRUCT_COLLECTIONS ; ======================================================================= ; STRUCT_LOOPS ; ======================================================================= entry STRUCT_LOOPS ; This example shows how to loop over a Struct to perform actions ; on the various levels. The example calls two other entries: ; REMOVE_TAGS simple loop that removes the tags on all (sub)structs ; PRINT_STRUCT slightly more complex loop that prints a ; representation of the Struct to the message frame ; that is similar to $dbgstring variables struct vStruct endvariables ; Display entry header in the message frame call printHeader("STRUCT_LOOPS") ; Create a Struct: vStruct ->$name = "Demo Struct" vStruct ->$tags->purpose = "sample" vStruct ->description = "Sample Struct" vStruct ->subNode = $newstruct vStruct ->subNode->$tags->purpose = "sample too" vStruct ->subNode->a = "AAA" vStruct ->subNode->b = "BBB" ; Print the Struct putmess "Print the whole Struct:" call PRINT_STRUCT(vStruct , " ") ; Result ; Print the whole Struct: ; [Demo Struct] ; [$tags] ; [purpose] = "sample" ; [description] = "Sample Struct" ; [subNode] ; [$tags] ; [purpose] = "sample too" ; [a] = "AAA" ; [b] = "BBB" ; Remove the tags from all levels of the Struct call REMOVE_TAGS(vStruct ) putmess "%%^After removing tags from the Struct:" ; Pass a collection of children : putmess " Print a collection of the children of the Struct:" call PRINT_STRUCT(vStruct ->*, " ") ; Result ; After removing tags from the Struct: ; Print a collection of the children of the Struct: ; [description] = "Sample Struct" ; [subNode] ; [a] = "AAA" ; [b] = "BBB" end ; entry STRUCT_LOOPS ; ======================================================================= ; REMOVE_TAGS ; ======================================================================= entry REMOVE_TAGS ; This entry loops over a Struct collection, recursively stripping ; tags from the Struct and all its children. ; The entry body strips tags from the Struct that was passed in, but ; not from its children. To deal with the child level, the entry ; calls itself recursively. ; When a collection is passed in, the entry calls itself for each ; Struct in the collection. The action is applied to individual ; Structs only (or, more precise: on collections with $collsize = 1) params struct pStruct: in ; A reference can refer to one or more Structs endparams variables numeric i, N endvariables if (pStruct->$collSize > 1) ; Multiple Structs in the collection i = 1 N = pStruct->$collSize while (i <= N) ; Call this entry recursively for each Struct in the collection call REMOVE_TAGS(pStruct{i}) i = i + 1 endwhile else ; Single Struct, so strip tags: pStruct->$tags->*->$parent = "" ; Recursively call this entry for all children of the current Struct. ; This is a single call, passing all children to the next level: if (pStruct->$membercount > 0) call REMOVE_TAGS(pStruct->*) endif end ;- entry REMOVE_TAGS ; ======================================================================= ; PRINT_STRUCT ; ======================================================================= entry PRINT_STRUCT ; The structure of this entry is similar to REMOVE_TAGS, ; but it prints a representation of the Struct to the message frame ; that is similar to $dbgstring. ; Note: $dbgstring is slightly richer in functionality and more ; efficient than this ProcScript implementation, but this example ; shows how you can write a custom print routine for Structs. ; The output of $dbgstring may change over Uniface versions, ; but a custom implementation enables you to determine ; a specific format yourself. params struct pStruct: in ; A reference can refer one or more structs string pMargin: in ; Initial margin + indenting for deeper levels endparams variables numeric i, N endvariables #comment using a define for double quotes to improve readability: #define DQ %%"%%% if (pStruct->$collSize > 1) ; Multiple structs in the collection i = 1 N = pStruct->$collSize while (i <= N) ; Call this entry recursively for each Struct in the collection call PRINT_STRUCT(pStruct{i}, pMargin) i = i + 1 endwhile else ; Single struct, so start printing: if (pStruct->$isTags) putmess $concat(pMargin, "[$tags]", pStruct) ; Use label '$tags' elseif (pStruct->$isLeaf) if (pStruct->$isScalar) ; Leaf is already printed on preceding level: if (!pStruct->$parent->$isLeaf) ; Scalar, so print: value putmess $concat(pMargin, "<DQ>%%pStruct%%%<DQ>") endif else ; Leaf, so print: [name] = value putmess $concat( %\ pMargin, "[", pStruct->$name, "] = <DQ>%%pStruct%%%<DQ>") endif else ; Complex Struct, so print: [name] putmess $concat(pMargin, "[", pStruct->$name, "]") endif ; Print tags, if applicable: if (pStruct->$tags->$memberCount > 0) call PRINT_STRUCT(pStruct->$tags, $concat(pMargin, " ")) endif ; End printing ; Recursively call this entry for all children of the current Struct. ; This is a single call, passing all children to the next level: if (pStruct->$membercount > 0) call PRINT_STRUCT(pStruct->*, $concat(pMargin, " ")) endif endif end ;- entry PRINT_STRUCT ; ======================================================================= ; REMOVE_STRUCT_LEVEL ; ======================================================================= ; This entry shows how to remove a level from a Struct ; (the reverse of the INSERT_STRUCT_LEVEL) entry REMOVE_STRUCT_LEVEL variables struct vStruct endvariables ; Display entry header in the message frame call printHeader("REMOVE_STRUCT_LEVEL") ; Build a struct vStruct->levelA = $newstruct vStruct->levelA->levelB = $newstruct vStruct->levelA->levelB->childX = "xx" vStruct->levelA->levelB->childY = "yy" vStruct->levelA->levelB->childComplex = $newstruct vStruct->levelA->levelB->childComplex->childZ = "zz" putmess "Original Struct:" putmess vStruct->$dbgstring ; Result: ; Original Struct: ; [] ; [levelA] ; [levelB] ; [childX] = "xx" ; [childY] = "yy" ; [childComplex] ; [childZ] = "zz" ; To remove level B, so that all its children become children of levelA. ; 1. Assign 'vStruct1->levelA' as parent of levelB's children: ; 2. Reset the parent of 'vStruct1->levelA->levelB' vStruct->levelA->levelB->*->$parent = vStruct->levelA vStruct->levelA->levelB->$parent = "" putmess "Struct with LevelB removed:" putmess vStruct->$dbgstring ; Result: ; Struct with LevelB removed: ; [] ; [levelA] ; [childX] = "xx" ; [childY] = "yy" ; [childComplex] ; [childZ] = "zz" ; Note: LevelB is no longer part of a containing struct, ; and no struct variables refer to levelB, ; so the Struct is actually deleted: it is removed from memory. end ;- entry REMOVE_STRUCT_LEVEL ; ======================================================================= ; INSERT_STRUCT_LEVEL ; ======================================================================= ; This entry shows how to insert an additional level into a Struct ; (the reverse of REMOVE_STRUCT_LEVEL)) entry INSERT_STRUCT_LEVEL variables struct vStruct, vNewLevel endvariables ; Display entry header in the message frame call printHeader("INSERT_STRUCT_LEVEL") ; Build a Struct vStruct->levelA = $newstruct vStruct->levelA->childX = "xx" vStruct->levelA->childY = "yy" vStruct->levelA->childComplex = $newstruct vStruct->levelA->childComplex->childZ = "zz" putmess "Original Struct" putmess vStruct->$dbgstring ; Result: ; [] ; [levelA] ; [childX] = "xx" ; [childY] = "yy" ; [childComplex] ; [childZ] = "zz" ; Insert a new Struct level between 'vStruct1->levelA' ; and its children vNewLevel = $newstruct vStruct->levelA->*->$parent = vNewLevel vStruct->levelA->levelB = vNewLevel putmess "Struct with new LevelB" putmess vStruct->$dbgstring ; Result: ; [] ; [levelA] ; [levelB] ; [childX] = "xx" ; [childY] = "yy" ; [childComplex] ; [childZ] = "zz" end ;- entry INSERT_STRUCT_LEVEL ; ======================================================================= ; STRUCT_CONVERSIONS ; ======================================================================= entry STRUCT_CONVERSIONS ; This entry uses Struct manipulation to transform incoming XML ; data into Uniface entities so that it can be updated, (e.g. in code, ; by users, DB I/O, etc.), and transform it back to the original ; XML format. ; To run this example, you need to paint the following entities ; in your test component: ; * Outer entity: BOOKS.MODELX, with the fields: TITLE, CATEGORY, DESCRIPTION ; * Inner entity: AUTHOR.MODELX, with the field : NAME variables struct vStruct, vOutStruct string vOutXML numeric i string vWarning endvariables ; Display entry header in the message frame call printHeader("STRUCT_CONVERSIONS") putmess "The starting point is the following XML sample:%%^%%%" ; $samplexml is defined as blockdata at the end of this entry putmess $samplexml xmlToStruct vStruct, $samplexml ; Result: ; The starting point is the following XML sample: ; ; <?xml version="1.0"?> ; <catalog> ; <book id="ref2451"> ; <author>Smith, John</author> ; <title>My Road</title> ; <genre>Biography</genre> ; <publisher>ABC books</publisher> ; <publication_date>2010-07-21</publication_date> ; <description>Autobiography by John Smith</description> ; <price>22.90</price> ; </book> ; <book id="ref7836"> ; <author>Black, E.J.</author> ; <title>Summer recipes</title> ; <genre>Cookery</genre> ; <publisher>Black Bee Publishers</publisher> ; <publication_date>2010-08-12</publication_date> ; <description>Recipes by top chef E.J. Black</description> ; <price>15.00</price> ; </book> ; </catalog> ; The XML tags can be relevant when converting back to XML, ; but are distracting when debugging the Struct. ; To display the Struct without tags, use $dbgstringplain. putmess "Original struct, as converted from XML (without tags):" putmess vStruct->$dbgstringplain ; Alternatively, ; 1. You could use the following code to remove tags: ;vStruct->$tags->*->$parent = "" ; collection update: all vStruct->$tags->* ;vStruct->*->$tags->*->$parent = "" ; collecion in collection... ;vStruct->*->*->$tags->*->$parent = "" ; etc. ;vStruct->*->*->*->$tags->*->$parent = "" ; 2. Or you could call the REMOVE_TAGS() entry ;call REMOVE_TAGS(vStruct) ; Result: ; Original struct, as converted from XML (all $tags reset): ; [] ; [catalog] ; [book] ; [id] = "ref2451" ; [author] = "Smith, John" ; [title] = "My Road" ; [genre] = "Biography" ; [publisher] = "ABC books" ; [publication_date] = "2010-07-21" ; [description] = "Autobiography by John Smith" ; [price] = "22.90" ; [book] ; [id] = "ref7836" ; [author] = "Black, E.J." ; [title] = "Summer recipes" ; [genre] = "Cookery" ; [publisher] = "Black Bee Publishers" ; [publication_date] = "2010-08-12" ; [description] = "Recipes by top chef E.J. Black" ; [price] = "15.00" ; Transform the Struct to match the required Uniface component structure ; Note: The XML includes elements that are not required in the data ; structure of the component, such as publisher and price. ; You can choose to remove the members that correspond to ; these elements, but in this example, they are left in the Struct. ; This will generate warnings in $procreturncontext ; 1. Change the structure for the outer entity ; FROM: vStruct->catalog->book TO: vStruct->catalog->BOOKS->OCC ; - Add one more level BOOKS vStruct->catalog->BOOKS = $newstruct ; - Put all 'book' Structs in it vStruct->catalog->book->$parent = vStruct->catalog->BOOKS ; - Rename the 'book Structs to "OCC" vStruct->catalog->BOOKS->book->$name = "OCC" ; 2. Rename fields to match those in BOOKS, where necessary vStruct->catalog->BOOKS->OCC->genre->$name = "CATEGORY" ; 3. Change the structure for the inner entity ; - Add a new level for the inner entity AUTHOR vStruct->catalog->BOOKS->OCC->AUTHOR = $newstruct vStruct->catalog->BOOKS->OCC->AUTHOR->OCC = $newstruct ; - For each BOOKS occurrence, move the author to this new Struct: i = 1 while (i <= vStruct->catalog->BOOKS->OCC->$collsize) vStruct->catalog->BOOKS->OCC{i}->author->$parent = %\ vStruct->catalog->BOOKS->OCC{i}->AUTHOR->OCC i = i + 1 endwhile ; - Rename the XML fields to match those in the entity AUTHOR vStruct->catalog->BOOKS->OCC->AUTHOR->OCC->author->$name = "NAME" putmess "Struct modified to match component structure:" putmess vStruct->$dbgstringplain ; Result: ; Struct modified to match component structure: ; [] ; [catalog] ; [BOOKS] ; [OCC] ; [id] = "ref2451" ; [title] = "My Road" ; [CATEGORY] = "Biography" ; [publisher] = "ABC books" ; [publication_date] = "2010-07-21" ; [description] = "Autobiography by John Smith" ; [price] = "22.90" ; [AUTHOR] ; [OCC] ; [NAME] = "Smith, John" ; [OCC] ; [id] = "ref7836" ; [title] = "Summer recipes" ; [CATEGORY] = "Cookery" ; [publisher] = "Black Bee Publishers" ; [publication_date] = "2010-08-12" ; [description] = "Recipes by top chef E.J. Black" ; [price] = "15.00" ; [AUTHOR] ; [OCC] ; [NAME] = "Black, E.J." ; At this point, the component entities and fields become important, ; because the Struct data needs to be inserted into the component. structToComponent vStruct->catalog->BOOKS ; Warnings are generated for entities and fields not found in the component: if ($procerror < 0) putmess "Failed to execute 'structToComponent': ProcScript error:%%$procerror%%%" putmess $procerrorcontext else ;Warnings are generated for unrecognized fields: if ($item("WARNINGS", $procreturncontext) > 0) putmess "Warnings after execution of 'structToComponent':" i = 1 while (i <= $item("WARNINGS", $procreturncontext)) vWarning = $itemnr(i, $item("DETAILS", $procreturncontext)) putmess " Warning %%i%%%:" while (vWarning != "") putmess " %%$itemnr(1, vWarning)%%%" delitem vWarning, 1 endwhile i = i + 1 endwhile endif endif ; Result: ; Warnings after execution of 'structToComponent': ; Warning 1: ; SEVERITY=Warning ; ID=-1161 ; MNEM=<USTRUCTERR_NO_MATCHING_NAME> ; DESCRIPTION=No matching name found during conversion from struct ; CURRENTSTRUCT=BOOKS->OCC{1}->id{1} ; ADDITIONAL=SPECIFIEDNAME=id•;EXPECTEDTYPE=entity or field ; Warning 2: ; ...etc... ; At this point regular Uniface processing may take place, ; This can be ProcScript processing, or a user that modifies data ; in the form. After that: ; Once any changes have been made, the data can be converted back componentToStruct vOutStruct ; Once again, to restrict the output in the message frame, ; you can use $dbgstringplain or remove the tags: ; call REMOVE_TAGS(vOutStruct) putmess "%%^%%%" putmess "Struct after conversion from component:" putmess "(This requires that the required entities/fields are present in the component)" putmess vOutStruct->$dbgstringplain ; Result: ; [STRUCTSAMPLES] ; [BOOKS.MODELX] ; [OCC] ; [TITLE] = "My Road" ; [CATEGORY] = "Biography" ; [DESCRIPTION] = "Autobiography by John Smith" ; [AUTHOR.MODELX] ; [OCC] ; [NAME] = "Smith, John" ; [OCC] ; [TITLE] = "Summer recipes" ; [CATEGORY] = "Cookery" ; [DESCRIPTION] = "Recipes by top chef E.J. Black" ; [AUTHOR.MODELX] ; [OCC] ; [NAME] = "Black, E.J." ; Reconstruct the original XML format: ; Move the author details from the AUTHOR level up to the BOOK level: ; 1. Rename the 'name' field (could also be done after the move) vOutStruct->BOOKS.MODELX->OCC->AUTHOR->OCC->name->$name = "author" ; 2. Move each occurrence up one level: i = 1 while (i <= vOutStruct->BOOKS.MODELX->OCC->$collsize) vOutStruct->BOOKS.MODELX->OCC{i}->AUTHOR->OCC->name->$parent = %\ vOutStruct->BOOKS.MODELX->OCC{i} i = i + 1 endwhile ; 3. Detach AUTHOR from its parent: vOutStruct->BOOKS.MODELX->OCC->AUTHOR.MODELX->$parent = "" ;- coll. upd. ; 4. Rename the field Structs: vOutStruct->BOOKS.MODELX->$name = "catalog" ;- use BOOKS for the "catalog" vOutStruct->catalog->OCC->$name = "book" ;- collection update vOutStruct->catalog->book->TITLE->$name = "title" ;- just the case vOutStruct->catalog->book->CATEGORY->$name = "genre" ;- collection update vOutStruct->catalog->book->CATEGORY->$name = "author" ;- collection update vOutStruct->catalog->book->DESCRIPTION->$name = "description" ;- the case ; 5 Add the XML declaration: vOutStruct->$tags->xmlVersion="1.0" ; 6. Make the top-level struct nameless, so that it ; will not be included as an additional level in the XML: vOutStruct->$name = "" ; Convert the Struct to XML: structToXml vOutXML, vOutStruct putmess "The XML after conversion back from the component" putmess vOutXML ; result: ; <?xml version="1.0"?> ; <catalog> ; <book> ; <title>My Road</title> ; <genre>Biography</genre> ; <description>Autobiography by John Smith</description> ; </book> ; <book> ; <title>Summer recipes</title> ; <genre>Cookery</genre> ; <description>Recipes by top chef E.J. Black</description> ; </book> ; </catalog> ; Note: Some xml elements, such as <publisher>, are now lost, ; but this was a choice. ; Assuming the component is a form, use the edit statement to see the data: ;edit samplexml:blockdata @ <?xml version="1.0"?> <catalog> <book id="ref2451"> <author>Smith, John</author> <title>My Road</title> <genre>Biography</genre> <publisher>ABC books</publisher> <publication_date>2010-07-21</publication_date> <description>Autobiography by John Smith</description> <price>22.90</price> </book> <book id="ref7836"> <author>Black, E.J.</author> <title>Summer recipes</title> <genre>Cookery</genre> <publisher>Black Bee Publishers</publisher> <publication_date>2010-08-12</publication_date> <description>Recipes by top chef E.J. Black</description> <price>15.00</price> </book> </catalog> @ end ;- entry STRUCT_CONVERSIONS