Statement
Options – TRIM, EXCEPT
Redefinitions
and Generic Assignment
Tables and Subscripted References
Assignment statements compute and copy data from field to field
within your program. They may be simple assignments: -
Record1.Customer-Name = 'Robert';
They may involve calculations
LineItem.Extended-Value = Part.UnitCost *
LineItem.Quantity;
Generic assignments may assign many fields at once
OutputRecord.* = InputRecord.* ;
Assignment statements have this general format: -
target
[, target]... = expression [Option];
The assignment symbol is normally “=”, but may instead be
“+=”, “-=”, “*=”, or “/=”. See notes below.
They follow these general rules
1.
Referenced fields and records must have already
been defined within the program.
2.
Multiple targets are only valid for simple
assignments.
a.
You may not specify more than one generic
target, i.e.
Record1.*, Record2,* = Record3.*
is not valid
b. You may not specify multiple targets if you are using a string expression. Thus
String1,
String2 = Field1 && Field2
is not valid.
c. All targets must have the same general type. For example, you cannot mix numeric, string, and date fields.
3. The target(s) must be compatible with the expression. Thus if the expression is a string, the target cannot be a numeric field
4. If the target is a generic reference, then the expression must be a single generic reference. For example
OutputRecord.* =
InputRecord.*
See Generic Assignments below for more information.
RTRIM, LTRIM, and TRIM. These are only valid for String expressions, or for assignments of single CHAR fields to VARCHAR
- see below.
EXCEPT, STRICT, SIMPLE. These are only valid for Generic Assignments – see below.
NOTNULL. This is only valid when the
target of the assignment is an OPTIONAL field
in a SQL record. See SQL and OPTIONAL Fields for more information.
If the
target is a field (or several fields), then the expression can be
· a constant
Record1.Customer-Name = 'Robert';
Record1.MinimumPayment = 100.00;
· a reference to another field
Record1.Customer-Name = InScreen.Customer-Name
· an expression in which a value is
calculated
Record1.MinimumPayment = Parameter.PaymentClass
* 100;
Record1.Customer-Name = Input.FamilyName && ', ' && Input.GivenName;
There are several types of expressions. The expression
type is determined from the target variable: if the target is numeric then the
expression must be numeric, and so on.
These have a numeric value. An example is
Parameter.PaymentClass * 100
Numeric
expressions may include
· Numbers (numeric constants)
· Numeric fields – fields with type DECIMAL, MONEY, BIGINT, INTEGER, SMALLINT, FLOAT, LONG
· Arithmetic operators + (Plus), -
(Minus), * (Multiply), / (Divide), and ** (to the power of)
· Parentheses, i.e. brackets, to group
operands and operators
Arithmetic expressions are evaluated according to the normal
rules of Mathematics, meaning that the expression is evaluated left to right
except that anything within (…) is evaluated first, and Multiplication and
Division is done before Addition and subtraction.
Here are some examples. With B = 2, C = 3, D = 4, and E = 5,
A = B + C * D
A is set to 14: C * D is evaluated first giving 12, this is
added to 2.
If we wanted B and C to be added first we’d put them in
brackets: this sets A to 20: -
A = (B + C) * D
It doesn’t hurt to add brackets when they’re unnecessary.
For example, A = B + (C * D) is equivalent to A = B + C * D, but it may be
clearer to a human reader.
While you can make arithmetic expressions as complicated as you
like, several simpler expressions may be easier to understand, both for you and
other human readers, and for Jazz software.
Strings are sequences of characters, so string expressions
are expressions which return this type of value. They basic form is a simple assignment of a
string constant or a CHAR or VARCHAR field: -
Record1.Customer-Name = 'John'
Record1.Customer-Name = InScreen.Customer-Name
If the assignment target is longer than the string value,
then the value is extended with blanks. For example, if Record1.Customer-Name
had been defined as CHAR(30), then
Record1.Customer-Name = 'John'
would assign 'Johnbbbbbbbbbbbbbbbbbbbbbbbbbbb' to it. Here “b”
represents a blank.
If the expression value is longer than the target string,
the value is truncated. Thus if
Record1.Customer-Name had been defined as CHAR(3), then
Record1.Customer-Name = 'John'
would have
assigned value 'Joh'.
As with COBOL’s “Address modification”, you can give starting position and length to specify a substring. Thus with definition: -
DEFINE
W DATA(
Name CHAR(26) VALUE 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
VName VARCHAR(30) VALUE 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
Char2 CHAR(2),
…
W.Char2
= W.Name(3:2);
#539
assigns value 'CD' to W.Char2. Message #539 will be produced if W.Char2 is shorter than W.Name, without taking account of the length value.
The rules for a substring references are: -
1. They can only appear in the source (right-hand side) of assignment statements.
2. The source variable, for example W.Name, must have type CHAR or VARCHAR. You cannot use this with a GROUP, a generic reference, or any other type of field.
3. If the source variable has dimension then you must give subscripts before the substring reference. Thus if W.Name had been defined
Name (5) CHAR(26) VALUE 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
then you’d write a reference such as
W.Char2 =
W.Name(3)(5:2);
to assign characters 5 and 6 of the 3rd occurrence of W.Name. A subscript is required when there is a dimension, but a subscript reference, if allowed, is always optional.
4. A substring reference has form ( Start : Length ). Start and Length may be numbers, or scalar (= without dimension) SMALLINT fields. Start must have value 1 to the length of the source field, and length may have value 0 to the remaining length of the source field. Thus
W.Char2 =
W.Name(26:2);
is invalid because the value would extend beyond the length of W.Name. When Start and Length are both numbers, as here, then Jazz will check their validity when [Check] is clicked, but if either is a SMALLINT field then the generated COBOL will contain statements to ensure their validity at run time. There are no run-time messages, but values are coerced into the valid range: -
W.Name(W.Start:*) will use value 1 as starting position if W.Start is smaller than 1, and 26 if it is larger than 26 (the length of W.Name).
W.Name(W.Start: W.Length) will
use length value 0 if W.Length is negative or the available length of W.Name if it is too
large.
If a variable Start is given with a fixed length, then the length assigned will be reduced if necessary. Thus
W.Start
= 23;
W.Char10
= W.Name(W.Start:10);
will
assign 'WXYZ' to W.Char10, not 'WXYZabcdef' where 'abcdef' represents the 6 bytes of data that happen to follow W.Name. This is different behaviour to COBOL. If W.Char10 has type VARCHAR then its length will be
set to 4, not to 10.
5.
Write Length as * to mean “To the end of the
string”. Thus write
W.VName
= W.Name(3:*);
to set W.VName to 'CDEFGHIJKLMNOPQRSTUVWXYZ'. Note the difference to COBOL, where you
would have written W.Name(3:) If you omit the * then Jazz will insert it, with a warning message.
6. Assignment to a VARCHAR field will set the target length from the substring reference. Thus
W.VName =
W.Name(3:2);
will set the value of W.VName to 'CD' and its length to 2.
W.VName =
W.Name(3:*);
sets the length of W.VName to 24.
As normal, if the length is too great, the length is set to the maximum length possible (30 in this case) and the value is truncated. If the source fields is also VARCHAR, then the maximum length possible is the current length: for example if VName is set to 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' giving it a current length=26, then a substring reference will be terminated at position 26.
The only operator permitted in a string expression is
“&&”, meaning “Concatenate”. This joins the two (or more) string
elements together. Each element can be a
CHAR
or VARCHAR
field, or a string constant: -
DEFINE
Record1 DATA(
FamilyName
VARCHAR(30),
GivenName
VARCHAR(30));
DEFINE
Record2 DATA(
NAME VARCHAR(62));
Record1.FamilyName
= 'Barnes';
Record1.GivenName
= 'Robert';
Record2.Name
= Record1.FamilyName
&& ', '
&& Record1.GivenName;
This assigns 'Barnes, Robert' to Record2.Name. Concatenation expressions are interpreted
left-to-right, and should not contain brackets or other punctuation.
Note the
constant, ', ',
separating the two fields. Without this we’d get a result like 'BarnesRobert'.
Note also the use of VARCHAR: if FamilyName and GivenName had been defined CHAR
then the value would have included 24 blanks after “Barnes”. Compare the
results above with
DEFINE
Record1 DATA(
FamilyName CHAR(30),
GivenName
CHAR(30));
DEFINE
Record2 DATA(
NAME VARCHAR(62));
Record1.FamilyName
= 'Barnes';
Record1.GivenName
= 'Robert';
Record2.Name
= Record1.FamilyName
&& ', '
&& Record1.GivenName;
Here Record2.Name is set to 'Barnes , Robert '.
To achieve the same result with CHAR as with VARCHAR, use RTRIM (see below), e.g.
Record2.Name
= Record1.FamilyName
&& ', '
&& Record1.GivenName
RTRIM;
DEFINE
R DATA(
F1 CHAR(10) VALUE ' CDE JH ',
F2 VARCHAR(10));
TRIM removes (trims) leading and trailing blanks, so that
R.F2
= R.F1 TRIM;
sets F2 to 'CDE JH' (length = 6). Note that the blank between E and J is retained.
RTRIM removes trailing (right-hand) blanks: -
R.F2
= R.F1 RTRIM;
sets F2 to ' CDE JH' (length = 8).
LTRIM removes leading (left-hand) blanks: -
R.F2
= R.F1 LTRIM;
sets F2 to 'CDE JH ' (length = 8).
TRIM, RTRIM and LTRIM affect only values with type CHAR or VARCHAR, and fields from 3270 screens, and cannot be used with constants or fields with any other type. Thus you could write
Record2.Name
= Record1.FamilyName
&& ', ' &&
Record1.GivenName RTRIM;
and have several blanks inserted between FamilyName and GivenName. Similarly, RTRIM is usually unnecessary for VARCHAR fields and fields from 3270 screens, as these have built-in length fields. Trim options TRIM, RTRIM and LTRIM may not be used if there is a substring reference.
If the source field (e.g. R.F1 above) has value ‘NULL’, ‘VALUE’, or ‘SPACE’ then the value assigned is: -
NULL. The value is a zero-length character string, and (if the field is
defined in a SQL table and is nullable, i.e. it is not defined as REQUIRED)
the indicator field will be set to -1 (field absent).
VALUE. The value is defined with its explicit or implicit VALUE property. This is usually the same as NULL.
SPACE. These is equivalent to assignments of SPACE. It does not set nullable SQL fields to NULL.
These special values must be written in upper case: if R.F1 = ‘Null’ then this is treated like any other four characters.
An ACCEPT statement will trim input data before testing them for validity, so you should be aware that the special values NULL, VALUE, and SPACE will be handled as above.
The target variable can appear in the source expression. For
example, incrementing a loop variable is simply achieved with
LoopCount = LoopCount + 1
The assignment symbols +=, -=, *=, and /= are convenient shorthand for this kind of assignment.
Thus
LoopCount += 1
is equivalent to the earlier assignment. +=, -=, *=, /= are valid for numeric assignments.
For String assignments you can use &=. For example
JZTMVS.Msg &= 'Min>59;' RTRIM;
is equivalent to
JZTMVS.Msg = JZTMVS.Msg && 'Min>59;' RTRIM;
You can
refer to any COBOL special values, for example Spaces and Zero, and COBOL
Intrinsic Functions: -
w.name1
= spaces;
You can
refer to Jazz built-in Functions, which will be named starting with $. There are also some Jazz special names that
you can refer to, such as JZ.JZReturnCode, that have meaning in particular contexts.
For a list
of these functions and names, refer to JazzLRMBuiltInFunctions
A generic assignment assigns all the fields of one record or
group to the like-named fields in another (except for FILLER). For
example, suppose that we had defined INPUT1 and RECORD1 like this:-
DEFINE
INPUT1 DATA(
NAME CHAR(30),
ADDRESS
CHAR(30),
CITY CHAR(15),
BALANCE
MONEY(7,2));
DEFINE
RECORD1 DATA(
CODE CHAR(1),
NAME CHAR(30),
BALANCE
MONEY(7,2));
Now if we write
RECORD1.* = INPUT1.*;
this is equivalent to the simple assignment statements:
RECORD1.NAME = INPUT1.NAME;
RECORD1.BALANCE = INPUT1.BALANCE;
The
source of a generic assignment must be another generic expression or the Jazz
function $Init (which Jazz will qualify to JAZZ.$Init):-
T.* = JAZZ.$Init;
This is identical in effect to the assignment statement
T
= JAZZ.$Init;
It is NOT valid to assign a single field or constant to a generic expression: -
T.*
= SPACES;
#543 S Source SPACES is invalid for a generic assignment.
Reason: Constant
Generic assignments like
RECORD1.* = INPUT1.*;
apply STRICT rules to determine which fields are included in the assignment. Since you didn’t write the STRICT option, message 698 (information-level only) is produced.
With STRICT rules, to be included in a generic assignment the fields must have the same names, including the names of containing groups. For example, record In1A is defined like this: -
DEFINE
IN1A TYPE(FB) DATA(
Region PIC '99',
Filler CHAR(3),
District PIC '99',
Filler CHAR(4),
Name CHAR(15),
Filler CHAR(2),
SalesThisMonth
PIC '99999.99',
Filler CHAR(3),
SalesYTD PIC '99999.99',
Filler CHAR(4),
Cardholder
CHAR(1),
Filler CHAR(4),
DateCommenced
CHAR(10),
Filler CHAR(14))
DSNAME '@Project.@Group.SRCLIB(DTDTA1)';
and record IN1 is defined like this: -
DEFINE
IN1 TYPE(VB) DATA(
JZ-Key GROUP KEY,
Region
DECIMAL(3),
District
DECIMAL(3),
END GROUP,
Name CHAR(15),
SalesThisMonth
DECIMAL(7,2),
SalesYTD DECIMAL(7,2),
Cardholder
CHAR(1),
DateCommenced
CHAR(10))
DSNAME('IBMUser.Files.IN1');
Because Region and District are within group JZ-Key in IN1 they are not included in the generic assignment, they have to be individually assigned in this program. The Jazz message is helpful in alerting us to this potential issue: -
COPY
in1a;
COPY
IN1;
PROCESS
in1A;
PRINT
(IN1A.*);
In1.* = IN1a.*;
#207 I
Name,SalesThisMonth,SalesYTD,Cardholder,DateCommenced included in generic
assignment
In1.Region
= in1a.region;
in1.district
= in1a.district;
WRITE
in1;
END
PROCESS in1A;
With SIMPLE the group structure is ignored, and it is sufficient for the field name to be found. You would use this in the case above to avoid the need for the individual assignments
In1.Region
= in1a.region;
in1.district
= in1a.district;
Be careful if there is ambiguity, as in
DEFINE
IN1 TYPE(VB) DATA(
JZ-Key GROUP KEY,
Region
DECIMAL(3),
District
DECIMAL(3),
END GROUP,
OLD-Key GROUP,
Region
DECIMAL(3),
District
DECIMAL(3),
END GROUP,
…
With SIMPLE you can only refer to the first occurrence of the name, and the duplicate names in OLD-Key will be ignored.
Use EXCEPT to exclude fields from a generic assignment. For example
IN1.*
= In1a.* EXCEPT(IN1.Region, IN1A.RDKey.District);
omits the fields Region and District from the generic assignment. Note that you can name fields on either side of the assignment. However if you name an unrelated field then there is no message and the irrelevant item is ignored. Thus in the program above if you add this option to the original statement
IN1.*
= In1a.* except region, district;
and then click [Check] it becomes
IN1.*
= In1a.* EXCEPT(Types.Region, IN1A.RDKey.District);
#207 I RDKey.Region,Name,SalesThisMonth,SalesYTD,DateCommenced,
included in generic assignment
As you see there is no message about the omission of Types.Region,
and RDKey.Region
from the assignment.
When redefined fields occur within a generic reference, as in: -
DEFINE
RECORD1 DATA(
ACCOUNT
NUMERIC(8),
BANK NUMERIC(2) REDEFINES RECORD1.ACCOUNT,
…);
DEFINE
RECORD2 DATA(
ACCOUNT NUMERIC(8),
BANK NUMERIC(2) REDEFINES RECORD1.ACCOUNT,
…);
then both ACCOUNT and BANK will be assigned by
RECORD1.*
= RECORD2.*;
even though assignment of BANK is superfluous.
Fields are assigned in order of their definition position within the target record.
If a field
is defined as a table, i.e. with one or more dimensions, then you must refer to
it with the correct number of subscripts. For example,
DEFINE
D DATA(
A SMALLINT,
B (3) SMALLINT,
C (3) SMALLINT,
D(3) INTEGER,
E (2, 3, 4) SMALLINT,
Gr1 (2) GROUP,
F
(3, 4) SMALLINT,
END
GROUP);
Just as we
can set the value of A to 1234: -
D.A = 1234;
we can refer to elements
of a table by specifying a subscript: -
D.B(2) =
5678;
D.A = D.B(2);
We must give the correct number of subscripts, and so we are not
permitted to write
D.B =
D.C;
to assign the whole array. Instead we use “(*)” to indicate “All
of this dimension”, writing
D.B(*) =
D.C(*); which Jazz will change to D.B(*) = D.C($IX1);
This is only valid if the arrays have exactly the same dimension,
as they do here, and imply a loop like
FOR
D.B(IX);
D.B(IX) = D.C(IX);
END
FOR;
Because there is an implied loop, a statement like D.B(*) =
D.C($IX1); is valid
as long as an assignment from an element of D.C to an element of D.B is valid. Thus we could write
D.D(*) =
D.B($IX1);
and each SMALLINT value becomes an INTEGER value. Similarly, we can
write
D.B(*) =
D.E(1, $IX1, 1);
(here we must write $IX1 explicitly because it is not in the same
position as the * in the target).
We can also write statements like this when the dimension of the
source and target are different. Thus
DEFINE
D DATA(
B (3) SMALLINT,,
E (2, 5, 4) SMALLINT,
…
D.B(*)
= D.E(1, $IX1, 1);
the first three elements of E are assigned, ignoring elements 4 and 5. However if the dimension of E is less than the corresponding dimension of B, this is not valid.
The rules of DATE assignment and arithmetic are detailed in this Users’
Guide page JazzUGDate.htm