Friday, October 14, 2011

Book recommendation - Clean Code

Motivation

I'm reading book Clean Code - A Handbook of Agile Software Craftsmanship. I was given advice from my college that this is very useful book. This book should teach you what actually works in practice, as opposite to what might work in theory. From it you can learn how to write cleaner code in order to call yourself a professional, because only true professional write clean unambiguous (easy to read) code. I think that every programmer should push him/her self to become better programmer. Programming is hard and writing good code is also hard, but learning writing clean code can be beneficiary in long run. As a good professional you should always think that code you write will be read by some else (or maybe you) in future. Also this code should be easy to follow by other professional programmer.

Quote from this book: "In general programmers are pretty smart people. Smart people sometimes like to show off their smarts by demonstrating their mental juggling abilities. After all, if you can reli-ably remember that r is the lower-cased version of the url with the host and scheme removed, then you must clearly be very smart.One difference between a smart programmer and a professional programmer is that the professional understand that clarity is king. Professionals use their powers for good and write code that others can understand."

Tuesday, September 27, 2011

Groovy differences from Java (2/2 part)

Here we will continue to describe differences between Groovy and Java. In this part we will describe more important concepts that separates Groovy from Java: closures and Groovy's dynamic nature.

Closures

Closures are something you will come across if you ever code in any functional language (Scheme, LISP and even JavaScript). Because in functional language everything is function, you pass these functions to another functions as arguments. Also you can give these function a name which will provide means for using name to refer to this function. When you have defined name you can also pass it to another function.

Why would you want to to this you may ask? Well, did you ever use template method design pattern. This design pattern shows you how you can defer implementation steps of some interface to subclasses and you can dynamically use whatever implementation you think is suited best at that moment. You are "injecting" some code to another code. This is what closures do. They "capture" some code and then you can reuse this code at other places. I know this is little bit confusing right now and my half-explanation is not helping at all. So here is how Groovy does it:

You are probably familiar with sort() method in Java. So here is example of code for sorting array in Java and in Groovy:

// We will define some list.
List cars = ["Skoda", "Honda", "Toyota", "Audi"]
// This is how Groovy sort this list.
// Bit behind sort function in within curly
// braces is closure. What it basically do is
// inserting some code (in this example 
// a.compareTo(b)) to sort function. That
// is what closures do. They encapsulate code.
// a and b are arguments to closure, bit behind
// -> is closure body.
cars.sort {String a, String b -> a.compareTo(b) }

println cars
// [Audi, Honda, Skoda, Toyota]

// In Java you have you must implement Comparator
// interface and override compare method. 
// We do this mainly through anonymous classes in
// Java. Should be familiar.
Collections.sort(cars, new Comparator() {
    public int compare(String a, String b) {
        return a.compareTo(b)
    }    
});

println cars
// [Audi, Honda, Skoda, Toyota]

// Another most common use of closure in
// Groovy is for .each method which will 
// loop through all elements in lists,maps,...
// This closure does not use argument, it 
// uses special variable "it" which represent
// single value from this list.
int sum = 0
[2, 3, 4].each { sum += it }
println sum
// 9

In Groovy you can also assign closure to variable and then pass them around (like function in functional language).
Closure comparator = {Integer a, Integer b ->
    a.compareTo(b)
}
// Passing a closure to method looks like
// normal method call.
[7, 1, 6, 7].sort(comparator)
// [1, 6, 7, 7]

Closures are effectively anonymous functions, but more powerful.

Dynamic programming

In Java all variables, properties, method arguments and method returns must have declared types. If you want to do some real dynamic things you can do it through reflections which is OK, but it is not really elegant way of doing things.

Groovy address this issue and brings dynamic behavior to Java world. But what is all this dynamic stuff anyway?

Imagine that you have a Person class with some properties (firstName, lastName for example). You can easily sort a list of Persons object like so:
// Using usual way of sorting stuff in Java
// by implementing Comparator...
public void sortPeopleByGivenName(List personList) {
    Collections.sort(personList, new Comparator() {
        public int compare(Person p1, Person p2) { 
            return p1.getFirstName().compareTo(p2.getFirstName());
        }
    } ) ;
}

But what if you don't know which property for sorting you will use until run time. You can do some magic with reflection here, but that is always bad idea because it looks odd, also you can do some "if-stuff", but what if you have 20 properties in your domain class...

Here is where you can use Groovy's dynamism and simply define property as String that will be resolved at runtime to some concrete property. This is called dynamic dispatch and it basically mean that groovy will "find" appropriate property at runtime (similar like dynamic binding is looking for appropriate method at runtime).
For example:
// Accessing property by "string".
// This is only possible in dynamic languages
// languages because properties and methods are resolved
// at run time (Duck Typing)
def sortPeople(people, property) {
    people.sort { p1, p2 
     -> p1."${property}" <=> p2."${property}" }
}
Groovy uses def keyword to specify untyped methods and variables. It is important to note that Groovy support declared types (because of Java heritage) and that is one of the main strengths of Groovy (that and Java background off course).

Upload files with Grails

I found several examples of uploading files with Grails but neither one works for me. So here is snippet of code that is actually working (at lease for me). Maybe someone will find it useful.

Controller method look like this:
// inputTagName shoud be "myFile"
public MultipartFile readUploadFile(HttpServletRequest request, 
String inputTagName) {
  return request.getFile(inputTagName)  
}

Here is GSP page snippet:

  
  

Sunday, September 25, 2011

Groovy differences from Java (1/2 part)

What is Groovy all about?

Groovy is a dynamic programming language that's running on JVM (meaning that it is compiled to java byte-code and then it run on Java Virtual Machine).
Read here more about it features and also you can download it from same place.

When you unpack/install it, you can use groovy console for writing groovy scripts and to play with it. Console is located under : "$GROOVY_HOME/bin/groovyConsole".

Groovy essentially should provide more productivity compared to Java (and probably compared to other main stream OO languages) and it is damn good at it as it turn out...
Simple hello world in groovy (no main method or anything required).
println "Hello programmers!"

Basic differences

Note that almost all Java code is valid groovy code and you can mix & match java and groovy in your coding.
  • Firstly you get more default imports in groovy (java.io, java.math, java.util, java.net and groovy.lang and groovy.util). Reasons for this are obvious (70% of Java classes imports some of these packages).
  • Both null values and empty collections evaluates to false in groovy conditional statements. For example:
    Map map = new HashMap();
    // In Java you must do following for example:
    if (map != null && map.size() > 0) {
      ...
    }
    // In groovy this is enough:
    if (!map) {
      ...
    }
    
    Empty string also evaluates to false.
  • public is default class,fields,constant,methods visibility as compared to java package default visibility.
  • You don't have to catch checked exceptions as opposition to Java where compiler forces you to catch them. Is not catching these exception good practice? Well it makes code cleaner and also remove false security, but it really depends of your problem domain.

Silent helpers

There are couple of things that help you be more productive in groovy. They basically removes some of the non-necessary syntax from Java equivalents and replace it with smart defaults.
  • Semicolons who need them? In Groovy you by default do not need to type semicolons. But if you want to put more statements on one line, you still must separate them with semicolons.
  • Parentheses who need them? But you still need to use them. :) There are only couple of places you don't need them (println command for example or closures - more on closures on second part).
  • Return statement. You may be surprised, but you don't need to use return statement in non void methods. Groovy will return result from last expression evaluated. For example:
    private boolean something() {
        "a" == "b"
    }
    // Will return "false".
    
    But it is advisable to use return statement because of readability issues.

Properties (fields)

Groovy allows field access notation to Java bean properties (or fields whatever you like better). No need to define all that getters and setters for private fields, groovy all that handle behind the scenes. All you need to do to make this work is to leave default visibility on your fields (or properties) and you good to go. Examples:
Date date = new Date()
// You can access all Java beans properties
// using field name.
println date.time
// prints some long number...

class MyClass {
    
    // Default visibility for fields is private
    // but groovy generate setters and getters
    // behind scene.
    String firstName
    String lastName
    
    // Compiles without problem.   
    public static void access() {
        MyClass myClass = new MyClass()
        myClass.firstName = "John"
    }
    
}

Constructors

In Groovy you can instantiate bean using list of named arguments. This enables you to effectively treat maps as objects you can pass around and everything. For example:
// Here we create new SimpleDateFormat and setting default
// values to some of it properties.
SimpleDateFormat format = 
new SimpleDateFormat(lenient:false, numberFormat: "ddMMyyyy")

Added operations
Groovy introduced some new operations that do not exist in Java.
  • Null safe (?.) object navigation is basically null safe dot (.) operator. For example:
    // In Java if you have an object named "obj" that
    // encapsulate object named "field" you must do 
    // following to check if "field" is null:
    if (obj != null && obj.field != null) {
      ...
    }
    
    // In groovy U can use null safe operator
    // for same check and get same result:
    if (obj?.value != null) {
      ..
    }
    
    No more java.lang.NullPointerException! Great!
  • Elvis operator (?:) is basically shortened version of Java ternary (if-then-else) operator. It is called the ‘Elvis Operator’ due to its resemblance of Elvis’ trademark hair! :)
    For example:
    // When you use ternary operator in Java
    // U normally code something like this:
    Date date = (row.getDateFromDatabase() != null) ? 
                 row.getDateFromDatabase() : new Date();
    // In groovy U can get same result by using elvis:
    Date date = row.getDateFromDatabase ?: new Date()
    // It knows that you want to check for null values ;)
    
  • Spread-dot opeator (*.) operator calls a method on every item in collection. For example:
    // This will create new List wit all employees salaries.
    List employees = allEmployees*.getSalary()
    
    You don't need anymore to loop list only to extract single properties in another list. Great and Groovy stuff! :)
  • Comparisons with a spaceship (<=>) is used when you implement Comparable interface. In Java you must read API specification in order to properly implement int compare method (return negative is values i smaller, return positive if it is bigger or zero otherwise). Groovy makes our lives easier by providing operator that do exactly this:
    // This method will properly compare 
    // (in correspondence to Java API) two integers.
    public int compare(int number1, int number2) {
      return number1 <=> number2
    }
    
  • Equals (==) in Groovy equivalent of Java equals() method. It is null safe so it will work on null values (it will not throw NPE). Important to note is that if object implement Comparable interface, than it uses compareTo() method rather than equals.
    You can use is() method for identity if you are wondering.

