The NetRexx Tutorial
- Classes and Objects in NetRexx
NetRexx Tutorial - Classes and Objects in NetRexx


Classes and Objects in NetRexx

As we already said, NetRexx, like its cousin Java, is an object-oriented (OOP) language . The term object-oriented has become so widely used that we need to give it a more concrete meaning.

This section assumes no knowledge of object-oriented concepts.

At the end of this section, I hope that you'll get the feeling of how OOP can be "fun".

Some basic ideas.

The Object Oriented Programming basic ideas are simple ones. Unfortunately, OOP has developed some special terminology, and many introductory works become totally incomprehensible to people encountering the subject food the first time.

OOP has four key concepts. You can remember them from the acronym "A PIE": think about the big pie that software vendors are sharing in selling us their OOP products. The components are:

 
A - Abstraction
P - Polymorphism
I - Inheritance
E - Encapsulation
 

In the following part of the chapter, we will consider, as an example, the OOP representation of a 3 dimensional vector.

A 3d vector, we will see, can be defined in a computer using three numbers (this is the ABSTRACTION). A whole series of operations can be performed on a 3d vector (like inverting it, summing with other vectors, etc), making sure that we never corrupt the values of it (this is the ENCAPSULATION part). Using the concepts we used to define the 3d vector, we can build a 4d vector, keeping some of the functions we used to encapsulate the 3d vector (and this is the INHERITANCE part). Indeed, some functions (like the sum) must be overridden by the new 4d vector functions (to take account of the 4th dimension), and that' all for the POLYMORPHISM.

