Processing GROUP Fields

 

Understanding GROUP Fields

The GROUP statement defines a data structure within a record or another group structure. The ENDGROUP statement indicates the end of a group declaration. A group name must be unique among all other field and group names within a parent group. If a group isn’t part of another group, its name must be unique within the record. Here is an example of a record containing several groups:

 

record info

  group customer      ,[100]a

    name              ,a30

    group office      ,a

      bldg            ,a20

      group address   ,a

        street        ,a40

        zip           ,d10

      endgroup

    endgroup

    group contact     ,a

      name            ,a40

      group address   ,a

        street        ,a40

        zip           ,d10

      endgroup

    endgroup

  endgroup
endrecord

 

Overlay Groups

Group fields can also be used to define overlays. By default, CodeGen excludes overlays when processing, and that included the exclusion of overlay groups. However, if the -f o command line option is used to cause overlays to be included when processing, then overlay groups will also be included. When processing in a field loop you can detect whether an unexpanded overlay group is being processed by using the <IF GROUP_OVERLAY> expression.

 

Implicit and Explicit Groups

While DBL has only one type of group, Repository allows you to define groups in two ways. These are referred to as "explicit groups" and "implicit groups". An explicit group is a group where the group members are defined explicitly within the definition of the group, all within the same structure. An implicit group is a group where the members of the group are defined by the members of a different structure.

 

Group Member Prefix

When a field is declared as a GROUP it is possible to also provide a "member prefix", which is a value to be pre-pended to the names of all fields in the group. By default, CodeGen will use any prefix specified, but if you prefer not to use the prefix when generating code then that behavior can be suppressed by specifying the -g f command line option.

 

When processing groups, you can determine whether a group member prefix is present by using the <IF GROUP_MEMBER_PREFIX> expression, and you can access the prefix value with the <FIELD_GROUP_MEMBER_PREFIX> expansion token.

 

How CodeGen Processes Groups

Under normal circumstances, whenever CodeGen encounters a GROUP field in a structure, it expands the group into individual fields. So, if a structure contains a group that has ten member fields, then the group field is removed from the structure and ten individual fields are added in its place. And if a group contains other "nested" groups, then the same process is applied to those fields also.  For example, given the following structure:

 

structure customer

    customer_id,      a10

    company_name,     a40

    group address,    a

        street,       a30

        city,         a20

        state,        a2

        zip,          d5

    endgroup

    phone,            d10

endstructure

 

If you use the <field_name> token to access the field names in a field loop, you would see field names like this:

 

customer_id

company_name

address.street

address.city

address.state

address.zip

phone

 

If you use the <field_sqlname> token to access the field names in a field loop, you would see field names like this:

 

customer_id

company_name

address_street

address_city

address_state

address_zip

phone

 

Controlling Group Expansion

If you prefer to not have group fields expanded during code generation then you can control that behavior, for both explicit and implicit groups, via two command-line options. The -g e option prevents explicit groups from being expanded, and the -g i option prevents the expansion of implicit groups.

 

When you use these options the original group field remains in the structure, and the values of some field loop tokens is altered for those fields. When processing a field loop you can detect whether groups are or are not being expanded using expression tokens <IF EXPLICIT_GROUP_NO_EXPAND> and <IF IMPLICIT_GROUP_NO_EXPAND>, and you can detect unexpanded group fields with expressions like <IF GROUP>, <IF EXPLICIT_GROUP>, <IF IMPLICIT_GROUP> and <IF GROUP_OVERLAY>. And when processing an unexpanded implicit group, you can refer to the name of the structure that defines the members of the group via the <FIELD_GROUP_STRUCTURE> token.

 

Generating Code for Unexpanded Groups

This is a complex subject, and one for which there are likely to be many different scenarios, and different requirements, but here is one example, based on the following structure:

 

