Thursday, June 4, 2009

Converting RPG-III to RPG-ILE

Hopefully you were able to complete the previous topic and compile a simple RPG III program. Though introduced in 1994, RPG IV is known as the "new" RPG. Because of the huge number of programs written in RPG III, the industry has been slow to adopt RPG IV. It is undoubtedly the way of the future on the AS/400.

RPG III is more accurately known as RPG/400 when it runs on an AS/400. RPG IV supports the "Integrated Language Environment" concept introduced in 1994. This is known as ILE and allows program modules to be bound together at compile time so that an RPG IV program can use routines written in C or any other language. So RPG III is essentially the same as RPG/400 and RPG IV is essentially the same as RPG ILE.

Before you go any further, read this short article:

Converting RPG-III to RPG-ILE

Now you are emotionally ready to convert your program TUTR001 to RPG IV.

First you must create a place for your program statements. Since RPG IV statements are longer than other program source statements, the command to create the source physical file is slightly different. To create the appropriately named place for RPG IV source:

CRTSRCPF FILE(USER999/QRPGLESRC) RCDLEN(112)

Of course, change the USER999 to your user ID. This command creates a source physical file named QRPGLESRC to hold your programs written in RPG IV. The name is the AS/400 standard for RPG IV or RPG ILE source code. It starts with "Q" followed by RPGLE (short for RPG ILE) and then SRC for source.

Now, use the AS400 command to convert the old program to RPG ILE. Key CVTRPGSRC (Convert RPG Source) and hit F4 to see the prompts. Your screen should look like:


Convert RPG Source (CVTRPGSRC)

Type choices, press Enter.

From file . . . . . . . . . . . Name
Library . . . . . . . . . . . *LIBL Name, *LIBL, *CURLIB
From member . . . . . . . . . . Name, generic*, *ALL
To file . . . . . . . . . . . . QRPGLESRC Name, *NONE, QRPGLESRC
Library . . . . . . . . . . . *LIBL Name, *LIBL, *CURLIB
To member . . . . . . . . . . . *FROMMBR Name, *FROMMBR
Now fill in the values to indicate that you want to convert the program located in source physical file QRPGSRC in library USER999 that is named TUTR001 and that you want the converted program to be in the same library with the name TUTR002.