Basic types in Groovy
In Groovy U can have access to all primitive Java types, but also you can use some Groovy specific type. Beside that you can also benefit from Groovy's syntactic sugar when working with basic types.
  • Numbers. Groovy threat primitive types as objects so you can call methods on them.
    // This is perfectly legal in Groovy
    222222.abs()
    // Although not so useful in this particular
    // example.
    
    Groovy uses BigDecimal for float-point calculations, it is maybe way slower than long and double, but it's removes quirks and quarks you have to deal with when working with float and double.
  • Strings. You can use Java Strings off course, but Groovy offers you several advantages over default Java String. The most important are embedded groovy expressions in the strings. Everything inside of ${} will be evaluated into string.
    int amount = 255
    Date today = new Date()
    
    String out = "Something, something, ${today} is amount: ${amount}"
    
    println out
    // This will print following:
    // Something, something, Sun Sep 25 18:34:52 CEST 2011 is amount: 255
    
    You don't need anymore to deal with clumsy Java concatenation. Which is good! :)

    Multiliners (""") are another Groovy extension to Java Strings. They allow you to easily create string literals that include line breaks.
    // So this is how you used to to this in Java.
    String bla1 = "Something, something,..." + "\n\n" +
    "              Another thing...."
    // Same code in groovy.
    String bla1 = """Something, something,...
    
                     Another thing...."""
    
    So no more + "\n" + stuff.
  • AS operator is very powerful. It is basically casting operator (like Java's (String)someObject). But is much more that that, it can cast Strings to Numbers (back and forth), List to Arrays (back and forth), Sets to Lists or Arrays(b & f) and not to forget String to Lists!(also b & f). For example:
    int number = ("12345" as int) - 1
    println number
    // 12344
    boolean isEqual = 123 as String == "123"
    // true
    println isEqual
    List abcList = "abc" as List
    println abc
    // [a, b, c]
    
    These are precisely things you would expect from script language and it is quite powerful stuff too.

Maps, lists and ranges
Groovy brings list and map concepts closer to it's type system.

  • Lists and Maps in Groovy have more natural feel than List and Maps in Java. Groovy uses comma-separated sequences of items between square brackets to define Lists or Maps. Let see it on example shall we:
    // In Java
    List cars1 = new ArrayList();
    cars1.add("Citroen");
    cars1.add("Audi");
    // [Citroen, Audi]
    
    // In groovy
    List cars2 = ["Citroen", "Audi"]
    // [Citroen, Audi]
    
    // Maps in groovy (you know Java part ;) )
    Map map1 = ["amount" : 15, "size" : 5]
    
    // Empty list
    List l = []
    // Empty map
    Map m = [:]
    
    // Get element from List
    String car = cars2[1]
    
    // Get element from Map
    String amount = map1["amount"]
    
    // Add item to list (somewhat different)
    cars2 << "Ferrari"
    // [Citroen, Audi, Ferrari]
    
    // Add item to map
    map1["mass"] = 55
    
  • Ranges are somewhat related to lists. Ranges are combination of lower and upper bound, which in the case of an integer range represents all numbers between two bounds for example.
    List computerComponents = ["Monitor", "CPU", "GPU", "Mem", "HDD"]
    // [Monitor, CPU, GPU, Mem, HDD]
    
    // Here you define only subset of previous list.
    List subComponents = computerComponents[1..2]
    // [CPU, GPU]
    
Regular expressions

Groovy provide support for regular expression at the language level. It provides new literals, new operators for regular expression matching, and a nice syntax for extracting groups. This is all you need to know about this because regular expression are topic for itself and it seem overkill to represent all complexity around then in this groovy article. For now we will say that groovy doesn't use backslash (as Java does) for regular expressions definition but rather forward slash. Also it introduce two new operators for matching:

=~ which match if pattern on the right side can be found in the string on the left side and ==! which match only strings that are exactly same as defined pattern.
Example:
println "999999" ==~ /\d+/
// true

println "mercury9" ==~ /\d+/
// false

println "mercury" ==~ /\w+/
// true
What is Groovy missing
Well not that much...
Character literals.
Java-like for loop in that you can't use "," operator.
Do...While.
Inner and anonymous classes, but it have closures (part 2 of this tutorial will deal with them) and together with ability to declare more than one class in Groovy file that is not so big deal.

Saturday, July 30, 2011

Functional programming with scheme

Why learn Scheme?

Main goal of learning functional programming is to better understand JavaScript and to have fun! We will be using Scheme (dialect of LISP) as a tool that will help us with this learning process. Scheme is not so popular programming language as JavaScript is. It is mainly well know inside university circles where it is usually used for some research in artificial intelligence. It is pure functional language what JavaScript off course is not, but it can be. That is beauty of JavaScript language. It can behave similar like object oriented language with code reuse pattern like inheritance and composition and in same time also as functional language. If you really want to learn JavaScript then previous knowledge is some functional language cannot hurt!

So anyway I'm start reading Structure and Interpretation of Computer Programs. One of the "must read" book for programmer. So lets see what this master peace has to offer.

And right on the foreword to this book I found some great comparison: "Pascal is for building pyramids -- imposing, breathtaking, static structures built by armies pushing heavy blocks into place. Lisp is for building organisms -- imposing, breathtaking, dynamic structures built by squads fitting fluctuating myriads of simpler organisms into place." Wow! Now I must read this book!

I did use Scheme at my university, but as year passed I can only remember basic stuff, so this will be great opportunity to refresh some of my knowledge.
Did you know that JavaScript has much in common with Scheme. Seems too odd, please see this link The Little JavaScripter. Douglas Crockford is one of the old gurus of JavaScript. Why they choose to build some part of JavaScript language similar to Scheme? My opinion is because Scheme is simple and concepts are natural. And there no much stuff going around you need to remember (like data types, pffff). So if you know Scheme it can be easier to understand some of JavaScript concepts. And I hear voices that this language (JavaScript that is) is becoming more and more important.

You can download MIT working Scheme interpreter, compiler, source-code debugger and what more not from here. Also you can download another scheme editor/interpreter named (recommended -- because it is more simple then MIT emacs environment) Racket (formerly know as DrScheme).

Language basics

In functional language you type expression and the compiler gives evaluation of that expression. Here are examples of rudimentary expressions in Scheme:
; expression
1586
; value of expression (evaluation) : 1586

; combination 
(+ 15 7)
; val: 22

(/ 12 3 4)
; val : 1

Combinations are expressions with within parentheses in order to denote procedure application. You can notice here that Scheme uses prefix notation which can look somewhat odd, but it have sever advantages over normal mathematical notation (operand-operator-operand). Firstly it enables procedures to capture arbitrary number of arguments and secondly it extends in straightforward way in allowing combination to be nested like in this example:
(+ 3 
   (* 2 3 5 
      (+ 2 1) 
      (- 8 1)) 
   (+ 3 1))

It is quite interesting that it is not necessary to explicitly instruct the interpreter to print the value of expression. Not like in Java where you explicitly must use System.out.println to print value of variable, here you know value of expression in every point. OK this is not so good comparison, but hopefully you get the point. :) In functional language any output from function can be input to another or can be printed to standard output.

For simple form of abstraction we use define keyword. It provide means for using names to refer to computational objects (variables). For example (semicolon represent comment):
; Use 'define' to identify variable PI and give it value. 
(define PI 3.14)
; Identify procedure for calculating area of circle.
(define (area r) 
  (* (* r r) 
     PI))
; run it
(area 5)
You can see that define also enables us to identify procedures (procedural definition) which is powerful form of abstraction by which a compound operation can be given a name. We refer to this kind of abstraction procedural abstraction and it help us suppressing details of procedure implementation and looking at procedure as black box.

You can see that procedure have parameters (these are formal parameter) and we say that procedure binds these formal parameters. In procedure definition the bound variables declares as formal parameters of the procedure have the body of the procedure as their scope. This means that names of formal parameters are "understand" only inside of procedure and are not defined outside of procedure body. But not all variables are binds (binds to the concrete procedure and are mining less outside that procedure...) to procedure. There can also be free variables. Free variables (such as +, -, *, /, pi, our defined PI and so on). Free variables are not bound. If we use free variable names as our procedure formal parameter we will then capture free variable name (binding it to current procedure we are defining) and by that we will introduce unexpected behavior and errors.

Here are some self explanatory examples of some procedure and variable definitions:
; Simple atomic operation (in this case +).
(+ 5 8 9 7 5 1 5 15)
; 55

; Nested combinations.
(+ (* 3 4) (- 4 6))
; 10

; Simple abstraction of variable.
(define a 3)
(define b 8)
(+ a b (- a))
; 8

; Calculate distance between two points.
(define (distance x1 y1 x2 y2) 
  (sqrt (+ (* (- x2 x1) (- x2 x1))
           (* (- y2 y1) (- y2 y1)))))
(distance 2 2 4 4)
; 2.828...
Every programming language must be able to define conditional expressions. In Scheme we use symbol cond followed by parenthesized pairs of expressions called clauses for conditional expressions. The first expression in each pair is a predicate - that is an expression whose value is interpreted as true or false.
(define (signum x)
  (cond ((> x 0) 1)
        ((= x 0) 0)
        ((< x 0) 0)))
(signum (- 3))
; value: 0
In previous example you can see definition of signum (sng) function using simple conditional expressions. It is straightforward how this function works. It evaluates predicates until it found one which evaluates to true. We can also use else in Scheme. Like in following example (calculation of absolute value):
(define (absolute1 x)
  (cond ((< x 0) (- x))
        (else x)))
(absolute1 456)
; values: 456

(define (absolute2 x)
  (if (< x 0)
      (- x)
      x))
(absolute2 456)
; value: 456
Here I also throw one example with special if form which is restricted type of conditional expressions that can be used when there are precisely two cases in the case analysis.

Wednesday, April 20, 2011

Passing multiple rows into oracle PL/SQL procedure

Introduction
Oracle off course uses standard Java database connectivity interfaces for communication with it's database. This is all nice and good, but if you know that you will use only Oracle database for your project, maybe it is useful to use some Oracle specific Java libraries in you project.

In this tutorial I will represent some specific Oracle Java libraries that can help you pass structured data into PL/SQL stored procedures. This structured data can contain multiple rows (each row contain multiple columns). So how can you do that?

Implementation

We will use two oracle specific classes:
oracle.sql.ARRAY
and
oracle.sql.STRUCT

First class (ARRAY) help us creating array of STRUCT object. STRUCT object is created by using static oracle.sql.StructDescriptor.createDescriptor method. This method uses existing Object type from database.
ARRAY object is created by using oracle.sql.ArrayDescriptor.createDescriptor method. This method uses existing collection type from database. This collection elements are of type that is same as our object type (I hope you understand this :) - This is basically "array of our object type" defined on database as collection type).

I create simple helper class to handle creation of structure and call to PL/SQL procedure. Here it is...

import java.sql.Connection;

import oracle.jbo.client.Configuration;
import oracle.jbo.server.DBTransaction;

import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;

public class TransferStructure {

    private StructDescriptor structureDescriptor;
    private ArrayDescriptor arrayDescriptor;
    private Object[] structValues;
    private int fieldsCount;
    private Connection connection;
    private STRUCT[] allRows;
    private String[] structDescColumnNames;
    private DBTransaction dbTransaction;
    private int currentRowIndx = 0;

    public TransferStructure(String objectTypeName, String arrayTypeName,
                             int numberOfRows,
                             DBTransaction dbTransaction) throws Exception {
        this.dbTransaction = dbTransaction;
        this.connection =
                Call_Pl_SqlCodeUtil.getCurrentConnection(dbTransaction);
        allRows = new STRUCT[numberOfRows];

        structureDescriptor =
                StructDescriptor.createDescriptor(objectTypeName, this.connection);
        arrayDescriptor =
                ArrayDescriptor.createDescriptor(arrayTypeName, this.connection);
        structValues = new Object[fieldsCount];
        fieldsCount = structureDescriptor.getMetaData().getColumnCount();
        structDescColumnNames = new String[fieldsCount];

    }

    public void initializeRow() throws Exception {
        structValues = new Object[fieldsCount];        
    }
    
    public void setFiledValue(String fieldName,
                              Object value) throws Exception {
        structValues[fieldPos(fieldName)] = value;
    }
    
    /**
     * Sadly but you need to call this at the end of row populatio.
     * @throws Exception
     */
    public void finalizeRow() throws Exception {
      STRUCT struct =
          new STRUCT(structureDescriptor, this.connection, structValues);
      allRows[currentRowIndx++] = struct;
    }
    
    /**
     * Finds field position in structure.
     * @param fieldName
     * @return
     * @throws Exception
     */
    private int fieldPos(String fieldName) throws Exception {
        int fieldPosition = -1;

        for (int i = 1; i < fieldsCount + 1; i++) {
            String currentField =
                structureDescriptor.getMetaData().getColumnName(i);
            if (currentField.equals(fieldName)) {
                fieldPosition = i - 1;
                break;
            }
        }
        return fieldPosition;
    }

    private ARRAY getArrayStructure() throws Exception {
        return new ARRAY(arrayDescriptor, this.connection, allRows);
    }

    public void makeProcedureCall(String procedureName) throws Exception {
        Call_Pl_SqlCodeUtil.callStoredProcedure(dbTransaction, procedureName,
                                                new Object[] { getArrayStructure() });
    }
}
You can use this helper class like so:
public static void main(String[] args) throws Exception {

        TransferStructure ts =
            new TransferStructure("YOUR_OBJECT_TYPE", "YOUR_ARRAY_TYPE_OF_OBJECTS",
                                  2, "getDatabaseConnection");
        ts.initializeRow();
        ts.setFiledValue("YOUR_FIRST_ATTRIBUTE", "value1");
        ts.finalizeRow();
        ts.initializeRow();
        ts.setFiledValue("YOUR_SECOND_ATTRIBUTE", new oracle.jbo.domain.Number(1234));
        ts.finalizeRow();
        ts.makeProcedureCall("YOUR_PACKAGE.your_procedure_with_in_parameter(?)");

}
You will also need method to call stored procedure from Java. This is not so hard to find on net. Also I want to note that this example is mainly implemented to run on Oracle ADF framework, but I think it will also run on some non-oracle development environments if you acquire necessary libraries.

Tuesday, April 19, 2011

Design patterns in Java - Decorator

Introduction


As holy grail of Java OO design book (GoF - Elements Of Reusable OO Software) say: "Decorator design pattern is intended to add additional responsibility to an object dynamically. This design pattern is also known as Wrapper".

So in another words this design pattern enable object inheritance at runtime. It achieve this by using interface inheritance and class composition.

Implementation

In Java classes java.io package also use decorator design pattern. For example:

File f = new file("d:\something.txt");
//Here FileInputStream "inherits" File class.
FileInputStream is = new FileInputStream(f);
//Here we add buffering capability to FileInputStream using decorator.
BufferedInputStream bis = new BufferedInputStream(fi);

I must point out that Decorator design pattern can also be used to remove responsibility from object (our example will not include this case).

Following code represent simple implementation of this pattern in Java.

Interface that will be our default "object". This object will be decorated (extended).
/**
 * Basic phone that can do only basic ringing.
 */
public interface IPhone {
    
    void announceCall();

    void announceMessage();

}
Decorator interface that extends our default "object" interface. So instead of static inheritance in implementation we create interface inheritance.
/**
 * Decorator that adds additional functionality to basic Phone.
 */
public interface IPhoneDecorator extends IPhone {

    void vibrate(int milliseconds);

    void flashLED();
}
Concrete implementation of our "default" object (nothing interesting, just simple interface implementation).
public class Phone implements IPhone {

    /*
     * This Phone announce call only by ringing.
     */
    public void announceCall() {
        System.out.println("Ring!");
    }

    /*
     * Phone announce message by makeing some sound.
     */
    public void announceMessage() {
        System.out.println("Make a sound!");
    }
}
Concrete decorator implementation that implements all operations. It delegate part of functionality to our "default" object and add more functionality of his own. It has one filed named phone which represent our "default" object implementation to which we will delegate stuff.
/**
 * Concrete decorator class that adds additional functionality.
 * @author jan.krizan
 */
public class UpgradePhoneDecorator implements IPhoneDecorator {

    private IPhone phone;

    /**
     * Constructor through which client put
     * instance that will be decorated.
     * @param phone
     */
    public UpgradePhoneDecorator(IPhone phone) {
        super();
        this.phone = phone;
    }

    public void vibrate(int milliseconds) {
        System.out.println("Vibrating for " + milliseconds + " milliseconds.");
    }

    public void flashLED() {
        System.out.println("Make a LED flash!");
    }

    public void announceCall() {
        //Delegate basic functionality to Phone clase.
        phone.announceCall();
        //Add additional functionality.
        vibrate(1500);
    }

    public void announceMessage() {
        //Delegate basic functionality to Phone clase.
        phone.announceMessage();
        //Add additional functionality.
        flashLED();
    }
}
We can now see what can our "default" object do before and after it has been decorated. You can see that we provide phone instance to decorator constructor and by using polymorphism (dynamic binding) we delegate basic work to our "default" object.
public static void main(String[] args) {
        IPhone oldPhone = new Phone();
        System.out.println("What can old phone do!");
        oldPhone.announceCall();
        oldPhone.announceMessage();
        System.out.println();
        IPhoneDecorator decoratedPhone = new UpgradePhoneDecorator(oldPhone);
        System.out.println("What can new phone do!");
        decoratedPhone.announceCall();
        decoratedPhone.announceMessage();
    }

Hope you like this!


Here is output (example uses ANT):
run:
What can old phone do!
Ring!
Make a sound!

What can new phone do!
Ring!
Vibrating for 1500 milliseconds.
Make a sound!
Make a LED flash!
BUILD SUCCESSFUL (total time: 0 seconds)

Wednesday, April 13, 2011

Faces generator for Oracle ADF (ver. 0.5)

Introduction


Hello everyone. I wish to present you one small project that I worked on in spare time (which I don't have plenty). The ultimate purpose of this "generator project" is to create JDeveloper plugin that will generate complete view controller project for Oracle ADF using just model project. This generator will enable programmer to create complete view controller (or several view controllers) project(s) in any state of application model project. So for example we can in about half an hour create model from database schema and create complete view controller based on that model. Then we can show that prototype to our customer and let him to decide what are necessary changes we need to perform.

Beside prototyping this can also help us in overcome many problems that JDeveloper programming has to offer. :) I know what you may be thinking: "Yes I think this idea is similar to existing Oracle product named HeadStart". But  this generator is flexible and can be customized to organization needs and to organization usual development methods for Oracle ADF. Also I think that programmers like (go figure) to program, so they will not just be clicking but they will create business logic and unit tests on model and that is important. You can also leverage existing skills inside your organization using generator and there is practically no learning curve as opposite to HeadStart.

You can watch how generator work by watching these two videos on youtube:


Hope you like it, full source code for project that is used in demonstration can be download from here.

Thursday, April 7, 2011

Complete Android game

Introduction

When developing android games you can decide to use OpenGL (for 3D games development) or Java drawing interfaces for simpler 2D games.
In this example I will show you how you can develop android 2D game. This game is similar to game "hexxagon" which I played a long ago on my old DOS box (eh...memories: 386SX, 2MB RAM, Trident 512KB, 42MB HDD, BASIC, TURBO PASCAL...that was the times!).

Main goal in this game is to have as many as possible balls at the end. There are following rules in this game: You have balls and you can move them or you can copy them (only to length of 3 places). Will the ball perform move or copy depends on distance that it need to travel. Only if distance is 1 then ball is copied. You perform move into destination empty field. Every opponent ball that is around destination empty field will be "transformed"
to you ball color.

Because I understand that this explanation is not so great I made simple video example on youtube and please see how it works, hope it will help you to better understand basic game logic (at least it is better than this horrible description :) ).


Class design

I put many comments in code to make it more readable and useful and if you are interested in details please look there. I will here only explain main class organization and design decisions. You can download full source code from my google code repository.
Please note that this is really far away from any finished or polished code. But enough with excuses let's begin!

We separated game implementation into two main classes. First class (BoardView) is custom view that is responsible for animation, drawing and for handling user input (touch on screen). Second class is main activity class and is responsible for game flow, saving state, restoring state and initialization. This is also only activity in this game. There is also third less important class that handle game logic.

There was strong motivation to separate view from game-flow controller in this implementation. BoardView class handle only stuff that is closely coupled with animation and drawing while activity (MinMaxBalls) handle game logic-flow.

I must admit something. The title of game can confuse experienced programmer that this game implementation uses MinMax algorithm to calculate next move. In first iteration of game implementation this algorithm was present but it use too much of CPU power and in real life we cannot predict that user will choose best move possible so algorithm perform strangely when user choose to perform illogical moves. So I decided that is best to exclude it for now. I will do another blog post about this algorithm but in some simpler scenario game (maybe tic tac toe or something like that).

MinMaxBalls main activity

Either way, here is the code for MinMaxBalls main activity:
package org.codingwithpassion.minmaxballs;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import org.codingwithpassion.minmaxballs.BoardView.MoveStageListener;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Main activity for game. It is really unnecessary complex and
 * need to be refactored at some point in future.
 * @author jan.krizan
 */
public class MinMaxBalls extends Activity {

 // This is actual location where we store state of board. It is here because
 // of save instance state stuff and decoupling between view/controler and model. 
 // So this activity is something like model.
 private State[][] positions = new State[BoardView.BOARD_SIZE][BoardView.BOARD_SIZE];

 // Game instance that is used mainly for calculating computer moves.
 private Game game = new Game();
 
 // Two TextView views that we use for tracking current score.
 private TextView humanScore;
 private TextView computerScore;
 
 // Dialog and menu id-s.
 private static final int DIALOG_RESET = 1;
 private static final int DIALOG_ABOUT = 2;
 private static final int MENU_RESET = 3;
 private static final int MENU_ABOUT = 4;

 // Intent parameter id.
 private static final String DIFFICULTY = "minmaxdifficulty";
  
 private boolean isFinish = false;

 private NumberFormat numberFormat = new DecimalFormat("00");
 
 // Instance of our view.
 private BoardView boardView;

 /*
  * OK, main activity, nothing clever.
  */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  setContentView(R.layout.main);

  boardView = (BoardView) findViewById(R.id.boardView);
  humanScore = (TextView) findViewById(R.id.humanScore);
  computerScore = (TextView) findViewById(R.id.compScore);

  boardView.setFocusable(true);
  boardView.setFocusableInTouchMode(true);
  boardView.setMoveStageListener(new CellSelected());
  
  // Initialize positions.
  Game.setEmptyValues(positions);
  Game.setSolidSquares(positions);
  Game.initializeStartPositions(positions);
  
  // Initial game difficulty.
  int difficulty = Game.MEDIUM_DIFFICULTY;

  Bundle extras = getIntent().getExtras();
  if (extras != null) {
   difficulty = Integer.parseInt(extras.getString(DIFFICULTY));
   Log.d("difficult", String.valueOf(difficulty));
  }
  
  // We set difficulty to know what kind (which "hard-nest") of moves to perform.
  game.setDifficulty(difficulty);
  
  // Bind positions table to View values.
  boardView.setPositions(positions);
    
  refreshScore();
 }
 
 /*
  * Simple but effective refresh score.
  */
 private void refreshScore() {
  humanScore.setText(numberFormat.format(Game.countPlayerBalls(positions,
    State.HUMAN)));
  computerScore.setText(numberFormat.format(Game.countPlayerBalls(
    positions, State.COMP)));
 }
 
 @Override
 protected Dialog onCreateDialog(int id) {
  if (id == DIALOG_RESET) {
   String easy = String
     .valueOf(this.getText(R.string.difficulty_easy));
   String hard = String
     .valueOf(this.getText(R.string.difficulty_hard));
   String medium = String.valueOf(this
     .getText(R.string.difficulty_medium));

   final CharSequence[] items = { easy, medium, hard };

   AlertDialog.Builder builder = new AlertDialog.Builder(this);
   builder.setTitle(R.string.game_difficulty);
   builder.setItems(items, new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int item) {
     String difficulty = String.valueOf(Game.MEDIUM_DIFFICULTY);
     if (item == 0) {
      difficulty = String.valueOf(Game.EASY_DIFFICULTY);
     } else if (item == 1) {
      difficulty = String.valueOf(Game.MEDIUM_DIFFICULTY);
     } else if (item == 2) {
      difficulty = String.valueOf(Game.HARD_DIFFICULTY);
     }
     Intent intent = getIntent();
     intent.putExtra(DIFFICULTY, difficulty);
     finish();
     startActivity(intent);
    }
   });
   return builder.create();
  } else if (id == DIALOG_ABOUT) {      
   final TextView message = new TextView(this);   
   final SpannableString s = new SpannableString(
     this.getText(R.string.blog_link));
   Linkify.addLinks(s, Linkify.WEB_URLS);
   message.setText(s);
   message.setMovementMethod(LinkMovementMethod.getInstance());
   AlertDialog.Builder builder = new AlertDialog.Builder(this);   
   builder.setView(message)
     .setCancelable(true)
     .setIcon(android.R.drawable.stat_notify_error)
     .setNegativeButton("OK",
       new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog,
          int id) {
         dialog.cancel();
        }
       });
   return builder.create();
  }
  return null;
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  super.onCreateOptionsMenu(menu);

  int groupId = 0;

  MenuItem menuItemReset = menu.add(groupId, MENU_RESET, Menu.NONE,
    R.string.new_game);
  menuItemReset.setIcon(android.R.drawable.star_big_on);
  MenuItem menuItemAbout = menu.add(groupId, MENU_ABOUT, Menu.NONE,
    R.string.about);
  menuItemAbout.setIcon(android.R.drawable.stat_notify_error);

  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  super.onOptionsItemSelected(item);

  if (item.getItemId() == MENU_RESET) {
   showDialog(DIALOG_RESET);
   return true;
  } else if (item.getItemId() == MENU_ABOUT) {
   showDialog(DIALOG_ABOUT);
   return true;
  }

  return false;
 }

 private void showToast(int message) {
  Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG)
    .show();
 }

 @Override
 public void onSaveInstanceState(Bundle bundle) {
  super.onSaveInstanceState(bundle);

  if (game.getMove().player == State.HUMAN) {
   if (!isFinish) {
    int source_i = game.getMove().sourceMove.i;
    int source_j = game.getMove().sourceMove.j;
    if (source_i != -1 && source_j != -1) {
     if (positions[source_i][source_j] == State.SELECTED) {
      positions[source_i][source_j] = State.HUMAN;
     }
    }
    game.bestMove(positions, State.COMP);
    game.getMove().player = State.COMP;
    performMove(game.getMove(), false);
    changecolors(game.getMove(), false);
   }
  }

  for (int i = 0; i < positions.length; i++) {
   int[] pos = new int[positions.length];
   for (int j = 0; j < positions.length; j++) {
    pos[j] = positions[i][j].getValue();
   }
   bundle.putIntArray("pos" + i, pos);
  }
 }

 @Override
 public void onRestoreInstanceState(Bundle bundle) {
  for (int i = 0; i < BoardView.BOARD_SIZE; i++) {
   for (int j = 0; j < BoardView.BOARD_SIZE; j++) {
    positions[i][j] = State
      .fromInt(bundle.getIntArray("pos" + i)[j]);
   }
  }
  refreshScore();
 }
 
 /*
  * OK, the real meat of game-flow control. It uses semaphore-like
  * poor design to control flow and it is crucially important to
  * preserve order of if-s and boolean fields checking.
  * Very, very poor design (dragons live here kind of design!) so:
  * TODO: Please refactor this I get pretty nasty poor design smell here!
  */
 private class CellSelected implements MoveStageListener {
  
  // Semaphore-like boolean fields.
  private boolean isCompMove = false;
  private boolean isCompSelected = false;

  private boolean isHumanBallChange = false;
  private boolean isCompBallsChange = false;

  private boolean isCompVictory = false;
  private boolean isHumanVictory = false;
  
  /* 
   * React on user click on the board. If user clicks on her/his
   * ball then select that ball, of she/he select empty field then
   * move ball, else display error by displaying error animation
   * on that square.
   */
  public void userClick(int i, int j) {
   Coordinate humanSource = game.getMove().sourceMove;
   if (!isFinish && positions[i][j] == State.HUMAN) {
    game.getMove().player = State.HUMAN;
    // If we already selected ball and now we change our mind.
    if (humanSource.isNotEmpty()) {
     positions[humanSource.i][humanSource.j] = State.HUMAN;
    }
    positions[i][j] = State.SELECTED;
    boardView.selectBall(i, j, State.SELECTED);
    humanSource.i = i;
    humanSource.j = j;
   } else if (!isFinish
     && humanSource.isNotEmpty()
     && positions[i][j] == State.EMPTY
     && Game.isAllowedDistance(i, j, humanSource.i,
       humanSource.j, 2)) {
    game.getMove().destinationMove.i = i;
    game.getMove().destinationMove.j = j;
    performMove(game.getMove(), true);
    isHumanBallChange = true;
   } else {
    boardView.error(i, j);
    isHumanBallChange = false;
   }

  }
  
  /*
   * If animation is complete, then it is obvious we need to do something.
   * What will be done is decided by checking various boolean fiels.
   */
  public void animationComplete() {
   // TODO: refactor:
   // Excessive conditional logic, must preserve conditions order of 
   // conditions...bad, bad design. :(
   if (isCompVictory) {
    changeAllColors(State.HUMAN, State.COMP);
    isCompVictory = false;
    // stop all activity
    isCompSelected = false;
   }
   if (isHumanVictory) {
    changeAllColors(State.COMP, State.HUMAN);
    isHumanVictory = false;
    // stop all activity
    isCompSelected = false;
   }

   if (isCompBallsChange) {
    changecolors(game.getMove(), true);
    isCompBallsChange = false;
    refreshScore();
    game.deleteMove();
    checkWin();
   }

   if (isCompMove) {

    performMove(game.getMove(), true);
    isCompMove = false;
    isCompBallsChange = true;
   }
   if (isCompSelected) {

    // Calculate move for computer.
    game.bestMove(positions, State.COMP);
    game.getMove().player = State.COMP;
    Coordinate compSelect = game.getMove().sourceMove;
    boardView.selectBall(compSelect.i, compSelect.j, State.SELECTED);
    positions[compSelect.i][compSelect.j] = State.SELECTED;
    isCompSelected = false;
    isCompMove = true;
   }

   if (isHumanBallChange) {
    changecolors(game.getMove(), true);
    isHumanBallChange = false;
    isCompSelected = true;
    refreshScore();
    game.deleteMove();
    checkWin();
   }
  }

  private void checkWin() {
   if (game.isWin(positions, State.HUMAN)) {
    checkWhoWin();
   }
   if (game.isWin(positions, State.COMP)) {
    checkWhoWin();
   }
  }

  private void checkWhoWin() {
   if (Game.countPlayerBalls(positions, State.HUMAN) >= Game
     .countPlayerBalls(positions, State.COMP)) {
    isHumanVictory = true;
    showToast(R.string.human_wins);
    isCompVictory = false;
    isFinish = true;
   } else {
    isCompVictory = true;
    showToast(R.string.comp_wins);
    isHumanVictory = false;
    isFinish = true;
   }
  }
 }
 
 private void changecolors(Move move, boolean withAnimation) {
  State toWho = move.player;
  State fromWho = toWho == State.HUMAN ? State.COMP : State.HUMAN;
  boolean[][] changeThese = Game.changeColors(positions,
    move.destinationMove.i, move.destinationMove.j, fromWho);

  for (int i = 0; i < BoardView.BOARD_SIZE; i++) {
   for (int j = 0; j < BoardView.BOARD_SIZE; j++) {
    if (changeThese[i][j]) {
     positions[i][j] = toWho;
    }
   }
  }
  if (withAnimation)
   boardView.changeColors(changeThese, fromWho, toWho);
 }

 private void changeAllColors(State fromWho, State toWho) {
  boolean[][] changeThese = Game.changeAllColors(positions, fromWho);

  for (int i = 0; i < BoardView.BOARD_SIZE; i++) {
   for (int j = 0; j < BoardView.BOARD_SIZE; j++) {
    if (changeThese[i][j]) {
     positions[i][j] = toWho;
    }
   }
  }

  boardView.changeColors(changeThese, fromWho, toWho);

 }

 private void performMove(Move move, boolean withAnimation) {
  int start_i = move.sourceMove.i;
  int start_j = move.sourceMove.j;
  int dest_i = move.destinationMove.i;
  int dest_j = move.destinationMove.j;

  State who = move.player;
  if (Game.getDistance(start_i, start_j, dest_i, dest_j) > 1) {
   if (withAnimation)
    boardView.moveBall(start_i, start_j, dest_i, dest_j, who);
   positions[start_i][start_j] = State.EMPTY;
  } else {
   if (withAnimation)
    boardView.createBall(dest_i, dest_j, who);
   positions[start_i][start_j] = who;
  }
  positions[dest_i][dest_j] = who;
 }

}
You can see that there is one inner class named CellSelected. It's responsibility is to control game flow and to handle "messages" from view that are send by method calls. It also save instance field data and perform additional moves when is necessary (when user tilt screen for example) in it's onSaveInstanceState method.

BoardView View class

The second interesting class is BoardView class.
package org.codingwithpassion.minmaxballs;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.os.Handler;
import android.os.Message;
import android.os.Handler.Callback;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * Implementation of board view/controller. It draws board, handle user events
 * (touch), rotating screen and animation.
 * 
 * @author jan.krizan
 */
public class BoardView extends View {

 public static final int BOARD_MARGIN = 10;
 public static final int BOARD_SIZE = 7;
 public static final int GRID_SIZE = 2;

 private static final int MSG_ANIMATE = 0;

 private final Handler animationHandler = new Handler(
   new AnimationMessageHandler());

 private MoveStageListener moveStageListener;

 /**
  * Listener interface that send messages to Activity. Activity then handle
  * this messages.
  */
 public interface MoveStageListener {

  // Fires when user click's somewhere on board.
  void userClick(int i, int j);

  // When animation complete at same current move stage is complete.
  void animationComplete();
 }

 public void setMoveStageListener(MoveStageListener selectionListener) {
  this.moveStageListener = selectionListener;
 }

 /**
  * Animation interface that control animation handler.
  */
 public interface Animation {
  // This is called on onDraw method.
  void animate(Canvas canvas);

  // Say if animation should end.
  boolean isFinish();

  // Control which cells will be animated and hence should be
  // ignored when we draw grid.
  boolean skip(int i, int j);

  // How much frames per second we will use for our animation.
  int fps();
 }

 private Animation animation = new NullAnimation();

 // Here we store animation board state with all players and intermediate
 // states for cells.
 private State[][] positions;

 public void setPositions(State[][] positions) {
  this.positions = positions;
 }

 // Paint for board table line. It is here because onPaint is
 // using it several time per frame.
 private Paint boardLinePaint;

 // Width of board is also calculated dynamically when screen
 // size changes.
 private float boardWidth;

 // Maximum radius of ball - calculated dynamically also...
 private float maxRadius;

 // Can freely be here because it is calculated every time screen size
 // changes.
 private float cellSize;

 public BoardView(Context context, AttributeSet attrs) {
  super(context, attrs);
  requestFocus();
  boardLinePaint = new Paint();
  boardLinePaint.setColor(0xFFFFFFFF);
  boardLinePaint.setStrokeWidth(GRID_SIZE);
  boardLinePaint.setStyle(Style.STROKE);
 }

 /*
  * Classic onDraw. It paints table and ball states. When we need to animate
  * stuff we call it to refresh canvas state (easy as in classic Java 2D
  * graphics animation).
  */
 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  float offsetBoardWidth = boardWidth - BOARD_MARGIN;
  canvas.drawRect(BOARD_MARGIN, BOARD_MARGIN, offsetBoardWidth,
    offsetBoardWidth, boardLinePaint);

  for (int i = 0; i < BOARD_SIZE; i++) {
   float cellStep = BOARD_MARGIN + (i * cellSize);
   canvas.drawLine(cellStep, BOARD_MARGIN, cellStep, offsetBoardWidth,
     boardLinePaint);
   canvas.drawLine(BOARD_MARGIN, cellStep, offsetBoardWidth, cellStep,
     boardLinePaint);
  }

  setValuesFromDatas(canvas);

  animation.animate(canvas);

 }

 /*
  * Set values from board state structure and skip animated items.
  */
 private void setValuesFromDatas(Canvas canvas) {
  for (int i = 1; i < BOARD_SIZE + 1; i++) {
   for (int j = 1; j < BOARD_SIZE + 1; j++) {
    // If this are currently animated squares, do not
    // draw them!
    if (!animation.skip(i - 1, j - 1))
     drawBall(i, j, positions[i - 1][j - 1], maxRadius, canvas,
       255);
    drawSolidSquare(canvas, i, j, positions[i - 1][j - 1]);
   }
  }
 }

 /*
  * Method for drawing filled square (when user touch inappropriate section
  * of table). It is stupid to create Paint object every time, but it is here
  * for readability and encapsulation reasons.
  */
 private void drawWhiteSquare(Canvas canvas, int i, int j, int alpha) {
  Paint paint = new Paint();
  paint.setColor(Color.WHITE);
  paint.setStyle(Style.FILL);
  paint.setAlpha(alpha);
  drawCustomRect(i, j, canvas, paint, 0);
 }

 private void drawCustomRect(int i, int j, Canvas canvas, Paint paint,
   float shrink) {
  canvas.drawRect(i * cellSize + GRID_SIZE + BOARD_MARGIN + shrink, j
    * cellSize + GRID_SIZE + BOARD_MARGIN + shrink, (i + 1)
    * cellSize - GRID_SIZE + BOARD_MARGIN - shrink, (j + 1)
    * cellSize + BOARD_MARGIN - GRID_SIZE - shrink, paint);
 }

 /*
  * Draw fancy "disabled" and solid square. Same story here for Paint object
  * as in drawWhiteSquare method.
  */
 private void drawSolidSquare(Canvas canvas, int i, int j, State who) {
  if (who == State.BLOCK) {

   Paint paintBigger = new Paint();
   paintBigger.setColor(0xFFA800A8);
   paintBigger.setStyle(Style.FILL);

   drawCustomRect(i - 1, j - 1, canvas, paintBigger, 0);

   Paint paintSmaller = new Paint();
   paintSmaller.setColor(0xFFFC54FC);
   paintSmaller.setStyle(Style.FILL);

   float shrink = cellSize * 0.15f;

   drawCustomRect(i - 1, j - 1, canvas, paintSmaller, shrink);

   canvas.drawLine((i - 1) * cellSize + GRID_SIZE + BOARD_MARGIN,
     (j - 1) * cellSize + GRID_SIZE + BOARD_MARGIN, (i - 1)
       * cellSize + GRID_SIZE + BOARD_MARGIN + shrink,
     (j - 1) * cellSize + GRID_SIZE + BOARD_MARGIN + shrink,
     paintSmaller);

   canvas.drawLine(i * cellSize - GRID_SIZE + BOARD_MARGIN, (j - 1)
     * cellSize + GRID_SIZE + BOARD_MARGIN, i * cellSize
     - GRID_SIZE + BOARD_MARGIN - shrink, (j - 1) * cellSize
     + GRID_SIZE + BOARD_MARGIN + shrink, paintSmaller);

   canvas.drawLine(i * cellSize - GRID_SIZE + BOARD_MARGIN, j
     * cellSize - GRID_SIZE + BOARD_MARGIN, i * cellSize
     - GRID_SIZE + BOARD_MARGIN - shrink, j * cellSize
     - GRID_SIZE + BOARD_MARGIN - shrink, paintSmaller);

   canvas.drawLine((i - 1) * cellSize + GRID_SIZE + BOARD_MARGIN, j
     * cellSize - GRID_SIZE + BOARD_MARGIN, (i - 1) * cellSize
     + GRID_SIZE + BOARD_MARGIN + shrink, j * cellSize
     - GRID_SIZE + BOARD_MARGIN - shrink, paintSmaller);
  }

 }

 /*
  * Draw custom balls. We can change balls alpha and radius in animation.
  */
 private void drawBall(int i, int j, State who, float radius, Canvas canvas,
   int alpha) {

  // Calculate where we will put ball in our grid based on coordinates in
  // grid.
  float x = cellSize * (i - 1) + cellSize / 2 + BOARD_MARGIN;
  float y = cellSize * (j - 1) + cellSize / 2 + BOARD_MARGIN;
  // Skip empty every time.
  if (who != State.EMPTY && who != State.BLOCK) {
   Paint smallBall = new Paint();

   int color = Color.RED;
   if (who == State.SELECTED)
    color = Color.BLACK;
   else if (who == State.COMP)
    color = Color.BLUE;
   smallBall.setColor(color);
   smallBall.setStyle(Style.FILL);
   smallBall.setAlpha(alpha);

   Paint bigBall = new Paint();
   bigBall.setColor(Color.WHITE);
   bigBall.setStyle(Style.FILL);
   bigBall.setAlpha(alpha);

   // Smaller ball is 15% smaller than bigger.
   canvas.drawCircle(x, y, radius * 1.15f, bigBall);

   canvas.drawCircle(x, y, radius, smallBall);
  }
 }

 /*
  * Select ball action operation (ball become black).
  */
 public void selectBall(int i, int j, State who) {
  animation = new PutBall();
  PutBall putBall = (PutBall) animation;
  putBall.alpha = 0;
  putBall.i = i;
  putBall.j = j;
  putBall.who = State.SELECTED;

  animationHandler.sendEmptyMessage(MSG_ANIMATE);
 }

 /*
  * Create new ball operation (on empty square in grid).
  */
 public void createBall(int i, int j, State who) {
  animation = new CreateBallAnimation();
  CreateBallAnimation createBallAnimation = (CreateBallAnimation) animation;
  createBallAnimation.radius = 0;
  createBallAnimation.i = i;
  createBallAnimation.j = j;
  createBallAnimation.who = who;

  animationHandler.sendEmptyMessage(MSG_ANIMATE);
 }

 /*
  * Paint square in white block operation (along with alpha animation) when
  * user perform illegal move.
  */
 public void error(int i, int j) {
  animation = new FillSquareAnimation();
  FillSquareAnimation fillSquareAnimation = (FillSquareAnimation) animation;
  fillSquareAnimation.i = i;
  fillSquareAnimation.j = j;
  fillSquareAnimation.alpha = 255;

  animationHandler.sendEmptyMessage(MSG_ANIMATE);
 }

 /*
  * Move ball from one place to another operation (with animation also).
  */
 public void moveBall(int i1, int j1, int i2, int j2, State who) {
  animation = new MoveBallsAnimation();
  MoveBallsAnimation createBallAnimation = (MoveBallsAnimation) animation;
  createBallAnimation.radius = maxRadius;
  createBallAnimation.moveFrom[i1][j1] = true;
  createBallAnimation.moveTo[i2][j2] = true;
  createBallAnimation.whoFrom = who;
  createBallAnimation.whoTo = who;

  animationHandler.sendEmptyMessage(MSG_ANIMATE);
 }

 /*
  * Change colors for all balls operation that have same coordinates as true
  * values in "changeThem" matrix. Animation is same as for move operation.
  */
 public void changeColors(boolean[][] changeThem, State whoFrom, State whoTo) {
  animation = new MoveBallsAnimation();
  MoveBallsAnimation createBallAnimation = (MoveBallsAnimation) animation;
  createBallAnimation.radius = maxRadius;
  createBallAnimation.moveFrom = changeThem;
  createBallAnimation.moveTo = changeThem;
  createBallAnimation.whoFrom = whoFrom;
  createBallAnimation.whoTo = whoTo;

  animationHandler.sendEmptyMessage(MSG_ANIMATE);
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  if (animation.isFinish()) {
   int action = event.getAction();

   int i = (int) ((event.getX() - BOARD_MARGIN) / cellSize);
   int j = (int) ((event.getY() - BOARD_MARGIN) / cellSize);

   if (i >= 0 && i <= (BOARD_SIZE - 1) && j >= 0
     && j <= (BOARD_SIZE - 1)) {

    // If user just click, then we will show painted square.
    if (action == MotionEvent.ACTION_DOWN) {
     moveStageListener.userClick(i, j);
     return true;
    }
   }
  }

  return false;
 }

 /*
  * Recalculate fields based on current screen size.
  */
 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  boardWidth = w < h ? w : h;
  cellSize = (boardWidth - GRID_SIZE * BOARD_MARGIN) / BOARD_SIZE;

  maxRadius = cellSize * 0.68f / 2;
 }

 /*
  * Set dimension of current view.
  */
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int w = MeasureSpec.getSize(widthMeasureSpec);
  int h = MeasureSpec.getSize(heightMeasureSpec);
  int d = w == 0 ? h : h == 0 ? w : w < h ? w : h;
  setMeasuredDimension(d, d);
 }

 /**
  * Inner animation handler. This handler call itself several times during
  * animation and in every pass invalidates current view (calls onDraw method
  * of View). It is controlled by Animation interface and hence concrete
  * implementation of Animation interface. This implementation "tells" it
  * when to stop.
  */
 private class AnimationMessageHandler implements Callback {
  public boolean handleMessage(Message msg) {
   if (msg.what == MSG_ANIMATE) {
    BoardView.this.invalidate();
    if (!animationHandler.hasMessages(MSG_ANIMATE)) {
     if (animation.isFinish()) {
      animationHandler.removeMessages(MSG_ANIMATE);
      moveStageListener.animationComplete();
     } else {
      animationHandler.sendEmptyMessageDelayed(MSG_ANIMATE,
        animation.fps());
     }
    }
    return true;
   }
   return false;
  }
 }

 /**
  * This animation doesn't do anything - null animation.
  */
 private class NullAnimation implements Animation {
  public void animate(Canvas canvas) {
   // do nothing
  }

  public boolean isFinish() {
   return true;
  }

  public boolean skip(int i, int j) {
   return false;
  }

  public int fps() {
   return 1000 / 1;
  }
 }

 /**
  * Create ball animation (balls pops-up up in empty square).
  */
 private class CreateBallAnimation implements Animation {

  public int i;
  public int j;
  public State who;
  public float radius;

  public void animate(Canvas canvas) {
   drawBall(i + 1, j + 1, who, radius, canvas, 255);
   radius += 8;
   if (radius >= BoardView.this.maxRadius)
    radius = BoardView.this.maxRadius;
  }

  public boolean isFinish() {
   return radius >= BoardView.this.maxRadius;
  }

  public boolean skip(int i, int j) {
   return (this.i == i && this.j == j);
  }

  public int fps() {
   return 1000 / 16;
  }
 }

 /**
  * Move ball animation that moves current ball from one square to another
  * altogether with pop-ing-up effect. :) It can be use for one ball or ball
  * set (represented by coordinate matrix).
  */
 private class MoveBallsAnimation implements Animation {
  public boolean[][] moveFrom = new boolean[BOARD_SIZE][BOARD_SIZE];
  public boolean[][] moveTo = new boolean[BOARD_SIZE][BOARD_SIZE];
  public State whoFrom;
  public State whoTo;
  public float radius;

  public boolean firstPahseFinish;
  public boolean secondPhaseFinish;

  public void animate(Canvas canvas) {
   if (!firstPahseFinish) {
    for (int i = 0; i < BOARD_SIZE; i++) {
     for (int j = 0; j < BOARD_SIZE; j++) {
      if (moveFrom[i][j])
       drawBall(i + 1, j + 1, whoFrom, radius, canvas, 255);
     }
    }

    radius -= 8;
    if (radius <= 0) {
     radius = 0;
     firstPahseFinish = true;
    }
   } else {

    for (int i = 0; i < BOARD_SIZE; i++) {
     for (int j = 0; j < BOARD_SIZE; j++) {
      if (moveTo[i][j])
       drawBall(i + 1, j + 1, whoTo, radius, canvas, 255);
     }
    }

    radius += 8;
    if (radius >= maxRadius) {
     radius = maxRadius;
     secondPhaseFinish = true;
    }
   }
  }

  public boolean isFinish() {
   return firstPahseFinish && secondPhaseFinish;
  }

  public boolean skip(int i, int j) {
   return moveFrom[i][j] || moveTo[i][j];
  }

  public int fps() {
   return 1000 / 16;
  }
 }

 /**
  * Paint square with white gradually disappeared white inner square.
  */
 private class FillSquareAnimation implements Animation {

  public int i;
  public int j;

  public int alpha;

  public void animate(Canvas canvas) {
   drawWhiteSquare(canvas, i, j, alpha);
   alpha -= 75;
   if (alpha <= 0)
    alpha = 0;
  }

  public boolean isFinish() {
   return alpha <= 0;
  }

  public boolean skip(int i, int j) {
   return false;
  }

  public int fps() {
   return 1000 / 16;
  }
 }
 
 /**
  * And last but not the least animation that gradually change ball
     * color.  
  */
 private class PutBall implements Animation {

  public int i;
  public int j;
  public State who;
  public int alpha;

  public void animate(Canvas canvas) {
   drawBall(i + 1, j + 1, who, maxRadius, canvas, alpha);
   alpha += 100;
   if (alpha >= 255)
    alpha = 255;
  }

  public boolean isFinish() {
   return alpha >= 255;
  }

  public boolean skip(int i, int j) {
   return (this.i == i && this.j == j);
  }

  public int fps() {
   return 1000 / 16;
  }
 }
}
This class extends View and overrides onDraw method. This is rudimentary animation implementation in android and it uses Java 2D interfaces so that means that it is almost same as Core Java 2D animation.

It defined two interfaces. MoveStageListener interface listener is used for sending messages to activity (MinMaxBalls) class. Second interface Animation is used to send messages to Message Handler. Message Handler is used to perform animation loop and to invalidate view (so it calls onDraw method). We can do this also using another thread, but in this way we have slightly cleaner code.

Animation interfaces "tells" Message Handler when to stop, how much fps it should use and stuff like that. We have several implementation of this Animation interfaces and all perform some simple animations.

And that's it. Please feel free to put comments if you want ask/suggest anything and I will do as much as I can to notice that comments (It is hard to notice comments on blog, because blogger do not send emails when someone put comment -- they shoud!) :) And yes, I will also try to answer them.

Thursday, March 24, 2011

XPath in Java

XPath is support in Java by default. In other to use is, you need to parse XML document using DOM (or maybe SAX -- I don't know if that works). Either way, when you do that, just call XPathFactory.newInstance().newXPath() and call evaluate method, like in following example:

public static Object xPathQuery(String expression, File file) {
         Document document;
         DocumentBuilderFactory dBF = DocumentBuilderFactory.newInstance();
         Object result = null;
         try {
             DocumentBuilder builder = dBF.newDocumentBuilder();
             document = builder.parse(file);
             
             XPath xpath = XPathFactory.newInstance().newXPath();
             XPathExpression expr = xpath.compile(expression);

             result = expr.evaluate(document, XPathConstants.NODESET);             
         } catch (Exception e) {
             e.printStackTrace();
         }
         return result;
     }

You can see that this method returns Object. This object can be cast to boolean, String or (more useful) NodeList. Be careful, this type depends of type of your XPath query. So for example to extract NodeList from XPath query that returns multiple nodes will look like this:

NodeList nodes = (NodeList) xPathQuery("//employees", new File("C:/some_xml"));

int j = nodes.getLength();

for (int i = 0; i < j; i++) {
    System.out.println(nodes.item(i).getTextContent());
}

And that's it!

Tuesday, March 15, 2011

Saxon XSLT Java example

I was looking for simple Saxon XSLT transformation example using Java and I could not find one that is simple and descriptive enough, so I made one and I hope it will helpful for someone. So here it is :

import java.io.File;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class Main {

    /**
     * Simple transformation method.
     * @param sourcePath - Absolute path to source xml file.
     * @param xsltPath - Absolute path to xslt file.
     * @param resultDir - Directory where you want to put resulting files.
     */
    public static void simpleTransform(String sourcePath, String xsltPath,
                                       String resultDir) {
        TransformerFactory tFactory = TransformerFactory.newInstance();
        try {
            Transformer transformer =
                tFactory.newTransformer(new StreamSource(new File(xsltPath)));

            transformer.transform(new StreamSource(new File(sourcePath)),
                                  new StreamResult(new File(resultDir)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        //Set saxon as transformer.
        System.setProperty("javax.xml.transform.TransformerFactory",
                           "net.sf.saxon.TransformerFactoryImpl");

        simpleTransform("d:/project/hob/AppModule.xml",
                        "d:/project/hob/create-fragment.xslt", "C:/");

    }
}

You just need to put Saxon library into project CLASSPATH. This example create resulting file(s) on the root of C: drive.

Have fun!

Thursday, March 3, 2011

Slow JDeveloper

Speed up JDeveloper


There are times when JDeveloper slow down so much that it becomes unusable. For example you need to wait 5-30 seconds for simple text change or you wait for structure window to refresh. This can happen when you have complex .jspx pages and when all your components are binds to backing bean.

There are some serious issues with JDeveloper memory management. This can be due to Java Swing API, but this is just wild guest and I really don't know nothing about this.

Eater way, to solve this problem you have two choices:
Use external editor (This is not so good solution).
Other (much better) is to disable automatic component binding by removing binding line from your jspx page. This will rise performance to acceptable level (noting spectacular).

General tip is not to have complex Java backing bean pages and complex .jspx pages (use fragments to simplify layout).

Tuesday, February 1, 2011

PL/SQL Cursors example

Introduction

PL/SQL provides a number of different ways for data retrieval all of which include working with cursors. You can think of cursor as a pointer to the results of a query run against one or more tables in current database. PL/SQL cursor and Java Database Connectivity (JDBC) cursors also share some similarities. Now when Oracle buys Sun it is only matter of time when we will have natural mapping between JDBC cursors and PL/SQL cursors! :) Only kidding here, this things should never be mixed together because JDBC spec should be independent from vendor and it is designed to prevent vendor locking and whole point of Java are clever interfaces and delegation of vendor specific stuff to vendor.

Ok, let's get back to point of this tutorial.

Why use cursors? Well when you retrieve subset of data from table (or whole table), then that data remains stored in SGA (Shared memory) until cursor is closed, so in this way you cache data and caching on database is good idea.

Choosing explicit or implicit cursor in your PL/SQL program?

Implicit cursors are used when you have a simple SELECT ... INTO single row of data into local program variables. It's the easiest path to your data, but it can often lead to coding the same or similar SELECTs in multiple places in your code.

Explicit cursors are defined in declaration section (package or block) and in this way, you can open and fetch from cursor in one or more places.

Implicit cursor will run more efficient than equivalent explicit cursor (from Oracle 8 Database onwards). So is there reasons to use explicit cursors at all? Off course. Explicit cursor can still be more efficient and they off course offer much programmatic control.

Implicit cursor

Implicit cursors are used when you need to retrieve single row from database. If you want to retrieve more than one row, then you must use either an explicit cursor or bulk collect.

Here one example of implicit cursor usage:

SET serveroutput on;

DECLARE

   PROCEDURE find_employee (employee_id_v employees.employee_id%TYPE)
   IS
      --Record in which we will fetch entire row.  
      emp_rec   employees%ROWTYPE;
   BEGIN
      --Begining of implicit cursor statement.
      SELECT *
        INTO emp_rec --Fetch into record.
        FROM employees
       WHERE employee_id = employee_id_v;
       --Write result.
      DBMS_OUTPUT.put_line (emp_rec.employee_id || ' ' || emp_rec.first_name);
   --Catch exception when there is no such employee.
   EXCEPTION
      WHEN NO_DATA_FOUND
      THEN         
         DBMS_OUTPUT.put_line ('Unknown employee with id: ' || employee_id_v);
   END find_employee;
BEGIN
   find_employee (101);
   find_employee (102);
   --This one will produce exeption (OK, only if you do not have employee  with id 1021).
   find_employee (1021);
END;

We encapsulate query with function (this is _aways_ a good idea). This function print employee information from database to output. Also we introduce some exception handling (when
no employee is found).

Because PL/SQL is so tightly integrated with the Oracle database, you can easily retrieve complex datatypes (entire row for example - as we did in our example).

You can see that using implicit cursor is quite simple (with basic understanding of SQL) we just create simple select statement and insert rowset into record (that we declared as local variable).

Explicit cursor

Explicit cursor is explicitly defined in the declaration section. With explicit cursor, you have complete control over the different PL/SQL steps involved in retrieving information from the database. You decide when to open, when fetch and how many records and when to close cursor. Information about the current state of cursor is available through examination of cursor attributes.

Example:

SET SERVEROUTPUT on;

DECLARE
   PROCEDURE get_all_employees
   IS
      --Employee record variable.
      employee_rec   employees%ROWTYPE;
      --Cursor variable for explicit use.
      CURSOR employee_cur
      IS
         SELECT *
           FROM employees;
   BEGIN
      --Open cursor so you can use it.      
      OPEN employee_cur;
      --Go through all employees.
      LOOP
         --Load current row from cursor into employee record. 
         FETCH employee_cur
          INTO employee_rec;
         --Loop until cursor attribute signals that no rows are found.
         EXIT WHEN employee_cur%NOTFOUND;
         DBMS_OUTPUT.put_line (   employee_rec.employee_id
                               || ', '
                               || employee_rec.first_name
                              );
      END LOOP;

      CLOSE employee_cur;
   EXCEPTION
      --Remember to close cursor even if there was some error.
      WHEN OTHERS
      THEN
         IF employee_cur%ISOPEN
         THEN
            CLOSE employee_cur;
         END IF;
   END get_all_employees;
BEGIN
   get_all_employees ();
END;

This PL/SQL block performs following:
Declare the cursor.
Declare а record based on that cursor.
Open the cursor.
Fetch rows until there are no rows left.
Close cursor.
Handle exception and close cursor if it is not closed.

You can see that in this way we have complete control of cursor variable and cursor initialization, fetching and so on.

PL/SQL (Forms) Word and Excel manipulation using OLE (OLE2)

Introduction

Several years ago I was given requirement for excel/word reporting through PL/SQL Oracle Forms 10g application. One catch was that these reports need to fill existing reports with data and I can not use reporting tool of any kind. Because I came from Java and OO word I started developing Java bean that will be inserted into Forms. I had idea to create reports through this bean (using Apache POI) and then I will fuse this bean into Forms applet. I was almost done, but then I find out that Oracle deliver library that can (among other things) handle Forms <-> Word or Excel communication called WEBUTIL. So I start to leverage this library and came up with following PL/SQL package for Word and Excel communication using this package and OLE2. I hope you will find them useful I will not comment them because they are quite complex, but if you have questions please comment (or ask me privately) and I will try to response in shortest time possible.

Excel package
Package specification:
PACKAGE excel
IS
   /*
             Global excel.Application Object --> this represent excel Object.
     */
   appl_id   client_ole2.obj_type;

   /*
           Open file that act as template. Parameter are:
           _application_ -- global word parameter that we initialize at 
           begining.
           _file_ -- file name we wish to open --> it can be from database, or filesystem...
   */
   FUNCTION file_open (application client_ole2.obj_type, FILE VARCHAR2)
      RETURN client_ole2.obj_type;

   /*
           Close current file.
   */
   PROCEDURE file_close (document client_ole2.obj_type);

   /*
           Saves current file (It is useful if we need to save current 
           file using another name)
   */
   PROCEDURE file_save_as (document client_ole2.obj_type, FILE VARCHAR2);

   /*
           Isert number (not formated)
           x - horizontal axei.
           y - vertical axis.
           v - value.
   */
   PROCEDURE insert_number (
      worksheet   client_ole2.obj_type,
      x           NUMBER,
      y           NUMBER,
      v           NUMBER
   );

   /*
           Insert number and format it as decimal value. 
           x - horizontal axei.
           y - vertical axis.
           v - value.
           Napomena: !!!THIS DOES NOT WORK IN EXCEL 2007!!!
   */
   PROCEDURE insert_number_decimal (
      worksheet   client_ole2.obj_type,
      x           NUMBER,
      y           NUMBER,
      v           NUMBER
   );

   /*
           Insert characters  (not formated)
           x - horizontal axei.
           y - vertical axis.
           v - value.
   */
   PROCEDURE insert_char (
      worksheet   client_ole2.obj_type,
      x           NUMBER,
      y           NUMBER,
      v           VARCHAR2
   );

   /*
           Insert character - formated
           color - numbers (15 for example is gray)           
           style - BOLD' or 'ITALIC'
           x - horizontal axei.
           y - vertical axis.
           v - value.
   */
   PROCEDURE insert_char_formated (
      worksheet   client_ole2.obj_type,
      x           NUMBER,
      y           NUMBER,
      v           VARCHAR2,
      color       NUMBER,
      style       VARCHAR2
   );

   /*
           Set autofit on whole sheet.
   */
   PROCEDURE set_auto_fit (worksheet client_ole2.obj_type);

   /*
           Set autofit for range r. For example. r can be: 'A2:E11'
   */
   PROCEDURE set_auto_fit_range (worksheet client_ole2.obj_type, r VARCHAR2);

   /*
           Put decimal format (0.00) on range r.
   */
   PROCEDURE set_decimal_format_range (
      worksheet   client_ole2.obj_type,
      r           VARCHAR2
   );

   /*
           Create new workbook.
   */
   FUNCTION new_workbook (application client_ole2.obj_type)
      RETURN client_ole2.obj_type;

   /*
           Create new worksheet.
   */
   FUNCTION new_worksheet (workbook client_ole2.obj_type)
      RETURN client_ole2.obj_type;

   /*
           Saves file in client tempfolder (It is necessary to save file if edit template).
   */
   FUNCTION download_file (
      file_name         IN   VARCHAR2,
      table_name        IN   VARCHAR2,
      column_name       IN   VARCHAR2,
      where_condition   IN   VARCHAR2
   )
      RETURN VARCHAR2;

   /*
           Run macro on client excel document.
   */
   PROCEDURE run_macro_on_document (
      document   client_ole2.obj_type,
      macro      VARCHAR2
   );

   /*
           Limit network load...not important.
   */
   PROCEDURE insert_number_array (
      worksheet   client_ole2.obj_type,
      x           NUMBER,
      y           NUMBER,
      v           VARCHAR2
   );
END;
Package body:
PACKAGE BODY excel
IS
   FUNCTION file_open (application client_ole2.obj_type, FILE VARCHAR2)
      RETURN client_ole2.obj_type
   IS
      arg_list    client_ole2.list_type;
      document    client_ole2.obj_type;
      documents   client_ole2.obj_type;
   BEGIN
      arg_list := client_ole2.create_arglist;
      documents := client_ole2.invoke_obj (application, 'Workbooks');
      client_ole2.add_arg (arg_list, FILE);
      document := client_ole2.invoke_obj (documents, 'Open', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.RELEASE_OBJ (documents);
      RETURN document;
   END file_open;

   PROCEDURE file_save_as (document client_ole2.obj_type, FILE VARCHAR2)
   IS
      arg_list   client_ole2.list_type;
   BEGIN
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, FILE);
      client_ole2.invoke (document, 'SaveAs', arg_list);
      client_ole2.destroy_arglist (arg_list);
   END file_save_as;

   FUNCTION new_workbook (application client_ole2.obj_type)
      RETURN client_ole2.obj_type
   IS
      workbook    client_ole2.obj_type;
      workbooks   client_ole2.obj_type;
   BEGIN
      workbooks := client_ole2.get_obj_property (application, 'Workbooks');
      workbook := client_ole2.invoke_obj (workbooks, 'Add');
      client_ole2.RELEASE_OBJ (workbooks);
      RETURN workbook;
   END new_workbook;

   FUNCTION new_worksheet (workbook client_ole2.obj_type)
      RETURN client_ole2.obj_type
   IS
      worksheets   client_ole2.obj_type;
      worksheet    client_ole2.obj_type;
   BEGIN
      worksheets := client_ole2.get_obj_property (workbook, 'Worksheets');
      worksheet := client_ole2.invoke_obj (worksheets, 'Add');
      client_ole2.RELEASE_OBJ (worksheets);
      RETURN worksheet;
   END new_worksheet;

   PROCEDURE file_close (document client_ole2.obj_type)
   IS
   BEGIN
      client_ole2.invoke (document, 'Close');
   END file_close;

   /*
       Macro:    Cells(3, 4).Value = 3
                   Cells(3, 4).Select
                   Selection.NumberFormat = "0.00"
   */
   PROCEDURE insert_number_decimal (
      worksheet   client_ole2.obj_type,
      x           NUMBER,
      y           NUMBER,
      v           NUMBER
   )
   IS
      args        client_ole2.list_type;
      cell        client_ole2.obj_type;
      selection   client_ole2.obj_type;
   BEGIN
      IF v IS NOT NULL
      THEN
         args := client_ole2.create_arglist;
         client_ole2.add_arg (args, x);
         client_ole2.add_arg (args, y);
         cell := client_ole2.get_obj_property (worksheet, 'Cells', args);
         client_ole2.destroy_arglist (args);
         client_ole2.set_property (cell, 'Value', v);
         client_ole2.invoke (cell, 'Select');
         selection := client_ole2.invoke_obj (appl_id, 'Selection');
         client_ole2.set_property (selection, 'Numberformat', '#.##0,00');
         client_ole2.RELEASE_OBJ (selection);
         client_ole2.RELEASE_OBJ (cell);
      END IF;
   END;

   /* Macro:
                       Cells(x, y).Value = v
   */
   PROCEDURE insert_number (
      worksheet   client_ole2.obj_type,
      x           NUMBER,
      y           NUMBER,
      v           NUMBER
   )
   IS
      args   client_ole2.list_type;
      cell   ole2.obj_type;
   BEGIN
      IF v IS NOT NULL
      THEN
         args := client_ole2.create_arglist;
         client_ole2.add_arg (args, x);
         client_ole2.add_arg (args, y);
         cell := client_ole2.get_obj_property (worksheet, 'Cells', args);
         client_ole2.destroy_arglist (args);
         client_ole2.set_property (cell, 'Value', v);
         client_ole2.RELEASE_OBJ (cell);
      END IF;
   END;


   PROCEDURE insert_char (
      worksheet   client_ole2.obj_type,
      x           NUMBER,
      y           NUMBER,
      v           VARCHAR2
   )
   IS
      args   client_ole2.list_type;
      cell   client_ole2.obj_type;
   BEGIN
      IF v IS NOT NULL
      THEN
         args := client_ole2.create_arglist;
         client_ole2.add_arg (args, x);
         client_ole2.add_arg (args, y);
         cell := client_ole2.get_obj_property (worksheet, 'Cells', args);
         client_ole2.destroy_arglist (args);
         client_ole2.set_property (cell, 'Value', v);
         client_ole2.RELEASE_OBJ (cell);
      END IF;
   END;

  
   /*
           Macro:
                       Cells(x, y).Value = v
                       Cells(x, y).Select
                       Selection.Interior.ColorIndex = color
                       if (style in 'BOLD')
                           Selection.Font.Bold = True
                       else if (style in 'ITALIC')
                           Selection.Font.Italic = True
   */
   PROCEDURE insert_char_formated (
      worksheet   client_ole2.obj_type,
      x           NUMBER,
      y           NUMBER,
      v           VARCHAR2,
      color       NUMBER,
      style       VARCHAR2
   )
   IS
      args        client_ole2.list_type;
      cell        client_ole2.obj_type;
      selection   client_ole2.obj_type;
      font        client_ole2.obj_type;
      interior    client_ole2.obj_type;
   BEGIN
      IF v IS NOT NULL
      THEN
         args := client_ole2.create_arglist;
         client_ole2.add_arg (args, x);
         client_ole2.add_arg (args, y);
         cell := client_ole2.get_obj_property (worksheet, 'Cells', args);
         client_ole2.destroy_arglist (args);
         client_ole2.set_property (cell, 'Value', v);
         client_ole2.invoke (cell, 'Select');
         selection := client_ole2.invoke_obj (appl_id, 'Selection');
         font := client_ole2.invoke_obj (selection, 'Font');
         interior := client_ole2.invoke_obj (selection, 'Interior');

         IF UPPER (style) IN ('BOLD', 'ITALIC')
         THEN
            client_ole2.set_property (font, style, TRUE);
         END IF;

         client_ole2.set_property (interior, 'ColorIndex', color);
         client_ole2.RELEASE_OBJ (interior);
         client_ole2.RELEASE_OBJ (font);
         client_ole2.RELEASE_OBJ (selection);
         client_ole2.RELEASE_OBJ (cell);
      END IF;
   END;

   /*
           Macro:
                       Range(r).Select
                       Selection.Columns.AutoFit
                       Cells(1,1).Select
   */
   PROCEDURE set_auto_fit_range (worksheet client_ole2.obj_type, r VARCHAR2)
   IS
      args        client_ole2.list_type;
      --range
      rang        client_ole2.obj_type;
      selection   client_ole2.obj_type;
      colum       client_ole2.obj_type;
      cell        client_ole2.obj_type;
   BEGIN
      args := client_ole2.create_arglist;
      client_ole2.add_arg (args, r);
      rang := client_ole2.get_obj_property (worksheet, 'Range', args);
      client_ole2.destroy_arglist (args);
      client_ole2.invoke (rang, 'Select');
      selection := client_ole2.invoke_obj (appl_id, 'Selection');
      colum := client_ole2.invoke_obj (selection, 'Columns');
      client_ole2.invoke (colum, 'AutoFit');
      --now select upper (1,1) for deselection.      
      args := client_ole2.create_arglist;
      client_ole2.add_arg (args, 1);
      client_ole2.add_arg (args, 1);
      cell := client_ole2.get_obj_property (worksheet, 'Cells', args);
      client_ole2.invoke (cell, 'Select');
      client_ole2.destroy_arglist (args);
      client_ole2.RELEASE_OBJ (colum);
      client_ole2.RELEASE_OBJ (selection);
      client_ole2.RELEASE_OBJ (rang);
   END set_auto_fit_range;

   /*
           Macro:
                       Range(r).Select
                       Selection.Numberformat = "0.00"
                       Cells(1,1).Select
   */
   PROCEDURE set_decimal_format_range (
      worksheet   client_ole2.obj_type,
      r           VARCHAR2
   )
   IS
      args        client_ole2.list_type;
      --range
      rang        client_ole2.obj_type;
      selection   client_ole2.obj_type;
      --colum Client_OLE2.Obj_Type;
      cell        client_ole2.obj_type;
   BEGIN
      args := client_ole2.create_arglist;
      client_ole2.add_arg (args, r);
      rang := client_ole2.get_obj_property (worksheet, 'Range', args);
      client_ole2.destroy_arglist (args);
      client_ole2.invoke (rang, 'Select');
      selection := client_ole2.invoke_obj (appl_id, 'Selection');
      --colum:= Client_OLE2.invoke_obj(selection, 'Columns');
      client_ole2.set_property (selection, 'Numberformat', '#.##0,00');
      --Client_OLE2.invoke(colum, 'AutoFit');
      --now select upper (1,1) for deselection.      
      args := client_ole2.create_arglist;
      client_ole2.add_arg (args, 1);
      client_ole2.add_arg (args, 1);
      cell := client_ole2.get_obj_property (worksheet, 'Cells', args);
      client_ole2.invoke (cell, 'Select');
      client_ole2.destroy_arglist (args);
      --Client_OLE2.release_obj(colum);
      client_ole2.RELEASE_OBJ (selection);
      client_ole2.RELEASE_OBJ (rang);
   END set_decimal_format_range;

   /*
           Macro:Cells.Select
                       Selection.Columns.AutoFit
                       Cells(1,1).Select
   */
   PROCEDURE set_auto_fit (worksheet client_ole2.obj_type)
   IS
      args        client_ole2.list_type;
      cell        client_ole2.obj_type;
      selection   client_ole2.obj_type;
      colum       client_ole2.obj_type;
   BEGIN
      cell := client_ole2.get_obj_property (worksheet, 'Cells');
      client_ole2.invoke (cell, 'Select');
      selection := client_ole2.invoke_obj (appl_id, 'Selection');
      colum := client_ole2.invoke_obj (selection, 'Columns');
      client_ole2.invoke (colum, 'AutoFit');
      --now select upper (1,1) for deselection.      
      args := client_ole2.create_arglist;
      client_ole2.add_arg (args, 1);
      client_ole2.add_arg (args, 1);
      cell := client_ole2.get_obj_property (worksheet, 'Cells', args);
      client_ole2.invoke (cell, 'Select');
      client_ole2.destroy_arglist (args);
      client_ole2.RELEASE_OBJ (colum);
      client_ole2.RELEASE_OBJ (selection);
      client_ole2.RELEASE_OBJ (cell);
   END set_auto_fit;

   PROCEDURE run_macro_on_document (
      document   client_ole2.obj_type,
      macro      VARCHAR2
   )
   IS
      arg_list             client_ole2.list_type;
      bookmarkcollection   client_ole2.obj_type;
      bookmarkobj          client_ole2.obj_type;
      selectionobj         client_ole2.obj_type;
   BEGIN
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, macro);
      client_ole2.invoke (excel.appl_id, 'Run', arg_list);
      client_ole2.destroy_arglist (arg_list);
   END;

   FUNCTION download_file (
      file_name         IN   VARCHAR2,
      table_name        IN   VARCHAR2,
      column_name       IN   VARCHAR2,
      where_condition   IN   VARCHAR2
   )
      RETURN VARCHAR2
   IS
      l_ok          BOOLEAN;
      c_file_name   VARCHAR2 (512);
      c_path        VARCHAR2 (255);
   BEGIN
      SYNCHRONIZE;
      c_path := client_win_api_environment.get_temp_directory (FALSE);

      IF c_path IS NULL
      THEN
         c_path := 'C:\';
      ELSE
         c_path := c_path || '\';
      END IF;

      c_file_name := c_path || file_name;
      l_ok :=
         webutil_file_transfer.db_to_client_with_progress
                                                   (c_file_name,
                                                    table_name,
                                                    column_name,
                                                    where_condition,
                                                    'Transfer on file system',
                                                    'Progress'
                                                   );
      SYNCHRONIZE;

      IF NOT l_ok
      THEN
         msg_popup ('File not found in database', 'E', TRUE);
      END IF;

      RETURN c_path || file_name;
   END download_file;
END;
Word package
Package specification
PACKAGE word
IS
   /*
           Global Word.Application Object --> represent word object.
   */
   appl_id   client_ole2.obj_type;

   /*
           Open file that act as template. Parameter are:
          _application_ -- global word parameter that we initialize at
          begining.
          _file_ -- file name we wish to open --> it can be from database, or filesystem...
   */
   FUNCTION file_open (application client_ole2.obj_type, FILE VARCHAR2)
      RETURN client_ole2.obj_type;

   /*
           Close current file.
   */
   PROCEDURE file_close (document client_ole2.obj_type);

   /*
           Saves current file (It is useful if we need to save current
          file using another name)
   */
   PROCEDURE file_save_as (document client_ole2.obj_type, FILE VARCHAR2);

   /*
           (Bizniss end of this whole package;) ) Inserts value in specific word bookmark.
           _dokcument_ -- Word document.
           _bookmark_ -- Name of bookmark that is defined in word template,
           _content_ --  Content we wish to insert into bookmark.
   */
   PROCEDURE insertafter_bookmark (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2,
      content    VARCHAR2
   );

   /*
           InsertAfter_Bookmark insert after bookmark and then delete that bookmark and this is not
           good if you itarate through values, so this one do not delete bookmark after insert.
           same paramters as previous one.
   */
   PROCEDURE replace_bookmark (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2,
      content    VARCHAR2
   );

   /*
           Saame as previous procedure but it handle next for you.
   */
   PROCEDURE insertafter_bookmark_next (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2,
      content    VARCHAR2
   );

   /*
           This one after value insert move itself on next row into table. When I say next I mean next-down.
           This is essential for iterating through word table (one row at the time)
           We need manualy create new row if it does not exists.!!!
   */
   PROCEDURE insertafter_bookmark_down (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2,
      content    VARCHAR2
   );

   /*
           Easy...delete bookmark,
   */
   PROCEDURE delete_bookmark (document client_ole2.obj_type, bookmark VARCHAR2);

   /*
           Create new table row (see InsertAfter_Bookmark_Next)
   */
   PROCEDURE insert_new_table_row (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2
   );

   /*
           Move bookmakr (ONLY IN TABLE) left, right, up, down.
           _direction_ can have following valyes'UP', 'DOWN', 'LEFT', 'RIGHT'
   */
   PROCEDURE move_table_bookmark (
      document    client_ole2.obj_type,
      bookmark    VARCHAR2,
      direction   VARCHAR2
   );

   /*
           File download.
           parametar _file_name_  -- client file name (name on client)
           _table_name_ -- Table name for where BLOB column is.
           _column_name_ -- BLOB column name that holds Word template.
           -where_condition_ -- filter.
   */
   FUNCTION download_file (
      file_name         IN   VARCHAR2,
      table_name        IN   VARCHAR2,
      column_name       IN   VARCHAR2,
      where_condition   IN   VARCHAR2
   )
      RETURN VARCHAR2;

   /*
           Calling macro's on bookmarks...only for test.
   */
   PROCEDURE run_macro_on_bookmark (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2,
      macro      VARCHAR2
   );

   PROCEDURE run_macro_on_document (
      document   client_ole2.obj_type,
      macro      VARCHAR2
   );
END;
Package body
PACKAGE BODY word
IS
   FUNCTION file_open (application client_ole2.obj_type, FILE VARCHAR2)
      RETURN client_ole2.obj_type
   IS
      arg_list    client_ole2.list_type;
      document    client_ole2.obj_type;
      documents   client_ole2.obj_type;
   BEGIN
      arg_list := client_ole2.create_arglist;
      documents := client_ole2.invoke_obj (application, 'documents');
      client_ole2.add_arg (arg_list, FILE);
      document := client_ole2.invoke_obj (documents, 'Open', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.RELEASE_OBJ (documents);
      RETURN document;
   END file_open;

   PROCEDURE file_close (document client_ole2.obj_type)
   IS
   BEGIN
      client_ole2.invoke (document, 'Close');
   --CLIENT_OLE2.RELEASE_OBJ(document);
   END file_close;

   PROCEDURE file_save_as (document client_ole2.obj_type, FILE VARCHAR2)
   IS
      arg_list   client_ole2.list_type;
   BEGIN
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, FILE);
      client_ole2.invoke (document, 'SaveAs', arg_list);
      client_ole2.destroy_arglist (arg_list);
   --CLIENT_OLE2.RELEASE_OBJ(document);
   END file_save_as;

   PROCEDURE replace_bookmark (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2,
      content    VARCHAR2
   )
   IS
      arg_list             client_ole2.list_type;
      bookmarkcollection   client_ole2.obj_type;
      bookmarkobj          client_ole2.obj_type;
      selectionobj         client_ole2.obj_type;
   BEGIN
      bookmarkcollection :=
                     client_ole2.invoke_obj (document, 'Bookmarks', arg_list);
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, bookmark);
      bookmarkobj :=
                client_ole2.invoke_obj (bookmarkcollection, 'Item', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.invoke (bookmarkobj, 'Select');
      selectionobj := client_ole2.invoke_obj (appl_id, 'Selection');
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, content);
      client_ole2.invoke (selectionobj, 'Delete');
      client_ole2.invoke (selectionobj, 'InsertAfter', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.RELEASE_OBJ (selectionobj);
      client_ole2.RELEASE_OBJ (bookmarkobj);
      client_ole2.RELEASE_OBJ (bookmarkcollection);
   END replace_bookmark;

   PROCEDURE insertafter_bookmark (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2,
      content    VARCHAR2
   )
   IS
      arg_list             client_ole2.list_type;
      bookmarkcollection   client_ole2.obj_type;
      bookmarkobj          client_ole2.obj_type;
      selectionobj         client_ole2.obj_type;
   BEGIN
      bookmarkcollection :=
                     client_ole2.invoke_obj (document, 'Bookmarks', arg_list);
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, bookmark);
      bookmarkobj :=
                client_ole2.invoke_obj (bookmarkcollection, 'Item', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.invoke (bookmarkobj, 'Select');
      selectionobj := client_ole2.invoke_obj (appl_id, 'Selection');
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, content);
      client_ole2.invoke (selectionobj, 'InsertAfter', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.RELEASE_OBJ (selectionobj);
      client_ole2.RELEASE_OBJ (bookmarkobj);
      client_ole2.RELEASE_OBJ (bookmarkcollection);
   END insertafter_bookmark;

   PROCEDURE insertafter_bookmark_next (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2,
      content    VARCHAR2
   )
   IS
      arg_list             client_ole2.list_type;
      bookmarkcollection   client_ole2.obj_type;
      bookmarkobj          client_ole2.obj_type;
      selectionobj         client_ole2.obj_type;
   BEGIN
      bookmarkcollection :=
                     client_ole2.invoke_obj (document, 'Bookmarks', arg_list);
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, bookmark);
      bookmarkobj :=
                client_ole2.invoke_obj (bookmarkcollection, 'Item', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.invoke (bookmarkobj, 'Select');
      selectionobj := client_ole2.invoke_obj (appl_id, 'Selection');
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, content || CHR (13));
      client_ole2.invoke (selectionobj, 'InsertAfter', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.RELEASE_OBJ (selectionobj);
      client_ole2.RELEASE_OBJ (bookmarkobj);
      client_ole2.RELEASE_OBJ (bookmarkcollection);
   END insertafter_bookmark_next;

   PROCEDURE insertafter_bookmark_down (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2,
      content    VARCHAR2
   )
   IS
      arg_list             client_ole2.list_type;
      bookmarkcollection   client_ole2.obj_type;
      bookmarkobj          client_ole2.obj_type;
      selectionobj         client_ole2.obj_type;
   BEGIN
      bookmarkcollection :=
                     client_ole2.invoke_obj (document, 'Bookmarks', arg_list);
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, bookmark);
      bookmarkobj :=
                client_ole2.invoke_obj (bookmarkcollection, 'Item', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.invoke (bookmarkobj, 'Select');
      selectionobj := client_ole2.invoke_obj (appl_id, 'Selection');
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, content);
      client_ole2.invoke (selectionobj, 'InsertAfter', arg_list);
      client_ole2.invoke (selectionobj, 'Cut');
      client_ole2.invoke (selectionobj, 'SelectCell');
      client_ole2.invoke (selectionobj, 'MoveDown');
      client_ole2.invoke (selectionobj, 'Paste');
      client_ole2.destroy_arglist (arg_list);
      client_ole2.RELEASE_OBJ (selectionobj);
      client_ole2.RELEASE_OBJ (bookmarkobj);
      client_ole2.RELEASE_OBJ (bookmarkcollection);
   END insertafter_bookmark_down;

   PROCEDURE delete_bookmark (document client_ole2.obj_type, bookmark VARCHAR2)
   IS
      arg_list             client_ole2.list_type;
      bookmarkcollection   client_ole2.obj_type;
      bookmarkobj          client_ole2.obj_type;
      selectionobj         client_ole2.obj_type;
   BEGIN
      bookmarkcollection :=
                     client_ole2.invoke_obj (document, 'Bookmarks', arg_list);
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, bookmark);
      bookmarkobj :=
                client_ole2.invoke_obj (bookmarkcollection, 'Item', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.invoke (bookmarkobj, 'Select');
      selectionobj := client_ole2.invoke_obj (appl_id, 'Selection');
      client_ole2.invoke (selectionobj, 'Delete');
      client_ole2.RELEASE_OBJ (selectionobj);
      client_ole2.RELEASE_OBJ (bookmarkobj);
      client_ole2.RELEASE_OBJ (bookmarkcollection);
   END delete_bookmark;

   PROCEDURE run_macro_on_bookmark (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2,
      macro      VARCHAR2
   )
   IS
      arg_list             client_ole2.list_type;
      bookmarkcollection   client_ole2.obj_type;
      bookmarkobj          client_ole2.obj_type;
      selectionobj         client_ole2.obj_type;
   BEGIN
      bookmarkcollection :=
                     client_ole2.invoke_obj (document, 'Bookmarks', arg_list);
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, bookmark);
      bookmarkobj :=
                client_ole2.invoke_obj (bookmarkcollection, 'Item', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.invoke (bookmarkobj, 'Select');
      selectionobj := client_ole2.invoke_obj (appl_id, 'Selection');
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, macro);
      client_ole2.invoke (word.appl_id, 'Run', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.RELEASE_OBJ (selectionobj);
      client_ole2.RELEASE_OBJ (bookmarkobj);
      client_ole2.RELEASE_OBJ (bookmarkcollection);
   END;

   PROCEDURE run_macro_on_document (
      document   client_ole2.obj_type,
      macro      VARCHAR2
   )
   IS
      arg_list             client_ole2.list_type;
      bookmarkcollection   client_ole2.obj_type;
      bookmarkobj          client_ole2.obj_type;
      selectionobj         client_ole2.obj_type;
   BEGIN
      --bookmarkCollection := CLIENT_OLE2.INVOKE_OBJ(document, 'Bookmarks', arg_list);
      --arg_list := CLIENT_OLE2.CREATE_ARGLIST;
      --CLIENT_OLE2.ADD_ARG(arg_list, bookmark);
      --bookmarkObj := CLIENT_OLE2.INVOKE_OBJ(bookmarkCollection, 'Item',arg_list);
      --CLIENT_OLE2.DESTROY_ARGLIST(arg_list);

      --CLIENT_OLE2.INVOKE(bookmarkObj, 'Select');
      --selectionObj := CLIENT_OLE2.INVOKE_OBJ(appl_id, 'Selection');
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, macro);
      client_ole2.invoke (word.appl_id, 'Run', arg_list);
      client_ole2.destroy_arglist (arg_list);
   --CLIENT_OLE2.RELEASE_OBJ(selectionObj);
   --CLIENT_OLE2.RELEASE_OBJ(bookmarkObj);
   --CLIENT_OLE2.RELEASE_OBJ(bookmarkCollection);
   END;

   PROCEDURE insert_new_table_row (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2
   )
   IS
      arg_list             client_ole2.list_type;
      bookmarkcollection   client_ole2.obj_type;
      bookmarkobj          client_ole2.obj_type;
      selectionobj         client_ole2.obj_type;
   BEGIN
      bookmarkcollection :=
                     client_ole2.invoke_obj (document, 'Bookmarks', arg_list);
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, bookmark);
      bookmarkobj :=
                client_ole2.invoke_obj (bookmarkcollection, 'Item', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.invoke (bookmarkobj, 'Select');
      selectionobj := client_ole2.invoke_obj (appl_id, 'Selection');
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, 1);
      client_ole2.invoke (selectionobj, 'InsertRowsBelow', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.RELEASE_OBJ (selectionobj);
      client_ole2.RELEASE_OBJ (bookmarkobj);
      client_ole2.RELEASE_OBJ (bookmarkcollection);
   END insert_new_table_row;

   PROCEDURE move_down_table_bookmark (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2
   )
   IS
      arg_list             client_ole2.list_type;
      bookmarkcollection   client_ole2.obj_type;
      bookmarkobj          client_ole2.obj_type;
      selectionobj         client_ole2.obj_type;
   BEGIN
      bookmarkcollection :=
                     client_ole2.invoke_obj (document, 'Bookmarks', arg_list);
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, bookmark);
      bookmarkobj :=
                client_ole2.invoke_obj (bookmarkcollection, 'Item', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.invoke (bookmarkobj, 'Select');
      selectionobj := client_ole2.invoke_obj (appl_id, 'Selection');
      client_ole2.invoke (selectionobj, 'Cut');
      client_ole2.invoke (selectionobj, 'SelectCell');
      client_ole2.invoke (selectionobj, 'MoveDown');
      client_ole2.invoke (selectionobj, 'Paste');
      client_ole2.RELEASE_OBJ (selectionobj);
      client_ole2.RELEASE_OBJ (bookmarkobj);
      client_ole2.RELEASE_OBJ (bookmarkcollection);
   END move_down_table_bookmark;

   PROCEDURE move_up_table_bookmark (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2
   )
   IS
      arg_list             client_ole2.list_type;
      bookmarkcollection   client_ole2.obj_type;
      bookmarkobj          client_ole2.obj_type;
      selectionobj         client_ole2.obj_type;
   BEGIN
      bookmarkcollection :=
                     client_ole2.invoke_obj (document, 'Bookmarks', arg_list);
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, bookmark);
      bookmarkobj :=
                client_ole2.invoke_obj (bookmarkcollection, 'Item', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.invoke (bookmarkobj, 'Select');
      selectionobj := client_ole2.invoke_obj (appl_id, 'Selection');
      client_ole2.invoke (selectionobj, 'Cut');
      client_ole2.invoke (selectionobj, 'SelectCell');
      client_ole2.invoke (selectionobj, 'MoveUp');
      client_ole2.invoke (selectionobj, 'Paste');
      client_ole2.RELEASE_OBJ (selectionobj);
      client_ole2.RELEASE_OBJ (bookmarkobj);
      client_ole2.RELEASE_OBJ (bookmarkcollection);
   END move_up_table_bookmark;

   PROCEDURE move_left_table_bookmark (
      document   client_ole2.obj_type,
      bookmark   VARCHAR2
   )
   IS
      arg_list             client_ole2.list_type;
      bookmarkcollection   client_ole2.obj_type;
      bookmarkobj          client_ole2.obj_type;
      selectionobj         client_ole2.obj_type;
   BEGIN
      bookmarkcollection :=
                     client_ole2.invoke_obj (document, 'Bookmarks', arg_list);
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, bookmark);
      bookmarkobj :=
                client_ole2.invoke_obj (bookmarkcollection, 'Item', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.invoke (bookmarkobj, 'Select');
      selectionobj := client_ole2.invoke_obj (appl_id, 'Selection');
      client_ole2.invoke (selectionobj, 'Cut');
      client_ole2.invoke (selectionobj, 'SelectCell');
      client_ole2.invoke (selectionobj, 'MoveUp');
      client_ole2.invoke (selectionobj, 'Paste');
      client_ole2.RELEASE_OBJ (selectionobj);
      client_ole2.RELEASE_OBJ (bookmarkobj);
      client_ole2.RELEASE_OBJ (bookmarkcollection);
   END move_left_table_bookmark;

   PROCEDURE move_table_bookmark (
      document    client_ole2.obj_type,
      bookmark    VARCHAR2,
      direction   VARCHAR2
   )
   IS
      arg_list             client_ole2.list_type;
      bookmarkcollection   client_ole2.obj_type;
      bookmarkobj          client_ole2.obj_type;
      selectionobj         client_ole2.obj_type;
   BEGIN
      bookmarkcollection :=
                     client_ole2.invoke_obj (document, 'Bookmarks', arg_list);
      arg_list := client_ole2.create_arglist;
      client_ole2.add_arg (arg_list, bookmark);
      bookmarkobj :=
                client_ole2.invoke_obj (bookmarkcollection, 'Item', arg_list);
      client_ole2.destroy_arglist (arg_list);
      client_ole2.invoke (bookmarkobj, 'Select');
      selectionobj := client_ole2.invoke_obj (appl_id, 'Selection');

      IF UPPER (direction) IN ('UP', 'DOWN', 'LEFT', 'RIGHT')
      THEN
         client_ole2.invoke (selectionobj, 'Cut');
         client_ole2.invoke (selectionobj, 'SelectCell');
         client_ole2.invoke (selectionobj, 'Move' || direction);
         client_ole2.invoke (selectionobj, 'Paste');
      END IF;

      client_ole2.RELEASE_OBJ (selectionobj);
      client_ole2.RELEASE_OBJ (bookmarkobj);
      client_ole2.RELEASE_OBJ (bookmarkcollection);
   END move_table_bookmark;

   FUNCTION download_file (
      file_name         IN   VARCHAR2,
      table_name        IN   VARCHAR2,
      column_name       IN   VARCHAR2,
      where_condition   IN   VARCHAR2
   )
      RETURN VARCHAR2
   IS
      l_ok          BOOLEAN;
      c_file_name   VARCHAR2 (512);
      c_path        VARCHAR2 (255);
   BEGIN
      SYNCHRONIZE;
      c_path := client_win_api_environment.get_temp_directory (FALSE);

      IF c_path IS NULL
      THEN
         c_path := 'C:\';
      ELSE
         c_path := c_path || '\';
      END IF;

      c_file_name := c_path || file_name;
      l_ok :=
         webutil_file_transfer.db_to_client_with_progress
                                                   (c_file_name,
                                                    table_name,
                                                    column_name,
                                                    where_condition,
                                                    'Transfer on file system',
                                                    'Progress'
                                                   );
      SYNCHRONIZE;
      RETURN c_path || file_name;
   END download_file;
END;
Simple test
PROCEDURE Call(c_prog IN VARCHAR2,param1 IN VARCHAR2 DEFAULT NULL,value1 IN VARCHAR2 DEFAULT NULL, 
                                    param2 IN VARCHAR2 DEFAULT NULL,value2 IN VARCHAR2 DEFAULT NULL) IS 
  list_id Paramlist; 
BEGIN 
   --Check if list exists. 
   list_id := Get_Parameter_List('param_list'); 
   IF NOT Id_Null(list_id) THEN 
     Destroy_Parameter_List(list_id); -- Ako postoji, unisti je! 
   END IF; 
 
   list_id := Create_Parameter_List('param_list'); 
 
   Add_Parameter(list_id, 'ps_sif',TEXT_PARAMETER, :Global.ps_sif); 
   Add_Parameter(list_id, 'frm_sif',TEXT_PARAMETER, :Global.frm_sif); 
   Add_Parameter(list_id, 'god_sif',TEXT_PARAMETER, :Global.god_sif); 
   Add_Parameter(list_id, 'ana_id',TEXT_PARAMETER, :Global.ana_id); 
   Add_Parameter(list_id, 'id_radnik',TEXT_PARAMETER, :Global.id_radnik); 
   Add_Parameter(list_id, 'forma',TEXT_PARAMETER, UPPER(c_prog)); 
 
   IF param1 IS NOT NULL THEN 
     Add_Parameter(list_id, param1,TEXT_PARAMETER, value1); 
   END IF; 
 
   IF param2 IS NOT NULL THEN 
     Add_Parameter(list_id, param2,TEXT_PARAMETER, value2); 
   END IF; 
 
 
   CALL_FORM(c_prog || '.FMX', NO_HIDE, DO_REPLACE, NO_QUERY_ONLY, list_id); 
 
END;