structure CUSTOMER                                            

  CUSTOMER_ID            ,D8   ; Customer ID                                   

  COMPANY                ,A40  ; Company name                                  

  group BILLING_ADDRESS  ,A    ; Billing address                               

    STREET               ,A30  ; Street address                                

    CITY                 ,A20  ; City                                          

    STATE                ,A2   ; State                                         

    ZIP                  ,D5   ; Zip code                                      

  endgroup                     ;

  group SHIPPING_ADDRESS ,A    ; Shipping address                              

    STREET               ,A30  ; Street address                                

    CITY                 ,A20  ; City                                          

    STATE                ,A2   ; State                                         

    ZIP                  ,D5   ; Zip code                                      

  endgroup                     ;

  group PRIMARY_CONTACT  ,A    ; Primary contact name                          

    FIRST_NAME           ,A15  ; First name                                    

    MIDDLE_INITIAL       ,A1   ; Middle initial                                

    LAST_NAME            ,A15  ; Last name                                     

  endgroup                     ;

  group BILLING_CONTACT  ,A    ; Billing contact                               

    FIRST_NAME           ,A15  ; First name                                    

    MIDDLE_INITIAL       ,A1   ; Middle initial                                

    LAST_NAME            ,A15  ; Last name                                     

  endgroup                     ;

  group SHIPPING_CONTACT ,A    ; shipping contact                              

    FIRST_NAME           ,A15  ; First name                                    

    MIDDLE_INITIAL       ,A1   ; Middle initial                                

    LAST_NAME            ,A15  ; Last name                                     

  endgroup                     ;

endstructure

 

In the repository, the groups BILLING_ADDRESS and SHIPPING_ADDRESS are defined as IMPLICIT groups, their fields being defined by a second structure named ADDRESS. Whereas the groups PRIMARY_CONTACT, BILLING_CONTACT and SHIPPING_CONTACT are all defined as explicit groups, i.e. their member fields are defined LOCALLY within the CUSTOMER structure

 

If we wanted to generate a Synergy .NET class to represent this structure, we might use a template like this:

 

namespace <NAMESPACE>


    public class <StructureName>


<FIELD_LOOP>

        public readwrite property <FieldSqlName>, <FIELD_SNTYPE>

</FIELD_LOOP>


    endclass


endnamespace

 

And we might use a codegen command like this:

 

codegen -t grouptest -s customer -n MyNamespace

 

And the output would be something like this:

 

namespace MyNamespace


    public class Customer


        public readwrite property CustomerId, int

        public readwrite property Company, String

        public readwrite property BillingAddressStreet, String

        public readwrite property BillingAddressCity, String

        public readwrite property BillingAddressState, String

        public readwrite property BillingAddressZip, int

        public readwrite property ShippingAddressStreet, String

        public readwrite property ShippingAddressCity, String

        public readwrite property ShippingAddressState, String

        public readwrite property ShippingAddressZip, int

        public readwrite property PrimaryContactFirstName, String

        public readwrite property PrimaryContactMiddleInitial, String

        public readwrite property PrimaryContactLastName, String

        public readwrite property BillingContactFirstName, String

        public readwrite property BillingContactMiddleInitial, String

        public readwrite property BillingContactLastName, String

        public readwrite property ShippingContactFirstName, String

        public readwrite property ShippingContactMiddleInitial, String

        public readwrite property ShippingContactLastName, String


    endclass


endnamespace

 

But what if we didn't want to expand the groups into individual properties. What happens if we add the command line options to tell CodeGen not to expand explicit and implicit groups, like this:

 

codegen -t grouptest -s customer -n MyNamespace -g e i

 

Well, we wind up with this:

 

namespace MyNamespace


    public class Customer


        public readwrite property CustomerId, int

        public readwrite property Company, String

        public readwrite property BillingAddress, @ADDRESS

        public readwrite property ShippingAddress, @ADDRESS

        public readwrite property PrimaryContact, @PRIMARY_CONTACT

        public readwrite property BillingContact, @BILLING_CONTACT

        public readwrite property ShippingContact, @SHIPPING_CONTACT


    endclass


endnamespace

 

As you can see, the group fields were not expanded, and the data type produced for the unexpanded groups by the <FIELD_SNTYPE> token became a class reference. In the case of the two implicit group fields, BILLING_ADDRESS and SHIPPING_ADDRESS, the type refers to a class defined by the name of the other structure that was used to define the group members, and in the case of the three explicit groups, the type refers to a class based on the name of the original group field.

 