Convert RPG Source (CVTRPGSRC

Type choices, press Enter.

From file . . . . . . . . . . . qrpgsrc Name
Library . . . . . . . . . . . user999 Name
From member . . . . . . . . . . tutr001 Name
To file . . . . . . . . . . . . QRPGLESRC Name
Library . . . . . . . . . . . user999 Name
To member . . . . . . . . . . . tutr002 Name
Hit ENTER and the command will copy and modify your program.

You can now use PDM to look at the converted program. From the PGM screen key in 3 and hit ENTER. Then fill in the screen to look like:


Specify Members to Work With

Type choices, press Enter.

File . . . . . . . . . . QRPGLESRC Name, F

Library . . . . . . . . USER999 *LIBL,

Member:
Name . . . . . . . . . *ALL *ALL
Type . . . . . . . . . *ALL *ALL
Hit enter and you will see your converted program in the list of programs:

Work with Members Using PDM

File . . . . . . QRPGLESRC
Library . . . . USER000 Position to . .

Type options, press Enter.
2=Edit 3=Copy 4=Delete 5=Display 6=Print
8=Display description 9=Save 13=Change text 14=Compile

Opt Member Type Text
TUTR002 RPGLE Change the first record in CUST
Notice that the "Type" is RPGLE. That tells the AS400 to compile this as RPG ILE instead of RPG III.

Key a 2 next to the program name (TUTR002) and hit ENTER and you will see the source code:


0001.00 FCUST UF E DISK
0002.00 C READ CUST 90
0003.00 C MOVE *BLANK CSADR1
0004.00 C MOVEL '456 OAK' CSADR1
0005.00 C UPDATE CSREC
0006.00 C MOVE *ON *INLR
0007.00 C RETURN
****************** End of data ****************************************

You can see that not much has changed. RPG IV has more room for operand names so the UPDAT and RETRN are now spelled out. The other values are spread out a bit more.

You can compile and run this program. You should try that now. Remember that you compile by using option 14. You can then CALL TUTR002 to run it.

Now copy this program and modify it to reflect the new coding style of RPG IV. Copy the program using PDM. Simply key 3 as the option:


Work with Members Using PDM

File . . . . . . QRPGLESRC
Library . . . . USER000 Position to . . . . .

Type options, press Enter.
2=Edit 3=Copy 4=Delete 5=Display 6=Print 7=Rename
8=Display description 9=Save 13=Change text 14=Compile 15=Create

Opt Member Type Text
3 TUTR002 RPGLE Change the first record in CUST file



When you hit ENTER, fill in TUTR003 as the new name:

Copy Members

From file . . . . . . . : QRPGLESRC
From library . . . . : USER000

Type the file name and library name to receive the copied members.

To file . . . . . . . . QRPGLESRC Name, F4 for list
To library . . . . . USER000

To rename copied member, type New Name, press Enter.

Member New Name
TUTR002 TUTR003
Now hit ENTER again and PDM will copy TUTR002 and name it TUTR003.

Edit TUTR003 by keying 2 as the option.

One way RPG IV is different is that lower case letters are allowed. This doesn't change the functionality of the program but it certainly makes it feel more modern.

You must tell SEU that you want to use lower case letters. When you are editing the TUTR003 program, hit F13 (well actually Shift-F1) and you will get the options for the editor:


Change Session Defaults

Type choices, press Enter.

Amount to roll . . . . . . . . . . . C H=Half, F=Full
C=Cursor, D=Data
1-999
Uppercase input only . . . . . . . . N Y=Yes, N=No
Tabs on . . . . . . . . . . . . . . N Y=Yes, N=No
Increment of insert record . . . . . 0.01 0.01-999.99
Full screen mode . . . . . . . . . . N Y=Yes, N=No

Source type . . . . . . . . . . . . RPGLE
Syntax checking:
When added/modified . . . . . . . Y Y=Yes, N=No
From sequence number . . . . . . . 0000.00-9999.99
To sequence number . . . . . . . . 0000.00-9999.99


Set records to date . . . . . . . . / / YY/MM/DD
It is best to change the first value to C. This affects how the screen scrolls when you PAGE-UP and PAGE-DOWN. Make sure the "Uppercase input only" selection is N. This allows you to key lowercase characters.

For now, make 2 changes in the style. First, use lower case characters when you want. Second, use the EVAL operand in place of MOVE and MOVEL. EVAL is short for EVALUATE and like the old BASIC "LET". It is not only more like other modern languages, it has other enhancements that make string handling easy.

Also, since there is more room, change the address to 456 OAK STREET.

The modified program should look like:


0001.00 FCUST UF E disk
0002.00 C Read CUST 90
0004.00 C Eval CSADR1 = '456 OAK STREET'
0005.00 C Update CSREC
0006.00 C Eval *inlr = *on
0007.00 C Return
Since the EVAL instruction works a little differently from the MOVE, it is no longer necessary to move blanks to CSADR1 before moving 456 OAK STREET to it.

Now compile and run the program. After running TUTR003 you can look at the file with:

RUNQRY QRYFILE(CUST)

and see that the address has changed to 456 OAK STREET.

Boy, in just 10 minutes you have learned a new language!

You should be getting more comfortable with PDM, SEU, compiling programs and looking at spool files. If so, you are on your way to being a productive programmer.

Remember that all examples are in the USER000 library for your viewing pleasure.

Methods

Chapter 5Methods appear inside class bodies. They contain local variable declarations and other Java statements that are executed by a calling thread when the method is invoked. Method declarations in Java look like ANSI C-style function declarations with two restrictions: a method in Java always specifies a return type (there's no default). The returned value can be a primitive type, a reference type, or the type void , which indicates no returned value. Next, a method always has a fixed number of arguments. The combination of method overloading and true arrays reduces the need for a variable number of arguments, as offered in some languages.

Here's a simple example:

class Bird {       int xPos, yPos;          double fly ( int x, int y ) {           double distance = Math.sqrt( x*x + y*y );           flap( distance );           xPos = x;           yPos = y;           return distance;       }       ...   }

In this example, the class Bird defines a method, fly(), that takes as arguments two integers: x and y. It returns a double type value as a result, using the return keyword.

5.2.1 Local Variables

The fly() method declares a local variable called distance, which it uses to compute the distance flown. A local variable is temporary; it exists only within the scope of its method. Local variables are allocated and initialized when a method is invoked; they are normally destroyed when the method returns. They can't be referenced from outside the method itself. If the method is executing concurrently in different threads, each thread has its own copies of the method's local variables. A method's arguments also serve as local variables within the scope of the method.

An object created within a method and assigned to a local variable may or may not persist after the method has returned. As with all objects in Java, it depends on whether any references to the object remain. If an object is created, assigned to a local variable, and never used anywhere else, that object is no longer referenced when the local variable is destroyed, so garbage collection removes the object. If, however, we assign the object to an instance variable, pass it as an argument to another method, or pass it back as a return value, it may be saved by another variable holding its reference. We'll discuss object creation and garbage collection in more detail shortly.

5.2.2 Shadowing

If a local variable and an instance variable have the same name, the local variable shadows or hides the name of the instance variable within the scope of the method. In the following example, the local variables xPos and yPos hide the instance variables of the same name:

class Bird {       int xPos, yPos;       int xNest, yNest;       ...       double flyToNest( ) {           int xPos = xNest;           int yPos = yNest:           return ( fly( xPos, yPos ) );       }       ...   }

When we set the values of the local variables in flyToNest(), it has no effect on the values of the instance variables.

5.2.2.1 The "this" reference

You can use the special reference this any time you need to refer explicitly to the current object. Often you don't need to use this, because the reference to the current object is implicit; such is the case when using unambiguously named instance variables inside a class. But we can use this to refer explicitly to instance variables in our object, even if they are shadowed. The following example shows how we can use this to allow argument names that shadow instance variable names. This is a fairly common technique, because it saves your having to make up alternative names. Here's how we could implement our fly() method with shadowed variables:

class Bird {       int xPos, yPos;          double fly ( int xPos, int yPos ) {           double distance = Math.sqrt( xPos*xPos + yPos*yPos );         flap( distance );           this.xPos = xPos;           this.yPos = yPos;           return distance;       }       ...   }

In this example, the expression this.xPos refers to the instance variable xPos and assigns it the value of the local variable xPos, which would otherwise hide its name. The only reason we need to use this in the previous example is because we've used argument names that hide our instance variables, and we want to refer to the instance variables. You can also use the this reference any time you want to pass a reference to your object to some other method; we'll show examples of that later.

5.2.3 Static Methods

Static methods (class methods), like static variables, belong to the class and not to an individual instance of the class. What does this mean? Well, foremost, a static method lives outside of any particular class instance. It can be invoked by name, through the class name, without any objects around. Because it is not bound to a particular object instance, a static method can directly access only other static members of the class. It can't directly see any instance variables or call any instance methods, because to do so we'd have to ask, "on which instance?" Static methods can be called from instances, just like instance methods, but the important thing is that they can also be used independently.

Our fly() method uses a static method: Math.sqrt(), which is defined by the java.lang.Math class; we'll explore this class in detail in Chapter 10. For now, the important thing to note is that Math is the name of a class and not an instance of a Math object. (It so happens that you can't even make an instance of the Math class.) Because static methods can be invoked wherever the class name is available, class methods are closer to C-style functions. Static methods are particularly useful for utility methods that perform work that is useful either independently of instances or in creating instances. For example, in our Bird class, we could enumerate all types of birds that can be created:

class Bird {       ...       static String [] getBirdTypes( ) {        String [] types;        // Create list...          return types;       }       ...   }

Here we've defined a static method getBirdTypes(), which returns an array of strings containing bird names. We can use getBirdTypes() from within an instance of Bird, just like an instance method. However, we can also call it from other classes, using the Bird class name as a reference:

String [] names = Bird.getBirdTypes( );

Perhaps a special version of the Bird class constructor accepts the name of a bird type. We could use this list to decide what kind of bird to create.

Static methods also play an important role in various design patterns, where you limit the use of the new operator for a class to one method, a static method called a factory method. We'll talk more about object construction later. But suffice it to say that it's common to see usage like this:

Bird bird = Bird.getBird( "pigeon" );

5.2.4 Initializing Local Variables

In the flyToNest() example, we made a point of initializing the local variables xPos and yPos. Unlike instance variables, local variables must be initialized before they can be used. It's a compile-time error to try to access a local variable without first assigning it a value:

void myMethod( ) {       int foo = 42;       int bar;          bar += 1;  // compile-time error, bar uninitialized        bar = 99;       bar += 1;  // OK here  }

Notice that this doesn't imply local variables have to be initialized when declared, just that the first time they are referenced must be in an assignment. More subtle possibilities arise when making assignments inside conditionals:

void myMethod {     int foo;     if ( someCondition ) {       foo = 42;       ...     }     foo += 1;   // Compile-time error, foo may not be initialized }

In this example, foo is initialized only if someCondition is true. The compiler doesn't let you make this wager, so it flags the use of foo as an error. We could correct this situation in several ways. We could initialize the variable to a default value in advance or move the usage inside the conditional. We could also make sure the path of execution doesn't reach the uninitialized variable through some other means, depending on what makes sense for our particular application. For example, we could return from the method abruptly:

int foo;   ...   if ( someCondition ) {       foo = 42;       ...   } else       return;        foo += 1;

In this case, there's no chance of reaching foo in an uninitialized state, so the compiler allows the use of foo after the conditional.

Why is Java so picky about local variables? One of the most common (and insidious) sources of errors in C or C++ is forgetting to initialize local variables, so Java tries to help us out. If it didn't, Java would suffer the same potential irregularities as C or C++.[2]

5.2.5 Argument Passing and References

Let's consider what happens when you pass arguments to a method. All primitive data types (e.g., int, char, float) are passed by value. By now you're probably used to the idea that reference types (i.e., any kind of object, including arrays and strings) are used through references. An important distinction is that the references themselves (the pointers to these objects) are actually primitive types and are passed by value too.

Consider the following piece of code:

...       int i = 0;       SomeKindOfObject obj = new SomeKindOfObject( );       myMethod( i, obj );       ...   void myMethod(int j, SomeKindOfObject o) {       ...   }

The first chunk of code calls myMethod(), passing it two arguments. The first argument, i, is passed by value; when the method is called, the value of i is copied into the method's parameter, j. If myMethod() changes the value of j, it's changing only its copy of the local variable.

In the same way, a copy of the reference to obj is placed into the reference variable o of myMethod(). Both references refer to the same object, so any changes made through either reference affect the actual (single) object instance. If we change the value of, say, o.size, the change is visible either as o.size (inside myMethod()) or as obj.size (in the calling method). However, if myMethod() changes the reference o itself—to point to another object—it's affecting only its local variable reference. It doesn't affect the caller's variable obj, which still refers to the original object. In this sense, passing the reference is like passing a pointer in C and unlike passing by reference in C++.

What if myMethod() needs to modify the calling method's notion of the obj reference as well (i.e., make obj point to a different object)? The easy way to do that is to wrap obj inside some kind of object. For example, we could wrap the object up as the lone element in an array:

SomeKindOfObject [] wrapper = new SomeKindOfObject [] { obj };

All parties could then refer to the object as wrapper[0] and would have the ability to change the reference. This is not aesthetically pleasing, but it does illustrate that what is needed is the level of indirection.

Another possibility is to use this to pass a reference to the calling object. In that case, the calling object serves as the wrapper for the reference. Let's look at a piece of code that could be from an implementation of a linked list:

class Element {       public Element nextElement;          void addToList( List list ) {           list.addToList( this );       }   }      class List {       void addToList( Element element ) {           ...           element.nextElement = getNextElement( );       }   }

Every element in a linked list contains a pointer to the next element in the list. In this code, the Element class represents one element; it includes a method for adding itself to the list. The List class itself contains a method for adding an arbitrary Element to the list. The method addToList() calls addToList() with the argument this (which is, of course, an Element). addToList() can use the this reference to modify the Element's nextElement instance variable. The same technique can be used in conjunction with interfaces to implement callbacks for arbitrary method invocations.

5.2.6 Method Overloading

Method overloading is the ability to define multiple methods with the same name in a class; when the method is invoked, the compiler picks the correct one based on the arguments passed to the method. This implies that overloaded methods must have different numbers or types of arguments. (In Chapter 6, we'll look at method overriding, which occurs when we declare methods with identical signatures in different classes.)

Method overloading (also called ad-hoc polymorphism) is a powerful and useful feature. The idea is to create methods that act in the same way on different types of arguments. This creates the illusion that a single method can operate on any of the types. The print() method in the standard PrintStream class is a good example of method overloading in action. As you've probably deduced by now, you can print a string representation of just about anything using this expression:

System.out.print( argument ) 

The variable out is a reference to an object (a PrintStream) that defines nine different, "overloaded" versions of the print() method. The versions take arguments of the following types: Object, String, char[], char, int, long, float, double, and boolean.

class PrintStream {       void print( Object arg ) { ... }       void print( String arg ) { ... }       void print( char [] arg ) { ... }       ...   }

You can invoke the print() method with any of these types as an argument, and it's printed in an appropriate way. In a language without method overloading, this requires something more cumbersome, such as a uniquely named method for printing each type of object. Then it's your responsibility to remember what method to use for each data type.

In the previous example, print() has been overloaded to support two reference types: Object and String. What if we try to call print() with some other reference type? Say, perhaps, a Date object? When there's not an exact type match, the compiler searches for an acceptable, assignable match. Since Date, like all classes, is a subclass of Object, a Date object can be assigned to a variable of type Object. It's therefore an acceptable match, and the Object method is selected.

What if there's more than one possible match? Say, for example, we tried to print a subclass of String called MyString. (The String class is final, so it can't be subclassed, but please allow this brief transgression for purposes of explanation.) MyString is assignable to either String or to Object. Here the compiler makes a determination as to which match is "better" and selects that method. In this case, it's the String method.

The intuitive explanation is that the String class is closer to MyString in the inheritance hierarchy. It is a more specific match. A more rigorous way of specifying it would be to say that a given method is more specific than another method if the argument types of the first method are all assignable to the argument types of the second method. In this case, the String method is more specific to a subclass of String than the Object method because type String is assignable to type Object. The reverse is not true.

If you're paying close attention, you may have noticed we said that the compiler resolves overloaded methods. Method overloading is not something that happens at runtime; this is an important distinction. It means that the selected method is chosen once, when the code is compiled. Once the overloaded method is selected, the choice is fixed until the code is recompiled, even if the class containing the called method is later revised and an even more specific overloaded method is added. This is in contrast to overridden methods, which are located at runtime and can be found even if they didn't exist when the calling class was compiled. We'll talk about method overriding later in the chapter.

One last note about overloading. In earlier chapters, we've pointed out that Java doesn't support programmer-defined overloaded operators and that + is the only system-defined overloaded operator. If you've been wondering what an overloaded operator is, we can finally clear up that mystery. In a language like C++, you can customize operators such as + and * to work with objects that you create. For example, you could create a class Complex that implements complex numbers and then overload methods corresponding to + and * to add and multiply Complex objects. Some people argue that operator overloading makes for elegant and readable programs, while others say it's just "syntactic sugar" that makes for obfuscated code. The Java designers clearly espoused the latter opinion when they chose not to support programmer-defined overloaded operators.

Accessing Fields and Methods In Java

Chapter 5Inside a class, we can access variables and call methods of the class directly by name. Here's an example that expands upon our Pendulum:
class Pendulum {       ...       void resetEverything( ) {           mass = 1.0;         length = 1.0;         cycles = 0;         ...           float startingPosition = getPosition( 0.0 );       }       ...   }

Other classes access members of an object through a reference, using the (C-style) dot notation:

class TextBook {       ...       void showPendulum( ) {           Pendulum bob = new Pendulum( );           ...           int i = bob.cycles;           bob.resetEverything( );           bob.mass = 1.01;           ...       }       ...   }

Here we have created a second class, TextBook, that uses a Pendulum object. It creates an instance in showPendulum() and then invokes methods and accesses variables of the object through the reference bob.

Several factors affect whether class members can be accessed from "outside" the class (from another class). You can use the visibility modifiers public, private, and protected to control access; classes can also be placed into a package, which affects their scope. The private modifier, for example, designates a variable or method for use only by other members of the class itself. In the previous example, we could change the declaration of our variable cycles to private:

class Pendulum {       ...       private int cycles;       ...

Now we can't access cycles from TextBook:

class TextBook {       ...       void showPendulum( ) {           ...           int i = bob.cycles;  // Compile time error

If we still need to access cycles in some capacity, we might add a public getCycles() method to the Pendulum class. We'll take a detailed look at packages, access modifiers, and how they affect the visibility of variables and methods in Chapter 6.

5.1.2 Static Members

As we've said, instance variables and methods are associated with and accessed through an instance of the class—i.e., through a particular object. In contrast, members that are declared with the static modifier live in the class and are shared by all instances of the class. Variables declared with the static modifier are called static variables or class variables; similarly, these kinds of methods are called static methods or class methods. We can add a static variable to our Pendulum example:

class Pendulum {       ...       static float gravAccel = 9.80;       ...

We have declared the new float variable gravAccel as static. That means if we change its value in any instance of a Pendulum, the value changes for all Pendulum objects, as shown in Figure 5-2.

Figure 5-2. Static variables shared by all instances of a class

figs/LJ2.0502.gif

Static members can be accessed like instance members. Inside our Pendulum class, we can refer to gravAccel, like an instance variable:

class Pendulum {       ...       float getWeight ( ) {           return mass * gravAccel;       }       ...   }

However, since static members exist in the class itself, independent of any instance, we can also access them directly through the class. We don't need a Pendulum object to set the variable gravAccel; instead we can use the class name in place of a reference-type variable:

Pendulum.gravAccel = 8.76;

This changes the value of gravAccel for any current or future instances. Why would we want to change the value of gravAccel? Well, perhaps we want to explore how pendulums would work on different planets. Static variables are also very useful for other kinds of data shared among classes at runtime. For instance, you can create methods to register your objects so that they can communicate, or you can keep track of references to them. It's also common to use static variables to define constant values. In this case, we use the static modifier along with the final modifier. So, if we cared only about pendulums under the influence of the Earth's gravitational pull, we could change Pendulum as follows:

class Pendulum {       ...       static final float EARTH_G = 9.80;       ...

We have followed a common convention and named our constant with capital letters. Now the value of EARTH_G is a constant; it can be accessed by any instance of Pendulum (or anywhere, for that matter), but its value can't be changed at runtime.

It's important to use the combination of static and final only for things that are really constant. That's because the compiler is allowed to "inline" such values within classes that reference them. This means that if you change a static final variable you may have to recompile all code that uses that class (this is really the only case where you have to do that in Java). Static members are useful as flags and identifiers, which can be accessed from anywhere. They are especially useful for values needed in the construction of an instance itself. In our example, we might declare a number of static values to represent various kinds of Pendulum objects:

class Pendulum {       ...       static int SIMPLE = 0, ONE_SPRING = 1, TWO_SPRING = 2;       ...

We might then use these flags in a method that sets the type of a Pendulum or, more likely, in a special constructor, as we'll discuss shortly:

Pendulum pendy = new Pendulum( );   pendy.setType( Pendulum.ONE_SPRING );

Again, inside the Pendulum class, we can use static members directly by name, as well; there's no need for the Pendulum. prefix:

class Pendulum {       ...       void resetEverything( ) {           setType ( SIMPLE );           ...       }       ...   } 

Objects in Java

Chapter 5

In this chapter, we get to the heart of Java and explore the object-oriented aspects of the language. The term object-oriented design refers to the art of decomposing an application into some number of objects, self-contained application components that work together. The goal is to break your problem down into a number of smaller problems that are simpler and easier to handle and maintain. Object-based designs have proven themselves over the years, and object-based languages such as Java provide a strong foundation for writing very small to very large applications. Java was designed from the ground up to be an object-oriented language, and all the Java APIs and libraries are built around solid object-based design patterns.

An object design "methodology" is a system or a set of rules created to help you break down your application into objects. Often this means mapping real-world entities and concepts (sometimes called the "problem domain") into application components. Various methodologies attempt to help you factor your application into a good set of reusable objects. This is good in principle, but the problem is that good object-oriented design is still more art than science. While you can learn from the various off-the-shelf design methodologies, none of them will help you in all situations. The truth is that there is no substitute for experience.

We won't try to push you into a particular methodology here; there are shelves full of books to do that.[1]

Instead, we'll just provide some common sense hints to get you started. The following general design guidelines will hopefully make more sense after you've read this and the next chapter.

  • Hide as much of your implementation as possible. Never expose more of the internals of an object than you have to. This is key to building maintainable, reusable code. Avoid public variables in your objects, with the possible exception of constants. Instead define accessor methods to set and return values (even if they are simple types). Later, when you need to, you'll be able to modify and extend the behavior of your objects without breaking other classes that rely on them.

  • Specialize objects only when you have to—use composition instead of inheritance. When you use an object in its existing form, as a piece of a new object, you are composing objects. When you change or refine the behavior of an object (by subclassing), you are using inheritance. You should try to reuse objects by composition rather than inheritance whenever possible, because when you compose objects, you are taking full advantage of existing tools. Inheritance involves breaking down the barrier of an object and should be done only when there's a real advantage. Ask yourself if you really need to inherit the whole public interface of an object (do you want to be a "kind" of that object) or if you can just delegate certain jobs to the object and use it by composition.

  • Minimize relationships between objects and try to organize related objects in packages. Objects that work closely together can be grouped using Java packages, which can hide those that are not of general interest. Think about how much work it would take to make your objects generally useful, outside of your current application. You may save yourself a lot of time later.

Chapter 5

Classes

Classes are the building blocks of a Java application. A class can contain methods (functions), variables, initialization code, and, as we'll discuss later on, even other classes. It serves as a blueprint for making class instances, which are runtime objects that implement the class structure. You declare a class with the class keyword. Methods and variables of the class appear inside the braces of the class declaration:

class Pendulum { 

float mass;
float length = 1.0;
int cycles;

float getPosition ( float time ) {
...
}
...
}

The Pendulum class contains three variables: mass, length, and cycles. It also defines a method called getPosition(), which takes a float value as an argument and returns a float value as a result. Variables and method declarations can appear in any order, but variable initializers can't make "forward references" to other variables that appear later. Once we've defined the Pendulum class, we can create a Pendulum object (an instance of that class) as follows:

Pendulum p; 

p = new Pendulum( );

Recall that our declaration of the variable p doesn't create a Pendulum object; it simply creates a variable that refers to an object of type Pendulum. We still have to create the object, using the new keyword. Now that we've created a Pendulum object, we can access its variables and methods, as we've already seen many times:

p.mass = 5.0; 

float pos = p.getPosition( 1.0 );

Two kinds of variables can be defined in a class: instance variables and static variables . Every object instance has its own set of instance variables; the values of these variables in one object can differ from the values in another object. We'll talk about static variables later, which, in contrast, are shared among all instances of an object. In either case, if you don't initialize a variable when you declare it, it's given a default value appropriate for its type (null, zero, or false).

Figure 5-1 shows a hypothetical TextBook application that uses two instances of Pendulum through the reference-type variables bigPendulum and smallPendulum. Each of these Pendulum objects has its own copy of mass, length, and cycles. As with variables, methods defined in a class may be instance methods or static methods. An instance method is associated with an instance of the class, but the relationship isn't quite as simple as for variables. Instance methods are accessed through an object instance, but the object doesn't have its own copy of the methods (there is no duplication of code). Instead, the association means that instance methods can "see" and operate on the values of the instance variables of the object. As you'll see in Chapter 6 when we talk about subclassing; there's more to learn about how methods see variables. In that chapter we'll also discuss how instance methods can be "overridden" in child classes—a very important feature of object-oriented design. Both aspect differ from static methods, which we'll see later are really more like global functions—associated with a class by name only.

Figure 5-1. Instances of the Pendulum class

figs/LJ2.0501.gif

Tuesday, May 19, 2009

Using PHP's Built-in Source Highlighter

PHP offer two functions for highlighting PHP code: highlight_file() and highlight_string(). Both functions return the same results, but each has its own specific input parameters. As their names suggest, the first function takes the filename of the PHP script whose code is to be highlighted, while the second takes the input as a string.

A Quick Example

Let's take a look at an example of the highlight_string() function in action:

include_once 'init.inc.php';

if (!empty($_POST['text'])){

echo '

';
highlight_string($_POST['text']);
echo '
';
}
?>

" method="post">




Monday, May 18, 2009

array to file

Code reads an array and writes array's php code to a file.
Inputs:array_to_file($array array,$arrayname string,$filename string)
Returns:none (writes file)


/* Array to File */
function __process_array($array,$indent)
{
$ret_str = "";
$first = true;
foreach ($array as $key => $value)
{

if (!$first)
$ret_str .= ",\n";
else
$first = false;

$ret_str .= str_repeat(" ",$indent);

if (is_array($value))
{
$ret_str .= "'$key' => array(\n".__process_array($value,$indent+5)."\n".str_repeat(" ",$indent).")";
}
elseif (is_string($value))
{
$ret_str .= "'$key' => '$value'";
}
elseif (is_int($value))
{
$ret_str .= "'$key' => $value";
}
elseif (is_bool($value))
{
$ret_str .= "'$key' => ".($value?"true":"false");
}
}
return $ret_str;
}

function array_to_file($array,$name,$filename)
{
$file_str = "
$file_str .= __process_array($array,6);
$file_str .= "\n );\n?>";

$file = fopen($filename,"w");
fwrite($file,$file_str);
fclose($file);
}
?>

Excel Format AS400

If you specify TOFMT(*XLS), CVTDBFSTMF will create an Excel (™) spreadsheet from your database file. The default is to create a BIFF 8 file (compatible with Excel 97, Excel 2000, Excel XP and above). Alternatively, you can choose to create a BIFF 5 file (compatible with Excel 5.0 and above) if your spreadsheet does not support the latest Excel file format.

Files created by CVTDBFSTMF in Excel format can be opened directly by any application with supports the Excel file format, including Microsoft Excel but many others besides, such as Lotus 123 and MS Works spreadsheet.

The Integrated File System (IFS)

#navbar-iframe {
display: none !important;
}

The iSeries Integrated File System (IFS) provides a coherent, coordinated set of file systems which can used for storing a variety of data physically on the iSeries or for communicating with file systems on other platforms.

These files systems include the following that may be of use to you for storing stream files created by CoolSpools or Slipstream locally on your iSeries or remotely on another computer (PC, UNIX server etc.)

"root" The "root" (/) file system. This file system takes full advantage of the stream file support and hierarchical directory structure of the integrated file system. The root file system has the characteristics of the Disk Operating System (DOS) and OS/2 file systems. You should typically use the root file system if you want to store stream files created by CoolSpools or Slipstream locally on the same iSeries where you run the CVTDBFSTMF command.

QNTC Windows NT Server file system. This file system provides access to data and objects that are stored on a PC running Windows NT 4.0 or higher. It allows iSeries server applications to use the same data as Windows NT clients. If you prefer not to store stream file locally on your iSeries, you can use QNTC to enable CoolSpools or Slipstream to write stream files directly to a PC running NT, Windows 2000, Windows XP etc. instead. This can be an effective way of sharing data created by CoolSpools or Slipstream amongst your users and customers.

NFS Network File System. This file system provides you with access to data and objects that are stored on a remote NFS server. An NFS server can export a network file system that NFS clients will then mount dynamically. This may be an option for sending stream files to a UNIX server.

QNetWare The QNetWare file system. This file system provides access to local or remote data and objects that are stored on a server that runs Novell NetWare 4.10 or 4.11 or to standalone PC Servers running Novell Netware 3.12, 4.10 4.11 or 5.0. You can dynamically mount NetWare file systems over existing local file systems.

QOpenSys The open systems file system. This file system is compatible with UNIX-based open system standards, such as POSIX and XPG. Like the root file system, this file system takes advantage of the stream file and directory support that is provided by the integrated file system. In addition, it supports case-sensitive object names.

QDLS The document library services file system (previously know as “shared folders”). This file system provides access to documents and folders. Use this file system only if you have applications which require it. QDLS is significantly slower and has major limitations (e.g. in relation to naming) compared with the root file system.

QFileSvr.400 The QFileSvr.400 file system provides access to other file systems that reside on remote iSeries servers. You can use QFileSvr.400 to save output from CoolSpools or Slipstream directly to another iSeries.

See http://publib.boulder.ibm.com/iseries/v5r1/ic2924/info/rzaia/rzaiacon.htm for full details of the Integrated File System.

We will now focus a little more closely on the QNTC File System since this is little known area of iSeries functionality which may well be of serious interest to users of CoolSpools and Slipstream.

QNTC

The QNTC file system is a subdivision of the IFS (Integrated File System) that enables the iSeries to access file and device shares (e.g. printers and CDROM) drives on a remote NT system. Please note that contrary to a commonly held fallacy this is NOT restricted to the Integrated NetFinity Server (aka IPCS or FSIOP).

Using the QNTC file system, your iSeries can read and write files that reside physically on a PC running Windows NT 4 or above. This means that CoolSpools or Slipstream can output stream files directly to an NT server if you would prefer to store them there rather than in the root file system of your IFS.

To use QNTC, the only software you need other than the base operating system is TCP/IP Connectivity Utilities for iSeries 400 (S722-TCl). However, setting up QNTC can be tricky. For full setup information, refer to the article in the IBM Software Knowledgebase at http://www-912.ibm.com/s_dir/slkbase.nsf/1ac66549a21402188625680b0002037e/aea450153eebf8ff8625670f0072550f?OpenDocument&Highlight=0,QNTC

However, here is a quick overview of the steps you need to follow to set up QNTC. This example relates to V5R1.

  1. First you must ensure that the domain name defined by your iSeries NetServer configuration matches your PC’s Windows network workgroup name. NetServer is the function on the iSeries that provides support for the Windows Network Neighbourhood. You can use Operations Navigator (OpsNav) to setup and manager NetServer. From the main OpsNav window, click the name of your machine, select File Systems, and then right-click File Shares. Choose Open iSeries NetServer from the menu to display the NetServer window. Right-click iSeries NetServer and choose Properties to display the Properties window, where you can change the domain. Click the General tab and press the Next Start button. A window appears where you can set properties that will be used the next time NetServer is restarted. In the domain name field, enter a name that matches the workgroup of the NT PC that you wish to access. Then end and restart NetServer by clicking the Stop icon followed (once NetServer has fully ended) by the green triangle icon.

  2. One other thing which must match between the iSeries and the NT PC is the user id and password you’re going to use. It is vital that your iSeries user id be recognised by the NT PC as a valid network logon id, and that the passwords are the same on both the iSeries and the NT machine. It may well be advisable to create a special user id on the two platforms specifically for the purpose of communicating between them using QNTC. You can then ensure that when the password needs to be changed, it is changed on both systems, if this needs to be done manually.

  3. On the PC (running Windows NT, 2000, XP etc.), select Start, Settings, Control Panel, and System. Then choose the Network Identification tab, click the Properties button, select Workgroup, and set the workgroup to match the domain that NetServer on the iSeries was configured to use. Note that you may have to reboot the PC for these changes to take effect. To test your connection, first ensure that you are logged on to the iSeries using a shared user id/password (see above), then run the command WRKLNK '/QNTC/*' on the iSeries. This could take several minutes to complete the first time it is run. Your NT system should appear on the list. If it doesn't, you may be able to manually establish a connection to the NT system using command CRTDIR /QNTC/ ‘, substituting the name of your NT system for servername. Find your NT system in this list and choose Display (option 5) in WRKLNK (Work with Object Links) to display your file shares on NT. If the file shares don't appear, double-check that your user IDs and passwords match exactly on both systems.

You access the QNTC file system by including QNTC and the name of your PC and share name in the path name you specify on TOSTMF (To Stream File) parameter of the CVTDBFSTMF command.

For example, let’s imagine you have a company server running Windows NT 4 or above and you have decided that this is a convenient place to save the output from CoolSpools or Slipstream so that all of your users can have shared access to the data. Let’s imagine that this server is called NTServer and that it has a file share name set up called NTFiles. Below the share name, there is a directory called CustData. If you wanted to convert data from your customer file and save it as an Excel spreadsheet in this location under the file name customer_file.xls, you would run a command something like this:

CVTDBFSTMF FROMFILE(custfile)

TOSTMF(‘/QNTC/NTServer/NTFiles/CustData/customer_file.xls’)

TOFMT(*XLS)

Slipstream AS400

Slipstream helps IBM iSeries sites deliver high-quality information to their customers and users more quickly and more simply than was possible previously.

Slipstream does this by providing tools for exporting iSeries database tables as PC-format stream files such as Excel spreadsheets, CSV, text and HTML. It also integrates with Query/400, QM/400 and SQl to let you save the output from those functions in Excel and other formats

Do you run queries and still produce the output on paper and have to distribute it through the mail? With Slipstream, without changing your query and with just a single extra command, you you can run the query, save the output as a native Excel spreadsheet and email it to your users or save it on a server for easy access!

The iSeries database (UDB for iSeries) is an advanced, function-rich, easily managed relational database allowing large quantities of data to be stored securely and retrieved and updated rapidly and efficiently. However, today, many of our customers and users want to be able to access and manipulate the data held in that database using tools such as spreadsheets, PC databases and Business Intelligence tools. Enabling their customers and users to access data held on the iSeries in the form in which they need it is a challenge that many iSeries sites face on daily basis.

Slipstream provides a innovative solution to this problem. With Slipstream you can create PC-format files from your iSeries database files right on the iSeries itself. You can also turn the laborious process of running a query and getting the output into a spreadsheet into a simple one-step operation.

Let's have a closer look at why your should choose Slipstream.