Icwri is a lightweight service oriented Java built-in script. It is a very simple script. It can help Java developers to build some applications interfaces and tools in a service oriented style. It can also help non-technical people to join the development team to write their own application scripts to enforce business rules and decisions. It can also be a good assistant language for Java developers.
Icwri service providers and consumers can be either Icwri scripts or Java classes. Icwri script can run all alone. But my primary purpose to invent Icwri script is to have an assistant language of Java.
Icwri can run on any system (UNIX/Linux, Windows, Mac, …) with JDK/JRE 1.5 or above installed. Icwri does not support Java SE 6 Script Engine because the service oriented design and the requirement to support Java SE 1.5 users.
Following are major resource URL’s of Icwri Script:
Following are major topics in this manual:
Most developers know OOP (Object Oriented Programming). OOP is a good development method to write high efficient code. But if the target system is complex, and you just want to use some of its service directly, it is not always the best way to invent objects for the system, and service oriented programming (SOP) becomes a better programming style. It is loose-coupling and easier to change. The obvious characters of SOP are direct business and technical independent.
For example, a store provides a service named "buyBeer". You can write this service as following code in Icwri style:
%service !buyBeer;
%uses
@brandsToBuy, @moneyPay, @priceLimit, #For inputs
@brandBought, @moneyReturned; #For outputs
%begin;
…<<script code>> …
brandBought:…;
moneyReturned:…;
%end;
You can also realize the service with java code in the similar style (see following chapters). The service implementation code can be a file on disk, or an URL in the Internet. Service consumers do not care the service implementation.
You can call the service with following Icwri script code:
!buyBeer.@brandsToBuy: `Qingdao, Carlsberg`;
!buyBeer.@moneyPay: 200;
!buyBeer.@priceLimit: 10;
!buyBeer();
#You can get !buyBeer.@brandBought and !buyBeer.@moneyReturned now.
Or:
!buyBeer.@priceLimit: 10;
!buyBeer(`Qingdao, Carlsberg`, 200);
#You can get !buyBeer.@brandBought and !buyBeer.@moneyReturned now.
Or following Java code:
Service svc= icwri.loadService("!buyBeer");
svc.setAttribute("@brandsToBuy", "Qingdao, Carlsberg");
svc.setAttribute("@moneyPay", 200);
svc.setAttribute("@priceLimit", 10);
svc.run();
Icwri can run on any system (UNIX/Linux, Windows, MAC, …) with JDK/JRE 1.5 or above installed. The kernel files include icwri.jar and service list files. The service list files can be rewrite by users. Icwri can be installed into any path. Following steps shows how to install Icwri and run some simple demo applications.
1. For Unix/Linux users
1) Extract the package
$ cd /opt
$ gzip -d icwri_0.361.tar.gz
$ tar vxf icwri_0.361.tar
2) Set environment
$ CLASSPATH=/opt/icwri_0.361/icwri.jar
$ export CLASSPATH
3) Run demo script application in style-1 (Service List - Service)
$ java lr.icwri.app.RunService -l /opt/icwri_0.361/config/IcwriApp.lst -s !helloWorld1
Hello World!
Icwri version 0.361
Or in style-2 (Script - Service)
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/HellpWorld2.icw -s !helloWorld2
Hello World!
Icwri version 0.361
Note:
"-s" argument specifies the service name.
"-l" argument specifies the service list (registry) file;
"-f" argument specifies the script file.
Note: Other demo acript applications include (Service "!testBonusPlan3A" requires GUI support):
java lr.icwri.app.RunService -l /opt/icwri_0.361/config/IcwriApp.lst -s !helloWorld1
java lr.icwri.app.RunService -l /opt/icwri_0.361/config/IcwriApp.lst -s !learnObject
java lr.icwri.app.RunService -l /opt/icwri_0.361/config/IcwriApp.lst -s !learnArray
java lr.icwri.app.RunService -l /opt/icwri_0.361/config/IcwriApp.lst -s !learnString
java lr.icwri.app.RunService -l /opt/icwri_0.361/config/IcwriApp.lst -s !testCommands1
java lr.icwri.app.RunService -l /opt/icwri_0.361/config/IcwriApp.lst -s !testCommands2
java lr.icwri.app.RunService -l /opt/icwri_0.361/config/IcwriApp.lst -s !testCommands3
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/HellpWorld2.icw -s !helloWorld2
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/HellpWorld2.icw -s !testString
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/HellpWorld2.icw -s !writeFile
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan1
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan2
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan2A
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan3
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan3A
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !readArray0 < /opt/icwri_0.361/samples/WorldCup.lst
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !readArray1 < /opt/icwri_0.361/samples/WorldCup.lst
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !readArray2 < /opt/icwri_0.361/samples/WorldCup.lst
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !readArray3 < /opt/icwri_0.361/samples/WorldCup.lst
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !readArray4 < /opt/icwri_0.361/samples/WorldCup.lst
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !readArray5 < /opt/icwri_0.361/samples/WorldCup.lst
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !readArray6 < /opt/icwri_0.361/samples/WorldCup.lst
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !analyzeData1 < /opt/icwri_0.361/samples/TestData
2. For Microsoft Windows users
1) Extract the package to directory, for example: c:\icwri_0.361
2) Set environment
SET CLASSPATH=c:/icwri_0.361/icwri.jar
3) Run demo script application in style-1 (Service List - Service)
$ java lr.icwri.app.RunService -l c:/icwri_0.361/config/IcwriApp.lst -s !helloWorld1
Hello World!
Icwri version 0.361
Or in style-2 (Script - Service)
$ java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/HellpWorld2.icw -s !helloWorld2
Hello World!
Icwri version 0.361
Note:
"-s" argument specifies the service name.
"-l" argument specifies the service list (registry) file;
"-f" argument specifies the script file.
Note: Other demo applications include (Service "!testBonusPlan3A" requires GUI support):
java lr.icwri.app.RunService -l c:/icwri_0.361/config/IcwriApp.lst -s !helloWorld1
java lr.icwri.app.RunService -l c:/icwri_0.361/config/IcwriApp.lst -s !learnObject
java lr.icwri.app.RunService -l c:/icwri_0.361/config/IcwriApp.lst -s !learnArray
java lr.icwri.app.RunService -l c:/icwri_0.361/config/IcwriApp.lst -s !learnString
java lr.icwri.app.RunService -l c:/icwri_0.361/config/IcwriApp.lst -s !testCommands1
java lr.icwri.app.RunService -l c:/icwri_0.361/config/IcwriApp.lst -s !testCommands2
java lr.icwri.app.RunService -l c:/icwri_0.361/config/IcwriApp.lst -s !testCommands3
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/HellpWorld2.icw -s !helloWorld2
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/HellpWorld2.icw -s !testString
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/HellpWorld2.icw -s !writeFile
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan1
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan2
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan2A
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan3
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan3A
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/formatter.icw -s !readArray0 < c:\icwri_0.361\samples\WorldCup.lst
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/formatter.icw -s !readArray1 < c:\icwri_0.361\samples\WorldCup.lst
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/formatter.icw -s !readArray2 < c:\icwri_0.361\samples\WorldCup.lst
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/formatter.icw -s !readArray3 < c:\icwri_0.361\samples\WorldCup.lst
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/formatter.icw -s !readArray4 < c:\icwri_0.361\samples\WorldCup.lst
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/formatter.icw -s !readArray5 < c:\icwri_0.361\samples\WorldCup.lst
java lr.icwri.app.RunScript -f c:/icwri_0.361/samples/formatter.icw -s !readArray6 < c:\icwri_0.361\samples\WorldCup.lst
You can find that there are 2 service invocation styles:
By convention the first simplest demo application is always named “Hello World”. You can find this demo script in "samples/HellpWorld2.icw". This file has defined 3 services including a very simple service named “!helloWorld2”. You can find that the names of the services in Icwri Script are all prefixed by character “!”. Following is the code:
... ...
################################################################################
#The Icwri service list file
%serviceList `../config/IcwriSystem.lst`;
################################################################################
%service !helloWorld2;
%uses;
%begin;
!sys_printLine( `Hello World!` );
!sys_printLine( !aboutIcwri() );
%end;
... ...
Icwri system treats text in a line after "#" as comments. Icwri commands are all prefixed by character “%”, for example: "%service", "%uses", "%begin", and "%end". It is not required to write one Icwri statement in one line, but each statement must be ended by character ";". Constant strings in Icwri script are enclose by back ticks (`) instead of apostrophe (‘) or quotation marks ("). So developers can use apostrophe (') or quotation marks (") in their strings without any escape characters.
The “%serviceList” statement specifies the service list file. Service list file is the service catalog or registry of syetem or user defined services. In this demo, the service list file is "../config/IcwriSystem.lst". The “uses” statement defines attributes used by the service. All lines between “%begin” statement and “%end” statement are the execution statements of the service. “!sys_printLine()” is a system defined service. The default behavior is to write message to a line on the console. “!aboutIcwri()” is another system defined service. It returns the version information of current Icwri Script engine.
Following is the output when running service “!helloWorld2” on UNIX/Linix:
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/HellpWorld2.icw -s !helloWorld2
Hello World!
Icwri version 0.35
Following ss the output when running service “!helloWorld2” on Windows:
C:\> java lr.icwri.app.RunScript -f C:/icwri_0.361/samples/HellpWorld2.icw -s !helloWorld2
Hello World!
Icwri version 0.35
Icwri services are dynamically invoked by checking a service registry named Icwri Service List. It is usually a file on a disk or in the Internet. Following is a sample Icwri Service List file (IcwriApp.lst):
# Generic Description
%icwriVersion:0.361
#%baseFilePath:
%include:file,IcwriSystem.lst
# Service list
!bonusPlan1:file,LalaBonus.icw
!testBonusPlan1:file,LalaBonus.icw
!bonusPlan2:file,LalaBonus.icw
!testBonusPlan2:file,LalaBonus.icw
!testBonusPlan2A:file,LalaBonus.icw
!bonusPlan3:file,LalaBonus.icw
!testBonusPlan3:file,LalaBonus.icw
!testBonusPlan3A:file,LalaBonus.icw
!testCommands1:file,test1.icw
!testCommands2:file,test1.icw
!testCommands3:file,test1.icw
Note: Icwri system treats text in a line after "#" as comments.
Following statement means the least required version of Icwri script engine is "0.361".
%icwriVersion:0.361
You can use "%baseFilePath" to specify the default path of Icwri script files and service list files like following:
%baseFilePath: /opt/icwri_0.361/config
If you do not set "%baseFilePath", system takes the current directory as the default path of Icwri script files and service list files.Following statement means "IcwriSystem.lst" file should be included in this Service List (You can also use a full path of the included file).
%include: file, IcwriSystem.lst
The included service list file can also be in the Internet. Following is a sample:%include: url, http://www.lrsolution.com/icwri/IcwriSystem.lst
You can also use non-default encoding (character-set ID defined in Java) for the include file. For example:
%include: file, IcwriSystem.lst, GBK
%include: url, http://www.lrsolution.com/icwri/IcwriSystem.lst, UTF-8
The most important content of Icwri Service List is service entries. Each service entry use following format:
<Service Name>:< Service type>,<Service path>
For example, Following statement registers an Icwri Service named "!helloWorld". The service is defined in a script file whose path is "test1.icw":
!helloWorld: file, test1.icw
Following statement registers an Icwri Service named "!bonusPlan1". The service is defined in a script file in "http://www.lrsolution.com/Icwri/LalaBonus.icw":
!helloWorld: url, http://www.lrsolution.com/Icwri/test1.icw
Following statement registers an Icwri Service named "!sys_printLine". The service is defined in a Java class named "lr.icwri.app.api.IOUtil".
!sys_printLine: java, lr.icwri.app.api.IOUtil
You can also use non-default encoding (character-set ID defined in Java) for the Services. For example:
!helloWorld: file, test1.icw, GBK
!helloWorld: url, http://www.lrsolution.com/Icwri/LalaBonus.icw, UTF-8
Under the "config" directory, you can find 2 service list files: "IcwriApp.lst" and "IcwriSystem.lst". "IcwriApp.lst" is a demo user service list. "IcwriSystem.lst" is the list of system provided services. "IcwriSystem.lst" is also included in "IcwriApp.lst". You can write your own service list. The nesting of "include" is not limited.
The"IcwriSystem.hlp" file is the help file to "IcwriSystem.lst". System does not read the ".hlp" files.
You can run the service defined in the service list file directly. For example you can use following command to run service “!helloWorld1” defined in service list file “IcwriApp.lst”:
java lr.icwri.app.RunService -l /opt/icwri_0.361/config/IcwriApp.lst -s !helloWorld1
You can also run the service defined in a script. The script must specify the system service list file. For example, you can use following command to run service “!helloWorld2” defined in script file “HellpWorld2.icw”:
java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/HellpWorld2.icw -s !helloWorld2
In script file “HellpWorld2.icw”, following statement is used to specify the system service list file "IcwriSystem.lst":
%serviceList `../config/IcwriSystem.lst`;
If you want to specify the character encoding of the service list file to be non-default, for example: GBK, you can use following statement:
%serviceList `../config/IcwriSystem.lst, GBK`;
There only 3 types of entities used by Icwri:
1) Constant (string or number)
Following are examples of constant:
123, 1.23, 1,23e5 # constant numbers
`abc`, `abc def` # constant strings
Constant strings in Icwri script are enclose by back ticks (`) instead of apostrophe (‘) or quotation marks ("). So developers can use apostrophe (') or quotation marks (") in their strings without any escape characters. The escape character of Icwri script is tilde (~). Following is a table showing how to use the escape character to express special characters in an Icwri string.
| Original Character | Escaped Sequence |
|---|---|
| LF (ASCII value=10) | ~LF |
| CR (ASCII value=13) | ~CR |
| ` (ASCII value=96) | ~BT |
| ~ (ASCII value=126) | ~TD |
| character of any ASCII value | ~nn (nn is the ASCII value in hexadecimal) |
Icwri script does not distinguish strings and numbers. In Icwri script following expression is true:
`12.3` = 12.3
See following chapters to know how to express a logical value (true or false) in Icwri script.
2) Service
You can define an Icwri service with either Icwri script or Java code. Then you should add this service description into the Icwri Service List described in previous chapters. Both Icwri script and Java application can be Icwri service consumers.
Following is snippet of a sample Icwri script (LalaBonus.icw) to define 2 services "!bonusPlan1" and "!testBonusPlan1". Service "!testBonusPlan1" calls service "!bonusPlan1".
%service !bonusPlan1;
%uses
@bonusRate:0.15, @totalSales, #Input
@bonus; #Output
%begin;
@bonus:@totalSales*@bonusRate;
%end;
%service !testBonusPlan1;
%uses;
%begin;
!bonusPlan1.@totalSales: !sys_inputLine(`Please input the total sales figure:`);
!bonusPlan1();
!sys_printLine( `The bonus is $` | !bonusPlan1.@bonus );
%end;
Note:
To implement an Icwri service with Java, you should compose a Java class to implement lr.icwri.app.Serviceable interface.
public interface Serviceable {
/**
* Get the Uses line of the service.
*
* @param serviceName
* @return The USES line of the service
* @throws IcwriException
*/
public String getUses(String serviceName) throws IcwriException;
/**
* Run the service code
*
* @param serviceName
* @param attrs
* @throws IcwriException
*/
public void run(String serviceName, Attributes attrs) throws IcwriException;
}
There are 2 methods in this interface. Use method "getUses" to return the "uses" statement. Use method "run" to implement the service.
Following are the simple implementations of system service "!sys_printLine", "!sys_print", and "!sys_inputLine":
public String getUses(String serviceName) throws IcwriException {
if (serviceName.equals("!sys_printLine"))
return "@message($1), @style($2);";
else if (serviceName.equals("!sys_print"))
return "@message($1), @style($2);";
else if (serviceName.equals("!sys_inputLine"))
return "@message($1), @style($2), @input($?);";
else
throw new IcwriException(IcwriException.ET_NO_SUCH_SERVICE, "No such Service - " + serviceName);
}
public void run(String serviceName, Attributes attrs) throws IcwriException {
String msg = attrs.getAttribute("@message").getStringValue();
String style = attrs.getAttribute("@style").getStringValue();
if (serviceName.equals("!sys_printLine")) {
if (style.equals("SWI")) {
JOptionPane.showMessageDialog(null, msg, "message", JOptionPane.PLAIN_MESSAGE);
} else {
System.out.println(msg);
}
} else if (serviceName.equals("!sys_print")) {
System.out.print(msg);
} else if (serviceName.equals("!sys_inputLine")) {
if (style.equals("SWI")) {
attrs.setAttribute("@input", JOptionPane.showInputDialog(msg));
} else {
try {
System.out.print(msg + "\n>> ");
attrs.setAttribute("@input", new BufferedReader(new InputStreamReader(System.in)).readLine());
} catch (IOException e) {
attrs.setAttribute("@input", "");
}
}
} else {
throw new IcwriException(IcwriException.ET_NO_SUCH_SERVICE, "No such Service - " + serviceName);
}
}
3) Attribute
Attributes must belong to a service. Attributes can be used as input, output or just internal used for a service. The format to quote an attribute is as following:
<service name>.<attribute name>
If you use following abbreviated format, it means that the service holding the attribute is the current service.<attribute name>
Using attributes without declaration in the "uses" statement is not permitted. Following "uses" statement declares 3 attributes:
%uses @v1, @v2, @v3;
Sometimes you want to give an initial value to the attributes. Following is a sample:%uses @v1:15.3, @v2:`good`, @v3;
The constants following a colon is the initial value. The initial value is applied only at the first time of an Icwri context loading a service.Sometimes you want to use an alias instead of the attribute name. The attribute alias must be declared in the "uses" statement in parentheses following the attribute. Attribute alias must be prefixed with "$". Following are samples:
%uses @v1($num1):15.3, @v2:`good`, @v3($num2);
$num1 is the alias of @v1 and $num2 is the alias of @v3. So you can use either $num1 or @v1 in your application code. So you can use either $num2 or @v3 in your application code.Why do we need attribute alias?
The traditional format to call a service is as following sample:
service1.attribute1: `value1`;
service1.attribute2: `value2`
service1.attribute3: `value3`
service1();
service1(`value1`, `value2`, `value3`);
Service invoked in this style is also called Function. System will always take "$0" as the Service name, "$1" as the first argument of the function, "$2" as the second argument of the function, "$3" as the third argument of the function… So you should declare aliases in the "uses" statement as following:
%uses attribute1($1), attribute2($2), attribute3($3),
All Icwri statements must begin with a command (prefixed by character "%") and end with a semicolon (;). There are 2 syntaxes:
<command> <expression>;
Or<command> <attribute>:<expression>;
The default command is "%do", which can be omitted. The "<attribute>" at the left side of colon is also called "Left Value". Following are steps to run a statement:
Following are samples of valid Left Values:
The "<expression>" can be a simple constant or attribute. It can also be a complex nested expression. Following are samples of valid expressions:
The commands used between “%begin” and “%end” statement are called Application Commands. There are 4 groups of Application Commands. The major usage of the Application Command is to control the execution flow.
1) Command "%do": Evaluate the expression and assignment then go to next step. Command "%do" can be omitted. Following are samples:
2) Commands "%if", "%elseif", "%else", and "%endif" are used to for branch choice. The branch choice can be nested. Following is a sample:
%if @totalSales<=@quota;
@bonus:0;
%if @totalSales < (@quota/2);
!sys_printLine( `Very poor performance!` );
%endif;
%elseif @totalSales > (@quota*2);
@bonus: (@totalSales-@quota*2)*(@bonusRate*2) + @quota*@bonusRate;
%else;
@bonus:(@totalSales-@quota)*@bonusRate;
%endif;
3) Commands "%loop" and "%endloop" are used to for loop execution. It can also be nested. Following is a sample:
%loop @v1<=5;
!sys_printLine( `loop: @v1=` | @v1 );
%endloop @v1:@v1+1;
4) Command "%break" can be used to jump out of the end of blocks labeled by the "%endloop" and "%endif" statements. You can also use an expression following command "%break". For example, if you want to jump out of 2 blocks, you can use "%break 2".
Following is a sample showing how to break out of "%endif" and "%endloop".
%service !testCommands3;
%uses @v1;
%begin;
@v1:1;
%loop @v1<=5;
!sys_printLine( `loop: @v1=` | @v1 );
%if @v1=3;
%break 2;
%endif;
%endloop @v1:@v1+1;
!sys_printLine( `out: @v1=` | @v1 );
%end;
If there are no more blocks to break out, the "%break" command will end the service. It is like a "return" command in other languages. See following example:
%service !testCommands3A;
%uses @v1;
%begin;
@v1:1;
%loop @v1<=5;
!sys_printLine( `loop: @v1=` | @v1 );
%if @v1=3;
%break 3;
%endif;
%endloop @v1:@v1+1;
!sys_printLine( `out: @v1=` | @v1 );
%end;
4) Command "%return" will end the service and set the result value to "$?". See following example:
%service !add;
%uses @v1($1), @v2($2), @v($?);
%begin;
%return @v1+@v2;
%end;
It is similar to following code:
%service !add;
%uses @v1($1), @v2($2), @v($?);
%begin;
@v: @v1+@v2;
%end;
If you want to quit the whole script process, you can call the "!sys_exit" service.
There are many types of operators which can be used in the Icwri expression in a statement. The priorities of operators are different. For example, the multiplication operator "*" take precedence of addition operator "+". You can use parentheses () to change the priority. Following is table to describe all operators in the sequence of priority:
| Operators | Description |
|---|---|
| "\\" | not (logical operator) |
| "**" | power |
| "*", "/" | multiplication, division |
| "+", "-" | addition, subtraction |
| "|" | concatenation (for 2 strings) |
| "=", ">", "<", ">=", "<=", "<>" "==", "/=" |
comparison operators for digits comparison operators for strings |
| "&&", "||", "^^" | and, or, xor (logical operators) |
Logical operators require logical values. Logical values include TRUE and FALSE. In Icwri, any number can be converted to logical value, and logical value can also be converted to a number. The conversion rule is as following:
For example "1+(2>3)" is a valid expression in Icwri script. The result is 0.
You can find this demo in "samples/LalaBonus.icw". It shows how to involve people with a little software programming knowledge to write business rules for an application system.
In the following “%serviceList” statement, “../config/IcwriSystem.lst” is specified as the service list.
%serviceList `../config/IcwriSystem.lst`;
The author of the script is Lucy, who is the CFO of a small company named "Lala". She wants to write the bonus plan for the company by herself. She chose Icwri script, which will be invoked by the financial software application system of her company.
The first plan is very simple. Any sales employees will get 15% of his/her total sales figure as the bonus. The code is as following:
%service !bonusPlan1;
%uses
@bonusRate:0.15, @totalSales, #Input
@bonus; #Output
%begin;
@bonus:@totalSales*@bonusRate;
%end;
%service !testBonusPlan1;
%uses;
%begin;
!bonusPlan1.@totalSales: !sys_inputLine(`Please input the total sales figure:`);
!bonusPlan1();
!sys_printLine( `The bonus is $` | !bonusPlan1.@bonus );
%end;
Following shows how to invoke the sample service:
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan1
Please input the total sales figure:
>> 10000
The bonus is $1500.00
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan1
Please input the total sales figure:
>> 10000
The bonus is $1500.00
Then Lucy wanted to simplify the application. She rewrote the code with attribute alias. Following is the code:
%service !bonusPlan2;
%uses
@bonusRate:0.15, @totalSales($1), #Input
@bonus($?); #Output
%begin;
@bonus:@totalSales*@bonusRate;
%end;
%service !testBonusPlan2;
%uses @n;
%begin;
@n: !sys_inputLine(`Please input the total sales figure:`);
!sys_printLine( `The bonus is $` | !bonusPlan2(@n) );
%end;
%service !testBonusPlan2A;
%uses @n;
%begin;
@n: !sys_inputLine(`Please input the total sales figure:`);
!bonusPlan2.@bonusRate: 0.2;
!sys_printLine( `The bonus is $` | !bonusPlan2(@n) );
%end;
Service "!testBonusPlan2" uses the default bonus rate (15%). Service "!testBonusPlan2A" sets new bonus rate (20%). Following shows how to invoke the sample service:
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan2
Please input the total sales figure:
>> 10000
The bonus is $1500.00
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan2A
Please input the total sales figure:
>> 10000
The bonus is $2000.0
Then Lucy changed the bonus plan to a more reasonable one. If the total sales figure is less than his/her quota, he/she will not get any bonus. On the contrary, if his/her total sales figure doubles his quota, he/she will get double bonus. Following is the code.
%service !bonusPlan3;
%uses
@bonusRate:0.15, @totalSales($1), @quota($2):10000, #Input
@bonus($?); #Output
%begin;
%if @totalSales<=@quota;
@bonus:0;
%if @totalSales < (@quota/2);
!sys_printLine( `Very poor performance!` );
%endif;
%elseif @totalSales > (@quota*2);
@bonus: (@totalSales-@quota*2)*(@bonusRate*2) + @quota*@bonusRate;
%else;
@bonus:(@totalSales-@quota)*@bonusRate;
%endif;
%end;
%service !testBonusPlan3;
%uses @n;
%begin;
@n: !sys_inputLine(`Please input the total sales figure:`);
!sys_printLine( `The bonus is $` | !bonusPlan3(@n) );
%end;
%service !testBonusPlan3A;
%uses @n;
%begin;
@n: !sys_inputLine( `Please input the total sales figure:`, `SWI` );
!sys_printLine( `The bonus is $` | !bonusPlan3(@n), `SWI` );
%end;
Service "!testBonusPlan3" uses command line for input/output. Service "!testBonusPlan3A" uses GUI for input/output. Following shows how to invoke the sample service:
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan3
Please input the total sales figure:
>> 10000
The bonus is $0
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan3
Please input the total sales figure:
>> 20000
The bonus is $1500.00
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/LalaBonus.icw -s !testBonusPlan3
Please input the total sales figure:
>> 30000
The bonus is $4500.00
If you want to call Icwri services in Java applications, the first step is to create an "IcwriContext" class with a specified Icwri Service List. Following is the sample code:
IcwriContext icwri = new IcwriContext();
icwri.builtFromFile("C:/Icwri/config/ShopApp.lst", null); //Service List is a file on disk
IcwriContext icwri = new IcwriContext();
icwri.builtFromUrl("http://www.lrsolution.com/icwri/ShopApp.lst", null); //Service List is from Internet
The second parameter of method "builtFromFile" and "builtFromUrl" is the encoding (character-set ID defined in Java).
Then you can load the target Icwri Service from the Icwri context, set attributes if required, then call the "run" method. The returned data is stored in an attribute with alias "$?" and other attributes used by the service.
Service svc= icwri.loadService("!buyBeer");
svc.setAttribute("@brandsToBuy", "Qingdao, Carlsberg");
svc.setAttribute("@moneyPay", 200);
svc.setAttribute("@priceLimit", 10);
svc.run();
Now it seems that all services should be defined in a file on disk or in a data stream from internet. If you want to call your dynamic script code directly in Java, you can define your service directly into the context. For example:
String serviceName= "!testSvc";
String code= "%service !testSvc; %uses; %begin; !sys_printLine( `Hello World!` ); %end;";
icwri= new IcwriContext();
icwri.builtFromFile("D:/Icwri/config/IcwriApp.lst", null);
icwri.defineDynamicService(serviceName, code);
icwri.loadService(serviceName).run();
If you want to run a service defined in a script file directly, use following code:
icwri= new IcwriContext();
icwri.builtFromScriptFile(scriptFile, encoding);
icwri.loadService(serviceName).run();
Icwri has similar services for strings as other programming languages. Please read "config/IcwriSystem.hlp" file to understand the services prefixed with “string_”. Following is the service list for string manipulations:
!string_length: @string($1), @result($?);
!string_snippet: @string($1), @startPosition($2), @length($3), @result($?);
!string_changeCase: @string($1), @toLower, @toUpper, @result($?);
!string_trim: @string($1), @result($?);
!string_compare: @string1($1), @string2($2), @ignoreCase, @result($?);
!string_find: @string($1), @find($2), @ifBackward, @ifStartWith, @ifEndWith, @position($?);
!string_match: @string($1), @match($2), @result($?);
!string_replace: @string($1), @replacePattern($2), @replacement($3), @result($?);
!string_getColumn: @string($1), @delimiter($2), @colNo($3),
@colData($?), @beginPos, @len;
There is a demo service named "!learnString" to test system provided string services:
%service !learnString;
%uses @str1:`South Africa World Cup 2010`,
@str2:` Cup `,
@pattern:`.*\d\d\d\d`,
@tmp;
%begin;
!sys_printLine( `str1= '` | @str1 | `'` );
!sys_printLine( `Length of str1=` | !string_length(@str1) );
!sys_printLine( `Get a snippet of str1= '` | !string_snippet(@str1, 14, 9) | `'` );
!string_changeCase.@toUpper =1;
!sys_printLine( `toUpper of str1= '` | !string_changeCase(@str1) | `'` );
@str2: !string_trim(@str2);
!sys_printLine( `Test Trim, str2= '` | @str2 | `'` );
@tmp: !string_compare(@str1, @str2);
%if @tmp<0;
!sys_printLine( `str1 < str2` );
%elseif @tmp>0;
!sys_printLine( `str1 > str2` );
%else;
!sys_printLine( `str1 = str2` );
%endif;
!sys_printLine( `Test Find, str2 position in str1= ` | !string_find(@str1, @str2) );
@tmp: !string_compare(@str1, @pattern);
%if @tmp>0;
!sys_printLine( `str1 matches the pattern - ` | @pattern );
%else;
!sys_printLine( `str1 does not match the pattern - ` | @pattern );
%endif;
!sys_printLine( `Replace the year number in str1 to null - ` | !string_replace(@str1, `\d\d\d\d`, ``) );
%end;
Following shows how to invoke the service "!learnString":
# java lr.icwri.app.RunService -l /opt/icwri_0.361/config/IcwriApp.lst -s !learnString
str1= 'South Africa World Cup 2010'
Length of str1=27
Get a snippet of str1= 'World Cup'
toUpper of str1= 'South Africa World Cup 2010'
Test Trim, str2= 'Cup'
str1 > str2
Test Find, str2 position in str1= 20
str1 matches the pattern - .*\d\d\d\d
Replace the year number in str1 to null - South Africa World Cup
Icwri script has limited Object and Array support with system provided services. Developer can create an Object or Array in the system which means to register an Object or Array in the Icwri context. Developer can use this Object or Array until he/she deletes the Object or Array.
Developer can read/write properties of the Object. Object can be cloned. Icwri service list file "IcwriSystem.lst" and help file "IcwriSystem.hlp" show more details.
Following is a demo application using Object. It clones an object and adds/changes its properties.
%service !learnObject;
%uses @person1:`Person1`, @user1:`User1`;
%begin;
!object_create(@person1);
!object_setProperty(@person1, `name`, `Liu Rui`);
!object_setProperty(@person1, `age`, 40);
!object_setProperty(@person1, `job`, `engineer`);
!object_clone(@person1, @user1);
!object_setProperty(@user1, `job`, `developer`);
!object_setProperty(@user1, `userID`, `liurui`);
!sys_printLine( `Person1 name= ` | !object_getProperty(@person1, `name` ) );
!sys_printLine( `Person1 age= ` | !object_getProperty(@person1, `age` ) );
!sys_printLine( `Person1 job= ` | !object_getProperty(@person1, `job` ) );
!sys_printLine( `User1 name= ` | !object_getProperty(@user1, `name` ) );
!sys_printLine( `User1 age= ` | !object_getProperty(@user1, `age` ) );
!sys_printLine( `User1 job= ` | !object_getProperty(@user1, `job` ) );
!sys_printLine( `User1 userID= ` | !object_getProperty(@user1, `userID` ) );
%end;
Run this demo service "!learnObject" with following command:
$ java lr.icwri.app.RunService -l /opt/icwri_0.361/config/IcwriApp.lst -s !learnObject
Person1 name= Liu Rui
Person1 age= 40
Person1 job= engineer
User1 name= Liu Rui
User1 age= 40
User1 job= developer
User1 userID= liurui
Developer can manipulate items in the Array. Icwri service list file "IcwriSystem.lst" and help file "IcwriSystem.hlp" show more details.
Following is a demo application using Array:
%service !learnArray;
%uses @array1:`array1`, @item, @size;
%begin;
!array_create(@array1, 5);
!array_setProperty(@array1, `owner`, `Liu Rui`);
@size: !array_getQuantity(@array1);
@item:1;
%loop @item <= @size;
!array_setItem(@array1, @item, `Item` | @item);
%endloop @item: @item+1;
!sys_printLine( `array1 owner= ` | !array_getProperty(@array1, `owner` ) );
@item:1;
%loop @item <= @size;
!sys_printLine( `No.` | @item | `= ` | !array_getItem(@array1, @item ) );
%endloop @item: @item+1;
%end;
Run this demo service "!learnArray" with following command:
$ java lr.icwri.app.RunService -l /opt/icwri_0.361 /config/IcwriApp.lst -s !learnArray
array1 owner= Liu Rui
No.1= Item1
No.2= Item2
No.3= Item3
No.4= Item4
No.5= Item5
You can find this demo in "samples/formatter.icw". It shows how to use Icwri as a text formatter tool.
Now we have a file (/opt/icwri_0.361/samples/WorldCup.lst) which is the list of countries which have hold the FIFA Football World Cup Games:
No.01 Uruguay 1930
No.02 Italy 1934
No.03 France 1938
No.04 Brazil 1950
No.05 Switzerland 1954
No.06 Sweden 1958
No.07 Chile 1962
No.08 England 1966
No.09 Mexico 1970
No.10 Germany 1974
No.11 Argentina 1978
No.12 Spain 1982
No.13 Mexico 1986
No.14 Italy 1990
No.15 United States 1994
No.16 France 1998
No.17 Korea & Japan 2002
No.18 Germany 2006
No.19 South Africa 2010
We want to manipulate this list to get our expected results.
Service “!readArray0” reads and writes the list without any change. Following is the code:
%service !readArray0;
%uses;
%begin;
!array_read(`Array1`);
!array_write(`Array1`);
%end;
Run service “!readArray0” with following command:
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !readArray0 < /opt/icwri_0.361/samples/WorldCup.lst
No.01 Uruguay 1930
No.02 Italy 1934
No.03 France 1938
No.04 Brazil 1950
No.05 Switzerland 1954
No.06 Sweden 1958
No.07 Chile 1962
No.08 England 1966
No.09 Mexico 1970
No.10 Germany 1974
No.11 Argentina 1978
No.12 Spain 1982
No.13 Mexico 1986
No.14 Italy 1990
No.15 United States 1994
No.16 France 1998
No.17 Korea & Japan 2002
No.18 Germany 2006
No.19 South Africa 2010
Service “!readArray1” reads the list and writes the list without the first 2 lines and the last 1 line. Following is the code:
%service !readArray1;
%uses;
%begin;
!array_read.@skipFirstLines:2;
!array_read.@skipLastLines:1;
!array_read(`Array1`);
!array_write(`Array1`);
%end;
Run service “!readArray1” with following command:
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !readArray1 < /opt/icwri_0.361/samples/WorldCup.lst
No.03 France 1938
No.04 Brazil 1950
No.05 Switzerland 1954
No.06 Sweden 1958
No.07 Chile 1962
No.08 England 1966
No.09 Mexico 1970
No.10 Germany 1974
No.11 Argentina 1978
No.12 Spain 1982
No.13 Mexico 1986
No.14 Italy 1990
No.15 United States 1994
No.16 France 1998
No.17 Korea & Japan 2002
No.18 Germany 2006
Service “!readArray2” reads the list and writes only the lines including Italy. Following is the code:
%service !readArray2;
%uses;
%begin;
!array_read.@find:`Italy`;
!array_read(`Array1`);
!array_write(`Array1`);
%end;
Run service “!readArray2” with following command:
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !readArray2 < /opt/icwri_0.361/samples/WorldCup.lst
No.02 Italy 1934
No.14 Italy 1990
Service “!readArray3” reads the list and writes the lines matching a regular expression “.*S.*n.*”. Following is the code:
%service !readArray3;
%uses;
%begin;
!array_read.@match:`.*S.*n.*`;
!array_read(`Array1`);
!array_write(`Array1`);
%end;
Run service “!readArray3” with following command:
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !readArray3 < /opt/icwri_0.361/samples/WorldCup.lst
No.05 Switzerland 1954
No.06 Sweden 1958
No.12 Spain 1982
Service “!readArray4” reads the list and writes the list without the first “No.nn” columns. Following is the code:
%service !readArray4;
%uses;
%begin;
!array_read.@replacePattern:`^No\.\d\d`;
!array_read.@replacement:``;
!array_read(`Array1`);
!array_write(`Array1`);
%end;
Run service “!readArray4” with following command:
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !readArray4 < /opt/icwri_0.361/samples/WorldCup.lst
Uruguay 1930
Italy 1934
France 1938
Brazil 1950
Switzerland 1954
Sweden 1958
Chile 1962
England 1966
Mexico 1970
Germany 1974
Argentina 1978
Spain 1982
Mexico 1986
Italy 1990
United States 1994
France 1998
Korea & Japan 2002
Germany 2006
South Africa 2010
Service “!readArray5” reads and writes the list with only the country names in alphabetical order without duplication. Following is the code:
%service !readArray5;
%uses;
%begin;
!array_read.@replacePattern:`^No\.\d\d`;
!array_read.@replacement:``;
!array_read(`Array1`);
!array_read.@fromArray:`Array1`;
!array_read.@replacePattern:`\d\d\d\d`;
!array_read.@replacement:``;
!array_read.@ifTrim:1;
!array_read.@ifRemoveDuplication:1;
!array_read.@sortMode:3;
!array_read(`Array2`);
!array_write(`Array2`);
%end;
Run service “!readArray5” with following command:
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !readArray5 < /opt/icwri_0.361/samples/WorldCup.lst
Argentina
Brazil
Chile
England
France
Germany
Italy
Korea & Japan
Mexico
South Africa
Spain
Sweden
Switzerland
United States
Uruguay
Service “!readArray6” reads and writes the list with the country names moved at the first column in alphabetical order. Following is the code:
%service !readArray6;
%uses @num, @row, @rowData, @pos, @seq;
%begin;
!array_read.@delimiter:` `;
!array_read.@column:2;
!array_read.@sortMode:3;
!array_read(`Array1`);
@num: !array_getSize(`Array1`);
@row: 1;
%loop @row<=@num;
@rowData: !array_getItem(`Array1`, @row);
@seq: !string_snippet(@rowData, 1, 5);
!sys_printLine( !string_snippet(@rowData, 7, 999) | ` - ` | @seq );
%endloop @row:@row+1;
%end;
Run service “!readArray6” with following command:
$ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !readArray6 < /opt/icwri_0.361/samples/WorldCup.lst
Argentina 1978 - No.11
Brazil 1950 - No.04
Chile 1962 - No.07
England 1966 - No.08
France 1938 - No.03
France 1998 - No.16
Germany 1974 - No.10
Germany 2006 - No.18
Italy 1934 - No.02
Italy 1990 - No.14
Korea & Japan 2002 - No.17
Mexico 1970 - No.09
Mexico 1986 - No.13
South Africa 2010 - No.19
Spain 1982 - No.12
Sweden 1958 - No.06
Switzerland 1954 - No.05
United States 1994 - No.15
Uruguay 1930 - No.01
The next sample is an advanced one. It is for a real case. I have just completed a stress test for IBM CICS applications. I got following raw test data:
SAPSIREG,65
MCOM01,134
SAPS2001,136
MCOM01,95
SAPS2001,96
MCOM01,151
MCOM01,82
SAPS2001,83
SAPS2001,175
MCOM01,168
MCOM01,149
SAPS2001,169
SAPS2001,150
MCOM01,199
MCOM01,210
SAPS2001,201
SAPS2001,212
MCOM01,253
SAPS2001,255
MCOM01,239
MCOM01,225
SAPS2001,240
SAPS2001,226
MCOM01,240
SAPS2001,241
... ...
... ...
The first column is the CICS program name, and the second column is the time used (in milliseconds) to run this CICS program. Now I want to make a report for all tested programs to show numbers of running, average running time, maximum running time, and minimum running time.
The UNIX tool “awk” is a wonderful to do this calculation. However I can also write an Icwri script to do this. Icwri script can run on any operating systems. Following shows the test result. You can study the code by yourself.
| $ java lr.icwri.app.RunScript -f /opt/icwri_0.361/samples/formatter.icw -s !analyzeData1 < /opt/icwri_0.361/samples/TestData | ||||
| Program | Num | Average | Min | Max |
| MCOM01 | 251 | 1224.21 | 82 | 2729 |
| SAPSIREG | 1 | 65 | 65 | 65 |
| SAPS2001 | 249 | 1227.41 | 83 | 2730 |
Generic considerations of Icwri script:
Exception Types of lr.icwri.app.IcwriException:
| ID | Definition | Description |
|---|---|---|
| 0 | ET_UNKNOWN |
|
| 1 | ET_ENVIRONMENT | |
| 2 | ET_COMMANDLINE | Command syntax error. The syntax of command line is:\njava lr.icwri.app.Main [-t {file|url}] -l <Service list file/url> -s <Service> [-v <log level>] |
| 3 | ET_CANNOT_ACCESS_FILE | File not found or access security problem |
| 4 | ET_CANNOT_ACCESS_URL | Invalid URL |
| 5 | ET_CANNOT_ACCESS_INPUT_STREAM | |
| 6 | ET_INVALID_ENCODING | Invalid encoding (character-set ID defined in Java) |
| 1000 | ET_INVALID_SERVICE_LIST | |
| 1001 | ET_INVALID_SERVICE_NAME | Service name must be prefixed with character "!" |
| 1002 | ET_INVALID_ATTR_NAME | Attribute name must be prefixed with character "@" |
| 1003 | ET_INVALID_ALIAS_NAME | Alias name must be prefixed with character "$" |
| 1004 | ET_INVALID_LINE_IN_SERVICE_LIST | The syntax of service registry line is: <Service name>, {file|url|java}, path |
| 1005 | ET_INVALID_SERVICE_TYPE | Service types in the service list can only be "file", "url", or "java" |
| 1006 | ET_INVALID_CLASSPATH | The "classpath" defined in the service list is invalid |
| 1007 | ET_INVALID_USES | The format of "%uses" statement is invalid |
| 1008 | ET_NO_SUCH_SERVICE | The service is not defined in service list |
| 1009 | ET_SERVICE_NO_SUCH_ATTR | The attribute is not defined in the "%uses" statement of the service |
| 1010 | ET_SERVICE_NO_SUCH_ALIAS | The alias of the attribute is not defined in the "%uses" statement of the service |
| 1011 | ET_DEF_SERVICE_LIST_FIRST | The script file should define "service list" before any services |
| 2000 | ET_SCRIPT_FORMAT_INVILID | |
| 2001 | ET_SCRIPT_FORMAT_INVILID1 | Lack of "%endif" or "%endloop", or invalid "%break" statement |
| 2002 | ET_SCRIPT_FORMAT_INVILID2 | Lack of "%endif" or "%endloop", or invalid "%break" statement |
| 2003 | ET_SCRIPT_FORMAT_INVILID3 | Lack of "%endif" or "%endloop", or invalid "%break" statement |
| 2010 | ET_STATEMENT_NOT_MATCH | The "%if" and "%endif" are not matched. Or the "%loop" and "%endloop" are not matched |
| 2011 | ET_SERVICE_NOT_DEFINED_CORRECTLY | The commands sequence should be "%service", "%uses", "%begin", "%end" |
| 3000 | ET_COMMAND_INVILID | Invalid command |
| 3001 | ET_BREAK_LEVEL_INVILID | The level of the "%break" command is invalid |
| 3002 | ET_NO_MATCH_BRACKETS | There is no matched brackets |
| 3003 | ET_LEFT_VALUE_INVILID | The left value should be an attribute |
| 3004 | ET_STRING_INVILID | The string in Icwri script should be enclosed with backticks (`) |
| 3005 | ET_EXPRESSION_INVILID | The expression is invalid. For example there is invalid operator |
| 3006 | ET_OPERATOR_INVILID | The operator is invalid |
| 3007 | ET_CONSTANT_INVILID | The format of the constant (number or string) is invalid |
| 3008 | ET_MULTIPLE_SERVICE_LINE | There are more than 1 "%service" statement. Or service is not ended by "%end" statement properly |
| 3009 | ET_MULTIPLE_USES_LINE | There are more than 1 "%uses" statement. Or service is not ended by "%end" statement properly |
| 3010 | ET_MULTIPLE_BEGIN_LINE | There are more than 1 "%begin" statement. Or service is not ended by "%end" statement properly |
| 9000 | ET_VT_ARRAY_OUTOF_BOUNDS | Array item must larger or equal to 0 |
| 10000 | ET_APP | |
| 10001 | ET_NO_SUCH_OBJECT | Object not found |
| 10002 | ET_ARRAY_QUANTITY_INVALID | |
| 10003 | ET_ARRAY_ITEM_OUTOF_RANGE | |
| 10004 | ET_CANNOT_READ_FROM_SYSIN | |
| 10005 | ET_CANNOT_WRITE_TO_SYSOUT | |
| 10006 | ET_INDEX_OUTOF_RANGE |
Coding, testing, helping to answer questions and making documents are all appreciated. Welcome to contact liurui@lrsolution.com to report defects and tell your suggestions.
-- Liu Rui