This code may or may not compile, depending on whether the three other classed being referred to actually exist or not. Let's explore how we can leverage the functionality of the special <FIELD_GROUP_EXPAND> token to enhance out template:

 

namespace <NAMESPACE>

    public class <StructureName>

 

<FIELD_LOOP>

  <IF GROUP>

    <IF IMPLICIT_GROUP>

        public readwrite property <FieldSqlName>, @<FIELD_GROUP_STRUCTURE>

    <ELSE>

        public class <FieldName>

        <FIELD_GROUP_EXPAND>

        endclass

        public readwrite property <FieldSqlName>, @<FieldName>

    </IF IMPLICIT_GROUP>

  <ELSE>

        public readwrite property <FieldSqlName>, <FIELD_SNTYPE>

  </IF GROUP>

</FIELD_LOOP>

 

    endclass

 

endnamespace

 

This template is a little hard to explain, so we'll break down the most important parts of the template one at a time:

 

1.Within the field loop, notice that the first thing we do is use an <IF GROUP> expression to give us a place to do special things for unexpanded groups, and the associated <ELSE> clause provides a place for the code for regular, non-group fields. In fact the code in that <ELSE> clause is the same code that we had in our original template, so for non-group fields we will continue to produce a public read-write property.

2.In the unexpanded group section of the field loop, notice how the next thing we do is use the <IF EXPLICIT_GROUP> expression, and it's associated <ELSE> clause, to allow us to generate different code for implicit groups and explicit groups.

3.For implicit groups we generate a read-write property and type it using the <FIELD_GROUP_STRUCTURE> token. This will result in a reference to the other structure that defines the members for the group.

4.For explicit groups we do a little more; we output the code for a nested class (a class within a class, just like a group is kind of like a record within a record), named after the original explicit group field, and we also emit a public property whose type is defined by that new nested class.

5. This is where it gets complicated. Notice that the body of the nested class contains a single token called <FIELD_GROUP_EXPAND>, which is a very special token that behaves very differently to most other field loop expansion tokens. Essentially what it does is "replay" the entire body of the field loop that contains it, but for the members of the current group field. If the group field only contains fields as members, then the nested class will simply contain public properties for those fields, but if the group contains members that are other groups, then the exact same process will occur, and on, and on, until all fields have been processed.

 

So, if we process this template, using the exact same CodeGen command that we used last time, the output will look like this:

 

namespace MyNamespace

 

    public class Customer

 

        public readwrite property CustomerId, int

        public readwrite property Company, String

        public readwrite property BillingAddress, @ADDRESS

        public readwrite property ShippingAddress, @ADDRESS

 

        public class PrimaryContact

            public readwrite property FirstName, String

            public readwrite property MiddleInitial, String

            public readwrite property LastName, String

        endclass

 

        public readwrite property PrimaryContact, @PrimaryContact

 

        public class BillingContact

            public readwrite property FirstName, String

            public readwrite property MiddleInitial, String

            public readwrite property LastName, String

        endclass

 

        public readwrite property BillingContact, @BillingContact

 

        public class ShippingContact

            public readwrite property FirstName, String

            public readwrite property MiddleInitial, String

            public readwrite property LastName, String

        endclass

 

        public readwrite property ShippingContact, @ShippingContact

 

    endclass

 

endnamespace

 

As you can see, the regular fields in the structure are exposed as public properties, the implicit groups are exposed as class references to an externally defined class, presumably code-generated separately using a different template, and the explicit groups are implemented as nested classes exposed via public properties.

 

By the way, if you were to process this token without the -g e i command line options, it would produce the exact same content as the original template, because there would not be any unexpended group fields present.

 

And I should also mention that if you use the sample templates from this article, the output won't be exactly as shown here; we are still working on how to get appropriate indentation when we iterate through multiple levels of groups with a single expansion token, so for the time being the code may look a little onorganized, but that's nothing that a quick CTRL+K+D in the Visual Studio editor can't fix!

 

 

 

 

 


Copyright © 2021  Synergex International, Inc.