Supporting user-defined data type fields
You can define fields whose data type is user-defined. These fields are treated as alpha fields in most cases, except when ReportWriter displays them. Every time ReportWriter needs to access a user-defined field, it calls RW_USAGE_METHOD, RW_GETVAL_METHOD, or RPS_DATA_METHOD as follows:
- RW_USAGE_METHOD is called when a structure with user-defined fields is accessed by ReportWriter, either when defining new structures/files from which to read or when opening a report with one of these structures. RW_USAGE_METHOD validates usage and determines the internal data type.
- RW_GETVAL_METHOD is called when the field is being read from a data file and determines the actual value ReportWriter will use when sorting, selecting, performing calculations on the field, and so forth.
- RPS_DATA_METHOD is called when ReportWriter needs to display the field. This subroutine formats the data as necessary.
You can register your own versions of these routines to provide the desired mechanisms for displaying and using user-defined data type fields. These routines are ideal for supporting data types that are not supported directly or for supporting alternate date storage formats. (See the RW_USAGE_METHOD Examples.)
If you use these routines to access data that is larger than the stored representation, ReportWriter will access the user-defined field only as a field to print. |
RW_USAGE_METHOD
subroutine RW_USAGE_METHOD a_user_text ,a ;User text string associated with field (a80) a_user_data ,a ;User data string associated with the field (a30) a_type ,n ;Returned with the field's data type for ; internal use (d1) ; 0 No data type change. ; 1 Alphanumeric. ; 2 Decimal. ; 3 Implied-decimal. ; 4 Integer. a_size ,n ;Returned with field length (d4) a_precision ,n ;Returned with field's precision if internal ; type is implied-decimal (d2) a_class ,n ;Returned with field's class and is one of ; the following values (d1) ; 0 Neither date nor time field. ; 1-6 Date field. ; 8-9 Time field. ;These values correspond to date and time storage ; formats listed in your Repository User’s Guide.
Discussion
During file processing, ReportWriter calls this routine once for all user type data fields. The a_user_data and a_user_text strings, a_size, a_precision, and a_class are those that have been defined in the Repository for the selected field.
By default, user-defined data type fields are treated as alpha fields. If you want ReportWriter to use a different data type internally when processing the field, you can modify the distributed version of this routine.
A_type, a_size, a_precision, and a_class tell ReportWriter how to treat the field internally when it is used in a calculation, selection, or conditional or when it is used for sorting or totaling. During report execution, ReportWriter calls RW_GETVAL_METHOD to convert the actual data in the user-defined field each time it is referenced.
If this routine modifies the data type, when the field is displayed in ReportWriter, its data type will be shown as the internal data type, preceded by a “U”. For example, if the field was defined in the repository as a “U4” and this routine specifies its internal type to be decimal with a size of 4, it will be shown as “UD4”.
This routine is always passed a type value of 0. The version of this subroutine linked with the ReportWriter in your original distribution returns the a_type value unchanged, meaning that the user-defined field will be treated as alpha. The default version of this subroutine also returns the a_size, a_precision, and a_class unchanged.
The following example supports a user-defined data type field for dates stored as MMDDYY(YY).
; Filename: usr.dbl ; ; Function: Defines user-overloadable subroutines for ; ReportWriter ; subroutine rps_data_method ; ; Description: Formats user-defined data type fields ; ; Arguments: ; a_source ,a ;Source field data (a99) a_dest ,a ;RETURNED - Modified field data (a99) a_use ,n ;ReportWriter's use of data (see Notes) (d1) a_usrtxt ,a ;User text string associated with data (a80) a_usrdata ,a ;User data string associated with data (a30) a_format ,a ;Format string associated with field (a40) ; ; Notes: ; ReportWriter calls this routine every time a user-defined ; field is displayed. See the Repository User’s Guide for more ; information on user-defined fields. ; ; a_use: When a_use is 0, the item is being output (printer, ; screen, or file). When a_use is not 0, the item is ; being used to format an input window. ; .include "WND:tools.def" record year2 ,a2 ;2-digit year year4 ,a4 ;4-digit year year ,a4 ;Temporary storage for date (year) month ,a2 ;Temporary storage for date (month) day ,a2 ;Temporary storage for date (day) date ,a10 ;Temporary storage for date (entire) r_date ,a10 ;Date returned from u_fmtdat proc if (a_use) ;Format only for output xreturn if ^passed(a_format) then begin case (a_format) of begincase "MM-DD-YY ": call format1 "MM/DD/YYYY ": call format2 endcase else a_dest = a_source ;Default behavior of rps_data_method end else a_dest = a_source xreturn format1, date = ^d(a_source(1:6)), "XXXXXX" xcall u_fmtdat(3, r_date, ^d(date),,, D_LEFT, 0, '-') if (r_date(3:1).ne.'-') then a_dest = "0" + r_date else a_dest = r_date return format2, date = ^d(a_source(1:8)), "XXXXXXXX" xcall u_fmtdat(6, r_date, ^d(date),,, D_LEFT, 0, '/') if (r_date(3:1).ne.'/') then a_dest = "0" + r_date else a_dest = r_date return endsubroutine subroutine rw_usage_method ; ; Description: Determines user-defined data type usage ; ; Arguments: ; a_usrtxt ,a ;User text string associated with data (a80) a_usrdata ,a ;User data string associated with data (a30) a_type ,n ;RETURNED - field's data type for internal ; usage (d1) (see Notes) a_size ,n ;RETURNED - field's length (d4) a_precision ,n ;RETURNED - Precision if internal type is ; implied-decimal (d2) a_class ,n ;RETURNED Field class (see Notes) (d1) ; ;Notes: ; ReportWriter calls this routine every time a user-defined ; field is selected in a report. See the Repository User’s Guide ; for more information on user-defined fields. ; proc upcase a_usrdata case (a_usrdata) of begincase "MMDDYY ": call usage1 "MMDDYYYY": call usage2 endcase else nop ;Default behavior of rw_getval_method xreturn usage1, a_type = 2 ;Decimal a_size = 6 a_precision = 0 a_class = 1 ;YYMMDD return usage2, a_type = 2 ;Decimal a_size = 8 a_precision = 0 a_class = 2 ;YYYYMMDD return endsubroutine subroutine rw_getval_method ; ;Description: Returns user-defined data type value ; ;Arguments: ; a_source ,a ;Source field data (a99) a_dest ,a ;RETURNED - Modified field data (a99) a_usrtxt ,a ;User text string associated with data (a80) a_usrdata ,a ;User data string associated with data (a30) a_type ,n ;Internal data type for the field (d1) a_size ,n ;Internal size of the field (d4) a_precision ,n ;Internal precision of the field if ; implied-decimal (d2) ;Notes: ; ReportWriter calls this routine during report generation ; for each user-defined data type field in each data record. ; ; This routine converts actual file data for user-defined fields ; before ReportWriter uses them for calculating, selecting, ; sorting, totaling, or defining a conditional. ; ; The values for each parameter are those returned from the ; RW_USAGE_METHOD routine when the user-defined field was selected. ; record syn_date1 ,d6 ;YYMMDD - Synergy/DE date format #1 s_year1 ,d2 @syn_date1 s_month1 ,d2 @syn_date1 + 2 s_day1 ,d2 @syn_date1 + 4 record syn_date2 ,d8 ;YYYYMMDD - Synergy/DE date format #2 s_year2 ,d4 @syn_date2 s_month2 ,d2 @syn_date2 + 4 s_day2 ,d2 @syn_date2 + 6 record my_date1 ,d6 ;MMDDYY - User-defined date format #1 m_month1 ,d2 @my_date1 m_day1 ,d2 @my_date1 + 2 m_year1 ,d2 @my_date1 + 4 record my_date2 ,d8 ;MMDDYYYY - User-defined date format #2 m_month2 ,d2 @my_date2 m_day2 ,d2 @my_date2 + 2 m_year2 ,d4 @my_date2 + 4 record date_out ,d8 ;Output for final date returned from dyadd date_in ,a10 ;Temporary storage for date transfer proc upcase a_usrdata case (a_usrdata) of begincase "MMDDYY ": call getval1 "MMDDYYYY": call getval2 endcase else a_dest = a_source ;Default behavior of RW_GETVAL_METHOD xreturn getval1, date_in = ^d(a_source(1,6)), "XXXXXX" my_date1 = ^d(date_in) s_year1 = m_year1 ;Transfer date information s_month1 = m_month1 s_day1 = m_day1 a_dest = syn_date1, "XXXXXX" return getval2, date_in = ^d(a_source(1,8)), "XXXXXXXX" my_date2 = ^d(date_in) s_year2 = m_year2 ;Transfer date information s_month2 = m_month2 s_day2 = m_day2 a_dest = syn_date2, "XXXXXXXX" return endsubroutine
RW_GETVAL_METHOD
subroutine RW_GETVAL_METHOD a_source ,a ;Source field data a_dest ,a ;Returned with modified field data a_user_text ,a ;User text string associated with field (a80) a_user_data ,a ;User data string associated with field (a30) a_type ,n ;Internal data type for field (d1) a_size ,n ;Internal size of field (d4) a_precision ,n ;Internal precision of field if internal type ; is implied-decimal (d2)
Discussion
During report generation, ReportWriter calls this routine once for each record for each user-defined data type field selected in the report. The a_user_data and a_user_text strings and a_type, a_size, and a_precision are values that were returned from the RW_USAGE_METHOD routine when the user-defined field was selected.
You can use this routine to convert the actual file data for user-defined fields before ReportWriter uses them in a calculation, selection, or conditional or for sorting or totaling. The data value is retrieved as an alpha field with the contents in user storage format. RW_USAGE_METHOD converts this value to an alpha field that contains the Synergy DBL storage format for the type to which you are converting.
Avoid explicit casting (using the ^D or ^A functions or alpha = decimal format) in creating the field’s new type, as you may lose the sign of a decimal field by performing these conversions. Instead, use a decimal field overlaid with an alpha field, as follows, to convert the field to alpha format implicitly.
record decimal ,d28 dec_alpha ,a28 @decimal
ReportWriter does not support changing data size using these routines. In this situation, you should convert to a decimal or alpha field of the same size. The version of this subroutine linked with ReportWriter in your original distribution returns the original data, unmodified, in a_dest. |
Note the following restrictions:
- If a user-defined field is a key segment in the primary file, optimizations in selection criteria and sorting use the storage format defined in the file, instead of the value returned from RW_GETVAL_METHOD. If you want to perform selection criteria on a user-defined field that is a key segment in the primary file and use the values as returned from RW_GETVAL_METHOD, you must create a temporary field that has that field as its expression. If a user-defined field is a segment of the primary key of the primary file, the default report is sorted on the storage format in the file of the user-defined field. The same is true if you depend on ReportWriter optimizations for sorting. For this reason, we do not recommend using user-defined fields in key segments.
- If two user-defined fields are overlaid, RW_GETVAL_METHOD is called for both fields. If the first field (as defined in the Repository) is reordered by RW_GETVAL_METHOD, the second field will be affected.
- If a user-defined field is used as a segment in a foreign key, any relations on that field use the value as returned from RW_GETVAL_METHOD.
See the RW_USAGE_METHOD Examples.
RPS_DATA_METHOD
subroutine RPS_DATA_METHOD a_source ,a ;Source field data (a99) a_dest ,a ;Returned with modified field data (a99) a_use ,n ;Indicates ReportWriter's use for the data (d1) ; 0 Output (screen, printer, or file). ; 1 Input window. a_user_text ,a ;User text string associated with the field (a80) a_user_data ,a ;User data string associated with the field (a30) a_format ,a ;(optional) Format string associated with field (a40)
Discussion
ReportWriter calls RPS_DATA_METHOD every time a user-defined data type field is displayed. (However, this routine is not called when the user-defined data type field is used as a sort field.) RPS_DATA_METHOD converts the storage format created by RW_GETVAL_METHOD into the format you want ReportWriter to display. Note that a format must be provided when you select a user-defined data type to print, and it must have at least the number of characters you wish to display within the format.
If a_use contains a value of 0, a_source is a field being printed on a detail line or break line. If a_use is nonzero, a_source is the input in a question field or a selection criteria comparison value. The user text string, user data string, and format string can contain whatever data is required to process the field.
Date and time fields do not automatically use the ReportWriter (or Repository) formats to display dates and times; you must perform this conversion yourself. This display conversion can also be performed using UI Toolkit U_FMTDAT and U_FMTTIM subroutines.
Remember to access implied-decimal fields as such before formatting, using the ^D function:
^d(a_source, precision)
The version of this subroutine linked with the ReportWriter in your original distribution returns the original data, unmodified, in a_dest.
Examples
Here’s a sample RPS_DATA_METHOD subroutine that formats the specified user-defined data type field based on the specified format. Also see the RW_USAGE_METHOD Examples.
subroutine rps_data_method ; ; Description: This subroutine formats the given user-defined data ; type field based on a format string stored in the ; field's user text area. Otherwise, it uses ; the format string passed. ; Arguments: ; a_srcdat ,a ;Input source data string a_dstdat ,a ;Output result data string a_use ,a ;Use for user-defined field a_usrtxt ,a ;User text string associated with source field a_dattyp ,a ;Data type string associated with source field a_fmtstr ,a ;Optional format string ; This example assumes the size of a_dstdat is big enough to load the ; extra format characters. The destination size can be controlled by a ; dummy format string size. record ix ,d3 t_dec ,d18 t_date1 ,d8 @t_dec t_date2 ,d6 @t_dec proc if (a_use) ;Only format for output xreturn upcase a_usrtxt case a_usrtxt of begincase "USER DATE #1": begin t_date1 = %d(a_srcdat) ;Destination source length must be case a_usrtxt(16:3) of ; enough begincase "--": xcall s_bld(a_dstdat,,"%4d%a%2a%a%2a", & t_date1(1:4), a_usrtxt(16:3), & t_date1(5:2), a_usrtxt(16:3), & t_date1(7:2)) "***": xcall s_bld(a_dstdat,, "%a%4d%a%2a%a%2a%a", & a_usrtxt(16:3), t_date1(1:4), & a_usrtxt(16:3), t_date1(5:2), & a_usrtxt(16:3), t_date1(7:2), & a_usrtxt(16:3)) endcase end "USER DATE #2": begin t_date2 = %d(a_srcdat) ;Destination source length must be case a_usrtxt(16:3) of ; enough begincase "--": xcall s_bld(a_dstdat,,"%4d%a%2a%a%2a", & t_date2(1:4), a_usrtxt(16:3), & t_date2(5:2), a_usrtxt(16:3), & t_date2(7:2)) "***": xcall s_bld(a_dstdat,, "%a%4d%a%2a%a%2a%a", & a_usrtxt(16:3), t_date2(1:4), & a_usrtxt(16:3), t_date2(5:2), & a_usrtxt(16:3), t_date2(7:2), & a_usrtxt(16:3)) endcase end endcase else if (%passed(a_fmtstr)) then ;Can use format string to if (%instr(1, a_fmtstr, "&") then ; control format operation begin clear ix do ;For all blanks, replace with "*" begin incr ix if (a_srcdat(ix:1).eq." ") then a_dstdat(ix:1) = "*" else a_dstdat(ix:1) = a_srcdat(ix:1) end until (ix.eq.%len(a_srcdat)) end else a_dstdat = a_srcdat else a_dstdat = a_srcdat xreturn endsubroutine