Resuming it in few lines definitely looks hard, but (you'll see) there is nothing more.

A vector class

In this section we develop a simple example class, that we will call vector3d, that , as you can easily guess, will represent a geometric object in a three-dimensional space.

A vector, quoting Feynman, is three numbers. In order to represent step in space, say from the origin to some particular point P whose location is (x, y, z), we really need three numbers, but we are going to invent a single mathematical symbol, r. (...) It is not a single number, it represents three numbers: x, y and z. (FEYNMAN, 1963).

In those words Feynman has, de facto, extracted out the essential characteristics that we need to consider in order to represent a vector on a computer. This process is called abstraction.

Translating the above words in the NetRexx language, we get:

 
class vector3d public
  properties public
    xc           -- x component
    yc           -- y component
    zc           -- z component
 

The important thing to note is that we did not define a real vector r. We just defined how we define a vector, i.e. with 3 quantities xc, yc, and zc.

The lines above contain two new keywords: class and properties.

The class keyword must be followed by the name of the class that we are defining. NOTE: this name MUST be the filename of the file we are writing: i.e. vector3d.nrx.

After the properties keyword we define the so called data-members, which are, de-facto, variable names.

Methods

There is a number of things that we can do with vectors: we can compute their magnitude (or module), we can inverse them. We can also execute operations with two vectors, like adding two vectors, computing their scalar product, check if they are equal, etc.

For each of those operations we then define a method which is, if you like, a sort of function that belongs to a class and that we perform over an object (belonging to this class).

 

   -- method x()
   -- will return the x component of the 3d vector
   method x() public
     -- the code will go here

   -- method inverse()
   -- will inverse a 3d vector
   method inverse() public
     -- the actual code will go here

   -- method mag()
   -- will return the magnitude of a vector
   method mag() public
     -- the actual code will go here
   -- etc. etc.

 

Why we use the term method, and not just function or procedure ? The reason is just historical [VAN DER LINDEN, 1997] and goes back to Smalltalk-72. For you, just remember that a method is just a function that belongs to a class.

With the definition of the methods, we then have completed the class definition.

Resuming, if we want to capture the class of 3d vectors, (at least partially) in NetRexx code, we will write:

 
class vector3d public
  properties public
    xc           -- x component
    yc           -- y component
    zc           -- z component

  method inverse() public
    xc = -xc
    yc = -yc
    zc = -zc

  method mag() public
    mag = Math.sqrt(xc*xc + xy*xy + xz*xz)
    return mag
 

When you define a class, you need to specify:

 

 +----------------------------------------+
 |                                        |
 | CLASS                                  |
 |                                        |
 |         +----------------------------+ |
 |         | PROPERTIES                 | |
 |         |  (storage definitions)     | |
 |         |                            | |
 |         +----------------------------+ |
 |         | METHODS                    | |
 |         |  (operations on the        | |
 |         |   PROPERTIES)              | |
 |         |                            | |
 |         +----------------------------+ |
 |                                        |
 +----------------------------------------+

 

Some "real" vectors

The Objects are instances of a Class. So far we have defined how and what we can do to define and use a vector, but we need a "real" one, to try out the class definitions, and use it. We need an instance of the class.

By defining the vector3d class in NetRexx, we have now created a new data type. To have a REAL vector3d you then write:

 
    v = vector3d()
 

Here you just told NetRexx: "please, treat the variable v as a vector3d: as I told you in the class definition, this variable will have 3 components associated to it, and I will be capable to perform operations like inverse , mag() etc. to it".

As you probably realize, all this procedure made you create "something" that is NOT a string. Infact, as I said, NetRexx has ONLY one NATIVE data type (the string) but you can create your own data types, and vector3d is just one example.

NOTE for Java Programmers: Note that this definition is a bit different of what you would do in Java. If you had to write the very same code in Java you would do:

 
  vector3d v;
  v = new vector3d();
 

In NetRexx, the dynamical definition of the object is done automatically for you (saving one line of typing).

Initialising the Vector values

Now that we have a real vector3d object v, we can use its data fields and initialise it to some values.

We do it like this:

 
  -- v is a vector3d object
  v = vector3d()
  -- initialise the vector 2 , 3 , 1
  v.xc = 2  
  v.yc = 3
  v.zc = 1
 

Memory Model

Consider the following definitions, were we define two vectors v1 and v2:

 
 v1 = vector3d( 1 , 3 , 0)
 v2 = vector3d( 0 , 1 , 1)
 

It is important to consider how NetRexx defines those objects (and the class methods) in your computer's memory.

 

   Objects:                     Code:
   +------+                     +-------------------+
   |xc=1  |                     | vector3d()        |
v1:|yc=3  |========>===========>| x()               |
   |xc=0  |         =           | y()               |
   |      |         =           | z()               |
   +------+         =           | mag()             |
                    =           | phi()             |
   +------+         =           |                   |
   |xc=0  |         =           |                   |
v2:|yc=1  |========>=           +-------------------+
   |xc=1  |
   |      |
   +------+

 

We can see that an object is an instance of a class (which is a new, user defined, type).

Each object (the vectors v1 and v2 in our example ) has its own data.

On the contrary, only ONE copy of the code for a class is shared by all the objects (that we now know we can call instances of the class).

Using vector3d Methods.

So far, we just defined the vector v, but we have done nothing with it.

To access vector3d methods, we use the very same syntax we used to access the data of the object.

 
  v = vector3d()
  -- Initialise values
  (...)
  m = v.mag()     -- compute vector' mag
  p = v.phi()     -- compute vector's PHI
 

In classical non-OO languages (FORTRAN, REXX, Pascal, etc.) the above call would have been written like:

 
   m = mag(v)
   p = phi(v)
 

while, in NetRexx, we wrote:

 
   m = v.mag()
   p = v.phi()
 

The difference is not just cosmetics: we are stressing the fact that the "center" of our attention is the v object, not the action that we are performing (the computation of the MAG or of PHI).

We see that properties and methods are considered at the same logical level (even if, as memory is concerned, treated in different ways).

So:

 
  v.xc = 2     -- means:
               -- assign 2 to the xc component of v

  m = v.mag()  -- means:
               -- apply method "mag()" to v, and store
               -- the result in "m"
 

Initialising a vector: the constructor.

If we look closer to the instruction we used to create a vector:

 
 v = vector3d()
 

we notice the usage of the parentheses () after the vector3d. This looks really like a method call. Infact, we are calling a special method, called constructor, which is used to perform all the initialisations that are needed to prepare the new object.

The constructor is a "special" method, that's why it MUST have the same name of the class. So, since our class is called vector3d, to define the vector3d constructor method we'll write:

 
class vector3d
      ========      <-----------------+
                                      |
                                      |
-- constructor                        |
  method vector3d( ... ) public       |
         ========   <-----------------+
                                      |
                                      |
                                      |
                +--------------------- ------------+
                |I MUST use the same name for      |
                |the CLASS and for the CONSTRUCTOR |
                +----------------------------------+
 

Our first constructor will then look like:

 
-- method......: vector3d
-- purpose.....: constructor
--
  method vector3d(x=Rexx,y=Rexx,z=Rexx) public
    this.xc = x
    this.yc = y
    this.zc = z
 

In order to use the constructor for our vector initialization, we'll then write:

 
  v = vector3d(2,3,1)
 

which is exactly the same as writing, when we had not defined the constructor:

 
  v = vector3d()       -- ditto like
  v.xc = 2             --
  v.yc = 3             --   v = vector3d(2,3,1)
  v.zc = 1             --
 

Defining more than one constructor.

You'll find that having just one constructor method is usually not enough. Even in our simple class, it would be nice if it was possible to write something like:

 
  zero = vector3d()     -- define a vector 0 0 0

  v = vector3d(3,2,1)
  z = vector3d(v)       -- define a vector like v,
                        -- i.e. 3 2 1

  unary = vector3d(1)   -- define a vector 1 1 1
 

You can do this in NetRexx writing "additional" methods, with the same name, but with different arguments. In our example, we'll write:

 
  -- overloaded constructors
  --
  method vector3d() public
    this(0,0,0)

  method vector3d(x=Rexx) public
    this(x,x,x)

  method vector3d(v1=vector3d) public
    this(v1.x,v1.y,v1.z)

 

What we just achieved is an operation of "method overloading", i.e. define a method with the same name, but different arguments.

Undefined constructor

So far we have defined 4 constructor methods, which are (just to summarise):

 
  method vector3d(x=Rexx,y=Rexx,z=Rexx) public

  method vector3d() public

  method vector3d(x=Rexx) public

  method vector3d(v1=vector3d) public

 

This tells NetRexx that there are 4 ways to define a new vector. What happens if you try to write:

 
  a = vector3d(1,2)
 

Simple: NetRexx does not know how to treat this case, so you'll get a very nasty message saying:

 
  4 +++ a = vector3d(1,2)
    +++     ^^^^^^^^
    +++ Error: cannot find constructor 'vector3d.vector3d(byte,byte)'
 

which means: "I do not know how to deal with this special case of vector3d followed by 2 arguments."

The main() method

The main() method is a special one. It is the method that will automatically be called if you invoke a class directly from the command line.

Recall the parrot program:

 
+----------------------------------------------------------------------+
| /* parrot.nrx                                                        |01
|  * echoes back what you type on command line                         |02
|  */                                                                  |03
| parse arg s1                                                         |04
| say 'you said "'s1'".'                                               |05
| exit 0                                                               |06
+----------------------------------------------------------------------+
                                                              parrot.nrx
Download the source for the parrot.nrx example

If you want to write the very same code using a class, you'll do:

 
+----------------------------------------------------------------------+
| -- This class implements a class version of parrot.nrx               |01
| --                                                                   |02
| class parrotc public                                                 |03
|                                                                      |04
|   method main(arguments=String[]) public static                      |05
|     parse Rexx(arguments) s1                                         |06
|     say 'you said "'s1'".'                                           |07
|     exit 0                                                           |08
+----------------------------------------------------------------------+
                                                             parrotc.nrx
Download the source for the parrotc.nrx example

The two programs are perfectly equivalent (although the first one is definitely less typing). Infact, what NetRexx does is to translate the 1st one into "something" that looks like the 2nd one.

The main() method is very useful if you want to test a class. You will just put the class test cases, and run it typing java PROGNAME.

Putting all those pieces together

This is probably the most important section we've seen so far, since we finally apply in reality what we've been doing till now.

We have a file, called vector3d.nrx, that contains all the properties and methods used by the vector3d class. We compile it, and obtain a vector3d.class class file.

We can now edit a file that exercises the 3d vectors. The easiest one can be something like:

 
+----------------------------------------------------------------------+
| -- tvec3ds.nrx                                                       |01
|                                                                      |02
| a = vector3d(1,1,1)    -- define a vector                            |03
| say 'Vector "a" components:' a.components()'.'                       |04
|                                                                      |05
| a.inverse()            -- inverse it                                 |06
| say 'Vector "a.inverse()" is' a.components()'.'                      |07
| exit 0                                                               |08
|                                                                      |09
+----------------------------------------------------------------------+
                                                             tvec3ds.nrx
Download the source for the tvec3ds.nrx example

As you can see, we do very little: just define a vector3d a, display his components, invert it, and check that all was OK.

We compile tvec3ds.nrx. NetRexx will grab the vector3d class definition at compile time, so it will know how a vector3d looks like. We end up with with a tvec3ds.class, which we can run as usual.

Resuming:

 
-- compile
[1]> java COM.ibm.netrexx.process.NetRexxC vector3d.nrx
[2]> java COM.ibm.netrexx.process.NetRexxC tvec3ds.nrx
-- run
[3]> java tvec3ds
 

To visually resume what we did, here's a picture:

 

   +---------+            +---------+
   |         |            |         |
   |SOURCE   |            |SOURCE   |
   |         |            |         |
   +---------+            +---------+
   vector3d.nrx           tvec3ds.nrx
       =                      =
       =                      =
 (java IBM... vector3d)   (java IBM... tvec3ds)
       =  [1]                 =   [2]
       =                      =
   +---------+             +---------+
   |         |             |         |
   |JAVA CODE|=============|JAVA CODE|
   |         |             |         |
   +---------+             +---------+
   vector3d.class          tvec3ds.class
                               =
                               =
                               =
                           (java tvec3ds)
                               =
                                 [3]
 

The following program illustrates all what we've implemented in the vector3d class.

 
+----------------------------------------------------------------------+
| -- tvec3d.nrx                                                        |01
| -- exercise the 3dim vector class                                    |02
| --                                                                   |03
| a = vector3d(1)                                                      |04
| b = vector3d(3,4,3)                                                  |05
| c = vector3d()                                                       |06
| d = vector3d(b)                                                      |07
| e = vector3d()                                                       |08
| f = vector3d()                                                       |09
|                                                                      |10
| say 'Vector "a" components:' a.components()'.'                       |11
| say 'Vector "b" components:' b.components()'.'                       |12
| say 'Vector "c" components:' c.components()'.'                       |13
| say 'Vector "d" components:' d.components()'.'                       |14
| say 'Say "a.mag()" is: 'a.mag()'.'                                   |15
|                                                                      |16
| e.zero()                                                             |17
| e.add(a)                                                             |18
| e.add(b)                                                             |19
| say 'Vector "a+b" is' e.components()'.'                              |20
| e.inverse()                                                          |21
| say 'Vector "e.inverse()" is' e.components()'.'                      |22
|                                                                      |23
| e = vector3d.add(a,b)                                                |24
| say 'Vector "a+b" is' e.components()'.'                              |25
|                                                                      |26
| f = vector3d.greater(a,b)                                            |27
| say 'Vector "greater(a,b)" is' f.components()'.'                     |28
|                                                                      |29
| -- let's play with an array of vectors                               |30
| --                                                                   |31
| k = 200                                                              |32
| v = vector3d[k]                                                      |33
| v[1] = vector3d(1,1,1)                                               |34
| v[2] = vector3d(2,2,1)                                               |35
| v[3] = vector3d(0,2,0)                                               |36
| e.zero()                                                             |37
| loop i = 1 to 3                                                      |38
|   say 'vector "v['i']" is' v[i].components()'.'                      |39
|   e.add(v[i])                                                        |40
| end                                                                  |41
| say 'Vector "INTEGRAL" is' e.components()'.'                         |42
|                                                                      |43
| exit 0                                                               |44
+----------------------------------------------------------------------+
                                                              tvec3d.nrx
Download the source for the tvec3d.nrx example
 
 *** This section is: 
  
 *** and will be available in next releases

Static Properties and Methods

Subclasses and Inheritance

The vector3d class we defined is very good for classical physics. But, for relativistic studies, we need also to add another dimension: t.

This means that we need a new class, which we'll call vectorLo (as an abbreviation for vectorLorentz: a vector in the 4 dimension space).

Extending a Class

NetRexx allows you to use the code we already wrote for the 3 dimension vector class, defining vectorLo as an extension (or subclass) of vector3d

We do this as:

 

class vectorLo public extends vector3d      
   properties public 
     (...)

   method (...)

 

The extends keyword tells NetRexx that the newly created vectorLo class is a subclass of vector3d. As such it INHERITS the variables and methods declared as public in that class.

That's where the real point is: we do not have to define again the method x(), in order to get the x component of a Lorentz vector, we just use the method we inherited from the 3 dimensional vector3d class.

Some methods, of course, need to be overloaded, like in the case of:

 
-- method......: components                                          
-- purpose.....: prints the components                               
--                                                                   
  method components() public returns string                          
    return '('this.xc','this.yc','this.zc','this.tc')'
 

to take into account the new dimension.

The Lorenz's vector implementation will be:

 
+----------------------------------------------------------------------+
| -- This class implements a Vector in a 4 dimentional space           |01
| --                                                                   |02
| class vectorLo public extends vector3d                               |03
|   properties public                                                  |04
|     tc                                                               |05
|                                                                      |06
| -- method......: vectorLo                                            |07
| -- purpose.....: constructor                                         |08
| --                                                                   |09
|   method vectorLo(x=Rexx,y=Rexx,z=Rexx,t=Rexx) public                |10
|     super(x,y,z)                                                     |11
|     this.tc = t                                                      |12
|                                                                      |13
|   method vectorLo() public                                           |14
|     this(0,0,0,0)                                                    |15
|                                                                      |16
|   method vectorLo(x=Rexx) public                                     |17
|     this(x,x,x,x)                                                    |18
|                                                                      |19
|   method vectorLo(v1=vectorLo) public                                |20
|     this(v1.xc,v1.yc,v1.zc,v1.tc)                                    |21
|                                                                      |22
| -- method......: components                                          |23
| -- purpose.....: prints the components                               |24
| --                                                                   |25
|   method components() public returns string                          |26
|     return '('this.xc','this.yc','this.zc','tc')'                    |27
|                                                                      |28
| -- method......: main                                                |29
| -- purpose.....: runs the test case                                  |30
| --                                                                   |31
|   method main(args=String[]) public static                           |32
|     args=args                                                        |33
|     a = vectorLo(1,1,1,1)                                            |34
|     b = a                                                            |35
|                                                                      |36
|     say 'Vector "a" components:' a.components()'.'                   |37
|     say 'Vector "b" components:' b.components()'.'                   |38
|     say b.mag()                                                      |39
|                                                                      |40
|     exit 0                                                           |41
+----------------------------------------------------------------------+
                                                            vectorLo.nrx
Download the source for the vectorLo.nrx example

Class Hierarchy

Just to clear out the terminology we speak about superclasses and sublasses, saying:

 
            +---------------+
            | vector3d      |
            +---------------+
         +-(...is a superclass of...)<--+
         |                              |
         |                              |
         |                              |
         +->(...is a subclass of...)----+
            +---------------+
            | vectorLo      |
            +---------------+
 

or, if you prefer:

 

                      +--------+
                      |KEYWORD |
                      +--------+
                          |
                          V
 class vectorLo public extends vector3d
          |                       |
          |                       |
          V                       V
      +---------+             +-----------+
      |SUBCLASS |             |SUPERCLASS |
      |INHERITS |             |           |
      +---------+             +-----------+
 

Check if an object belongs to a subclass

It is sometimes useful to check if we have a particular subclass, within a superclass, and perform this check at runtime.

Java programmers might use the instanceof operator; in NetRexx you just do:

 
 object <= class_name
 

using the <= operator.

So, for example, we might have:

 
   class vector3d public
      (...)
   class vectorLo public extends vector3d
      (...)
   class vectorHEP public extends vectorLo
      (...)

   v1 = vectorLo()
   if v1 <= vectorLo
 

Another way to test for a class match, as suggested by Mike Cowlishaw in a recent thread is:

 
   if OBJECT.getclass.getname == 'CLASS' then
 

I resume the above discussion in the following code:

 
+----------------------------------------------------------------------+
| -- tvecLo1.nrx                                                       |01
| --                                                                   |02
| a = vectorLo(1,2,1,1)  -- a Lorentz vector                           |03
| b = vector3d(1,1,1)    -- a 3d vector                                |04
|                                                                      |05
| -- check if a and b are Lorentz vectors                              |06
| --                                                                   |07
| if a <= vectorLo                                                     |08
|   then say 'a is a Lor vec'                                          |09
| if b <= vectorLo                                                     |10
|   then say 'b is a Lor vec'                                          |11
|                                                                      |12
| -- get in another way                                                |13
| --                                                                   |14
| say 'a is a:' a.getclass.getname                                     |15
| say 'b is a:' b.getclass.getname                                     |16
|                                                                      |17
| exit 0                                                               |18
+----------------------------------------------------------------------+
                                                             tvecLo1.nrx
Download the source for the tvecLo1.nrx example

First case study: A better approach to vectors.

I presented the example of the 3 dimensional and 4 dimensional vector classes mainly for "educational" purposes. We saw infact a "minor" problem which is the need to write again some methods for the 4 dimensional vector class, because we need to take into account the fact that we have an extra dimension (remember the mag() method). So, if we have to deal with 5 dimensional vectors, we'll need to rewrite AGAIN those methods.

There MUST be a better approach; the idea is to write a class which has NO notion of the space dimension, and use that to build the 3d, 4d, 5d, etc. vectors.

This class will be called xvector.nrx.

The xvector class: a generic vector.

The xvector class will implement a N-dimensional vector. We are then forced to use arrays to hold the numerical values.

The mag() method will look like:

 
+----------------------------------------------------------------------+
| -- method......: mag                                                 |97
| -- purpose.....: vector's elements mag                               |98
| --                                                                   |99
|   method mag() public                                                |00
|     sum = 0                                                          |01
|     loop i = 1 to this.dimension                                     |02
|       sum = sum + this.element[i]*this.element[i]                    |03
|     end                                                              |04
|     sum = Math.sqrt(sum)                                             |05
|     return sum                                                       |06
|                                                                      |07
+----------------------------------------------------------------------+
                                                 xvector.nrx(Method:mag)
Download the complete source for the xvector.nrx library

 
+----------------------------------------------------------------------+
| -- method......: add                                                 |24
| -- purpose.....: adds a vector to another                            |25
| --                                                                   |26
|   method add(v1=xvector,v2=xvector) public static returns xvector    |27
|     v3 = xvector('0')                                                |28
|     v3.dimension = v1.dimension                                      |29
|     loop i = 1 to v1.dimension                                       |30
|       v3.element[i] = v1.element[i] + v2.element[i]                  |31
|     end                                                              |32
|     return v3                                                        |33
|                                                                      |34
+----------------------------------------------------------------------+
                                                 xvector.nrx(Method:add)
Download the complete source for the xvector.nrx library

A revisited 3d vector.

Look now how simple is to build our 3 dimension vector class: we just extend the xvector class and override the constructor, to allow writing:

 
  v = xvector3d(1,2,3)
 

I'll call the new 3d vector class xvector3d, to avoid confusion with the vector3d one we studied in the previous sections.

 
+----------------------------------------------------------------------+
| -- This class implements a Vector in a 3 dimensional space           |01
| -- extending the xvector class                                       |02
| --                                                                   |03
| class xvector3d public extends xvector                               |04
|                                                                      |05
| -- method......: vectorLo                                            |06
| -- purpose.....: constructor                                         |07
| --                                                                   |08
|   method xvector3d(x=Rexx,y=Rexx,z=Rexx) public                      |09
|     super(x','y','z)                                                 |10
|                                                                      |11
|   method xvector3d() public                                          |12
|     this('0','0','0')                                                |13
|                                                                      |14
|   method xvector3d(x=Rexx) public                                    |15
|     this(x,x,x)                                                      |16
|                                                                      |17
|   method xvector3d(v1=xvector3d) public                              |18
|     this(v1.element[1],v1.element[2],v1.element[3])                  |19
|                                                                      |20
| -- method......: main                                                |21
| -- purpose.....: runs the test case                                  |22
| --                                                                   |23
|   method main(args=String[]) public static                           |24
|     args=args                                                        |25
|     a = xvector3d(1,1,1)                                             |26
|     b = a                                                            |27
|                                                                      |28
|     say 'Vector "a" components:' a.display()'.'                      |29
|     say a.mag()                                                      |30
|     say 'Vector "b" components:' b.display()'.'                      |31
|                                                                      |32
|     exit 0                                                           |33
+----------------------------------------------------------------------+
                                                           xvector3d.nrx
Download the source for the xvector3d.nrx example

Second case study: the command line class cmdline.

After having dealt with vectors, which might not be interesting for you, if you're not a physicist or an engineer, let's start with some real objects that you are dealing with everyday.

The command line

The command line is one of those objects. With command line I mean all what you enter after a program's name on the command line (shell or DOS prompt).

 
prompt> java my_command arguments -options
                        ------------------
                                |
                         +--------------+
                         | COMMAND LINE |
                         +--------------+
 

A command line is usually divided in

Just take a UNIX book and you'll find hundreds, if not thousands of examples. I give you a really small sample:

 
command             options      arguments
----------------    -------      ---------

ls -la test tost    -l -a        test tost
df -k /usr          -k           /usr
cat test            NONE         test
tar -cvf o.tar *    -c -v        *
                    -f tar
 

The operations that we do, when analysing a command line in a in a program are (in random order):

Additional requirements

Since we want to be clever, we add also some requirements:

We want that the arguments and options can be intermixed: this means that:

 
myprog -t -o test.file input_arg
myprog -to test.file input_arg
myprog input_arg -o test.file -t
 

MUST be perfectly equivalent from the user's point of view. (note that this is not always true in UNIX!).

Also, we want to be capable to query, at any time in the program, the value of an option, in order to write something like:

 
il = cmdline()
(...)
if il.option('TRACE')
  then say 'Tracing is active'
(...)
 

Option pre-setting.

In the actual implementation, we need indeed an additional information, which is "how to pass the options and their default value when we create the cmdline?".

A way is to use a string that holds, separated by a delimiter, the value of :

 
- the symbol of the option (like r, t, o, etc.);
- a parameter indicating if it's a flag or a variable;
- the NAME of the option
- the default value
 

We will call this string the rules definition, since we use those rules to define the options.

Example:

 
't/FLA/TRACE/0'
 

we define an option (-t) which is a flag, known in our program as 'TRACE' and defaulted to 0

 
'o/VAR/OUTFID/test.output'
 

we define an option (-o) which defines a variable, known in our program as 'OUTFID' and defaulted to test.output

 
't/FLA/TRACE/0 o/VAR/OUTFID/test.output'
 

our rules definition is now to have two options, the same as above

Cmdline class overview

The cmdline constructor will accept two arguments: the first one being a rexx string containing the line entered by the user; the second one being again a rexx string, containing the rules in the format we defined. This allows us to already prepare all the options and all the arguments.

 
+----------------------+
|USER'S INPUT          |
|like: file1 -t -o test|
+----------------------+
                 |
cl = cmdline( inputline , rules )
 |                          |
 |                +------------------------------+
 |                | PROGRAMMER'S RULES           |
 |                | like:  't/FLA/TRACE/0' -     |
 |                |        'o/VAR/OFID/test.out' |
 |                +------------------------------+
 |
 +------------------------------------+
 | This object is now aware of the    |
 | options as entered by the user     |
 | allowing something like:           |
 | if \cl.option('TRACE') then ...    |
 +------------------------------------+

The class will look like:

 
class cmdline
  properties private
    options
    argument
  (...)
  method cmdline(rexx,rexx) public
  method option(rexx) public
  method verify(rexx) public
  method optiondump() public
  (...)
 

Cmdline class implementation.

I show now how some of the class methods are implemented.

By far the most complex is the cmdline constructor. We need infact to analyse the command line, as entered by the user (instr) and parse the options as defined by the programmer (rules).

The first step is to check the rules, set the valid options and set the default option values.

 
+----------------------------------------------------------------------+
| -- method......: cmdline                                             |95
| -- purpose.....: constructor                                         |96
| --                                                                   |97
|   method cmdline(instr=Rexx,rules=Rexx) public                       |98
|                                                                      |99
|     -- initial setup                                                 |00
|     --                                                               |01
|     olist  = ''        -- option_list                                |02
|     oinfo  = ''        -- option info                                |03
|     outstr = ''        -- that's the string that holds all BUT the   |04
|                        -- options; we'll return this                 |05
|                                                                      |06
|     -- set the defaults                                              |07
|     --                                                               |08
|     loop for rules.words()                                           |09
|       parse rules rule rules                                         |10
|       parse rule opt'/'info                                          |11
|       olist = olist opt                                              |12
|       oinfo[opt] = info                                              |13
|       parse  info kin'/'nam'/'def                                    |14
|       select                                                         |15
|         when kin = 'FLA' then                                        |16
|           do                                                         |17
|             value[nam] = def                                         |18
|           end                                                        |19
|         when kin = 'VAR' then                                        |20
|           do                                                         |21
|             def = def.translate(' ','$')                             |22
|             value[nam] = def                                         |23
|           end                                                        |24
|         otherwise                                                    |25
|           do                                                         |26
|             say '(parse_UXO) Internal error.'                        |27
|             say '(parse_UXO) kin was "'kin'".'                       |28
|             say '(parse_UXO) Aborted.'                               |29
|             exit 901                                                 |30
|           end                                                        |31
|       end                                                            |32
|     end                                                              |33
|                                                                      |34
|     -- get the options as entered                                    |35
|     --                                                               |36
|     loop while instr <> ''                                           |37
|       parse instr var instr                                          |38
|       if var.left(1,1) <> '-' then                                   |39
|         do                                                           |40
|           outstr = outstr var                                        |41
|           Iterate                                                    |42
|         end                                                          |43
|       svar = var                                                     |44
|       var = var.substr(2,1)                                          |45
|       if olist.wordpos(var) = 0 then                                 |46
|         do                                                           |47
|           say 'Invalid option "'var'" selected.'                     |48
|           say 'Valid options are "'olist.space()'".'                 |49
|           say 'Program aborted.'                                     |50
|           exit 902                                                   |51
|         end                                                          |52
|       info = oinfo[var]                                              |53
|       parse info kin'/'nam'/'def                                     |54
|       select                                                         |55
|         when kin = 'FLA' then                                        |56
|           do                                                         |57
|             if def = '0'                                             |58
|               then def = '1'                                         |59
|               else def = '0'                                         |60
|             value[nam] = def                                         |61
|           end                                                        |62
|         when kin = 'VAR' then                                        |63
|           do                                                         |64
|             def = def.translate(' ','$')                             |65
|             cho = ''                                                 |66
|             loop for def.words()                                     |67
|               parse instr tt instr                                   |68
|               if tt = '' then                                        |69
|                 do                                                   |70
|                   say 'Invalid argument for option "'var'".'         |71
|                   say 'Should be a' def.words() 'words string.'      |72
|                   say 'Like default "'def'".'                        |73
|                   say 'Program Aborted.'                             |74
|                   exit 903                                           |75
|                 end                                                  |76
|               cho = cho tt                                           |77
|             end                                                      |78
|             value[nam] = cho.space()                                 |79
|           end                                                        |80
|         otherwise NOP                                                |81
|       end                                                            |82
|       -- here I deal with the case when one enters                   |83
|       -- -tf instead of -t -f                                        |84
|       --                                                             |85
|       if svar.length() <> 2 then                                     |86
|         do                                                           |87
|           ll = svar.length() - 2                                     |88
|           oo = svar.substr(3,ll)                                     |89
|           instr = '-'oo instr                                        |90
|         end                                                          |91
|     end                                                              |92
|     argumentlist = outstr.space()                                    |93
|                                                                      |94
+----------------------------------------------------------------------+
                                             xstring.nrx(Method:cmdline)
Download the complete source for the xstring.nrx library

 
+----------------------------------------------------------------------+
| -- method......: option                                              |95
| -- purpose.....:                                                     |96
| --                                                                   |97
|   method option(in=Rexx) public                                      |98
|     out = value[in]                                                  |99
|     return out                                                       |00
|                                                                      |01
+----------------------------------------------------------------------+
                                              xstring.nrx(Method:option)
Download the complete source for the xstring.nrx library

Additional examples

This two additional examples should clarify what we did.

 
+----------------------------------------------------------------------+
| -- test for the cmdline class                                        |01
| --                                                                   |02
| -- we allow 2 options:                                               |03
| --   -t  (TRACE)  flag default to 0                                  |04
| --   -o  (OUTFID) variable defaulted to test.out                     |05
| --                                                                   |06
| parse arg argsl                                                      |07
|                                                                      |08
| cl = cmdline(argsl,'t/FLA/TRACE/0'           -                       |09
|                    'o/VAR/OUTFID/test.out')                          |10
| say 'The arguments are:' cl.arguments()'.'                           |11
| if cl.option('TRACE')                                                |12
|   then say 'Tracing is ON'                                           |13
|   else say 'Tracing is OFF'                                          |14
| say 'The output file is:' cl.option('OUTFID')                        |15
|                                                                      |16
| exit 0                                                               |17
+----------------------------------------------------------------------+
                                                                tcl1.nrx
Download the source for the tcl1.nrx example

 
+----------------------------------------------------------------------+
| -- another test                                                      |01
| --                                                                   |02
| class tcl2                                                           |03
|   properties public                                                  |04
|                                                                      |05
|   method tcl2() public                                               |06
|                                                                      |07
|   method main(ar=String[]) public static                             |08
|     argsl = xstring.a2s(ar)                                          |09
|                                                                      |10
|     -- test for the cmdline class                                    |11
|     --                                                               |12
|     -- we allow 2 options:                                           |13
|     --   -r  (REPLACE)   flag default to 0                           |14
|     --   -T  (TESTLEVEL) variable defaulted to 0                     |15
|     --                                                               |16
|     cl = cmdline(argsl,'r/FLA/REPLACE/0'           -                 |17
|                        'T/VAR/TESTLEVEL/0')                          |18
|     say 'The arguments are:' cl.arguments()'.'                       |19
|     if cl.option('REPLACE')                                          |20
|       then say 'Replace is ON'                                       |21
|       else say 'Replace is OFF'                                      |22
|     say 'The testlevel is:' cl.option('TESTLEVEL')                   |23
|                                                                      |24
|     exit 0                                                           |25
+----------------------------------------------------------------------+
                                                                tcl2.nrx
Download the source for the tcl2.nrx example

This chapter's tricks.

Getting the arguments from main()

As we have seen, the arguments in the main() method are passed as an array of string[].

This is clearly different from the approach we saw in Chapter 2 about the argument passing from the command line, where arg was returning a simple NetRexx string.

To get the arguments in the "right" way (i.e. the way you have been used to) you need to code an extra line:

 
   method main(args=String[]) public static
     arg = Rexx(args)    -- ADD THIS LINE
     parse arg p1 p2 .   -- THIS as usual
     --
 

The line:

 
  arg = Rexx(args)
 

instruct NetRexx to "translate" the array of string args into a single NetRexx variable string.

 
   args[0]   -+---( Rexx() )--> arg
   args[1]   -+
   (...)
   args[n]   -+
 
 
 *** This section is: 
  
 *** and will be available in next releases

Chapter Summary

 
 *** This section is: 
  
 *** and will be available in next releases


File: nr_11.html.

The contents of this WEB page are Copyright © 1997 by Pierantonio Marchesini / ETH Zurich.

Last update was done on 18 May 1998 21:47:45(GMT +2).