Summary:
See also: Variables, Records, Data Types, User Types, Operators.
Starting with Genero BDL 2.20, you can use the Java Interface to import Java classes which can be instantiated in the BDL program. With the Java Interface, Genero BDL opens the doors to the free huge Java libraries, as well as commercial libraries for specific purposes. The methods of Java objects can be called with Java objects referenced in the BDL program, as well as with native BDL typed values like INTEGER, DECIMAL, CHAR.
Before starting with the Java Interface, if you are not familiar with Java and Object Oriented Programming, we strongly recommend that you learn more about this language from the different tutorials and courses you can find on the internet.
In order to use the Java Interface you need the following software, installed and properly configured:
The minimum required JRE version is Java 6.10.
--java-option=-Djava.class.path=<pathlist>
option to fglrun with the directories of the Java packages you want to
use.On some platforms like HP-UX, you must pay attention to additional configuration settings in order to use the Java Interface. For more details, see Operating System Specific Notes in the installation guide.
01
IMPORT JAVA java.util.regex.Pattern
Before creating a Java object in Genero BDL, you must declare a program variable to reference the object. The type of the variable must be the name of the Java class, and can be fully qualified if needed:
01
DEFINE p1 Pattern02
DEFINE p2 java.util.regex.Pattern
In Java, you can use the new operator to create a new object. The new operator does not exist in Genero BDL; you must use <ClassName>.create() instead. Assign the return value of the create() method to a program variable declared with the Java class name:
01
DEFINE sb StringBuffer02
LET sb = StringBuffer.create()
If the Java class constructor uses parameters, you must pass the parameters to the create() method:
01
LET sb1 = StringBuffer.create("abcdef") -- Uses StringBuffer(String str) constructor02
LET sb2 = StringBuffer.create(2048) -- Uses StringBuffer(int capacity) constructor
Note that you cannot use a full-qualifier class name when invoking the create() method:
01
LET sb = java.lang.StringBuffer.create("abcdef") -- Raises compiler error!
You can call a class method (static method in Java) without instantiating an object of the class. Static method invocation must be prefixed with the class name. In the next example, the compile() class method of Pattern class returns a new instance of a Pattern object:
01
DEFINE p Pattern02
LET p = Pattern.compile("[,\\s]+")
Note that if you define a variable with the same name as a Java class, you must fully qualify the class when calling static methods, as shown in the following example:
01
DEFINE Pattern Pattern02
DEFINE Matcher Matcher03
LET Pattern = java.util.regex.Pattern.compile("[a-z]+") -- static method, needs full qualifier04
LET Matcher = Pattern.matcher("abcdef") -- regular instance method, Pattern resolves to variable
Remember also that BDL variables are case-insensitive (Pattern = pattern).
Once the class has been instantiated as an object, and the object reference has been assigned to a variable, you can call a method of the Java object by using the variable as the prefix:
01
DEFINE m Matcher02
...03
LET m = p.matcher("aaabbb")04
DISPLAY m.matches()
In the above code example, the method in line #3 returns a new object of the class java.util.regex.Matcher and the method in line #4 returns a boolean.
When using the Java interface, you can instruct fglrun or fglcomp to pass Java VM
specific options during JNI initialization, by using the --java-option
command
line argument.
In the next example, fglrun will pass -verbose:gc
to the Java Virtual
Machine:
$ fglrun --java-option=-verbose:gc myprog.42r
If you want to pass several options to the JVM, repeat the --java-option
argument as in the following example:
$ fglrun --java-option=-verbose:gc --java-option=-esa myprog.42r
You may want to pass the Java class path as command line option to fglrun
with -Djava.class.path
option as in the next example:
$ fglrun --java-option=-Djava.class.path=$FGLDIR/lib/fgl.jar:$MYCLASSPATH myprog.42r
Regarding class path specification,
the java
runtime or javac
compiler provide the -cp
or -classpath
options but when loading the JVM library from fglrun
or fglcomp, only -Djava.class.path
option is supported by the JNI
interface.
See also the fglrun tool options.
Unlike Genero BDL, the Java language is case-sensitive. Therefore, when you write the name of a Java package, class or method in a .4gl source, it must match the exact name as if you were writing a Java program. The Genero fglcomp compiler takes care of this, and writes case-sensitive class and method names in the .42m pcode modules.
01
IMPORT JAVA java.util.regex.Pattern02
MAIN03
DEFINE p java.util.regex.PATTERN -- Note the case error04
END MAIN
If you compile the above code example, fglcomp will raise error -6622 at line 3, complaining that the "java/util/PATTERN" name cannot be found.
The Java language allows method overloading; the parameter count and the parameter data types of a method are part of the method identification. Thus, the same method name can be used to implement different versions of the Java method, taking different parameters:
01
DEFINE int2 SMALLINT, int4 INTEGER, flt FLOAT02
CALL myobj.display(int2) -- invokes method display(short) of the Java class03
CALL myobj.display(int4) -- invokes method display(int) of the Java class04
CALL myobj.display(flt) -- invokes method display(double) of the Java class05
CALL myobj.display(int2,int4) -- invokes method display(short,int) of the Java class
As described in the "Getting started with Java Interface" section, Java objects must be instantiated and referenced by a program variable. The object reference is stored in the variable and can be passed as a parameter or returned from a BDL function. Like built-in class objects, the Java objects are passed by reference. This means that the called function does not get a clone of the object, but rather a handle to the original object. The function can then manipulate and modify the original object provided by the caller:
01
IMPORT JAVA java.lang.StringBuffer02
03
MAIN04
DEFINE x java.lang.StringBuffer05
LET x = StringBuffer.create()06
CALL change(x)07
DISPLAY x.toString()08
END MAIN09
10
FUNCTION change(sb)11
DEFINE sb java.lang.StringBuffer12
CALL sb.append("abc")13
END FUNCTION
Similarly, Java object references can be returned from BDL functions:
01
IMPORT JAVA java.lang.StringBuffer02
03
MAIN04
DEFINE x java.lang.StringBuffer05
LET x = build()06
DISPLAY x.toString()07
END MAIN08
09
FUNCTION build()10
DEFINE sb java.lang.StringBuffer11
LET sb = StringBuffer.create() -- Creates a new object.12
CALL sb.append("abc")13
RETURN sb -- Returns the reference to the object, not a copy/clone.14
END FUNCTION
Like built-in class objects, Java objects do not need to be explicitly destroyed; as long as an object is referenced by a variable, on the stack or in an expression, it will remain. When the last reference to an object is removed, the object is destroyed automatically. The next example shows how a unique object can be referenced twice, using two variables:
01
FUNCTION test()02
DEFINE sb1, sb2 java.lang.StringBuffer -- Declare 2 variables to reference a StringBuffer object03
LET sb1 = StringBuffer.create() -- Create object and assign reference to variable04
LET sb2 = sb1 -- Same object is now referenced by 2 variables05
CALL sb1.append("abc") -- Object is modified through first variable06
CALL sb2.append("def") -- Object is modified through second variable07
DISPLAY sb1.toString() -- Shows content of StringBuffer object08
DISPLAY sb2.toString() -- Same output as previous line09
LET sb1 = NULL -- Object is only referenced by second variable10
END FUNCTION -- sb2 removed from stack, object is no longer referenced and is destroyed.
If a Java method returns an object, you can use the method call directly as an object reference to call another method:
01
IMPORT JAVA java.util.regex.Pattern02
MAIN03
DEFINE p Pattern04
LET p = Pattern.compile("a*b")05
IF p.matcher("aaaab").matches() THEN06
DISPLAY "It matches..."07
END IF08
END MAIN
In line #5, the matcher() method of object p is invoked and returns an object of type java.util.regex.Matcher. The object reference returned by the matcher() method can be directly used to invoke a method of the Matcher object (i.e. matches()), which returns a boolean.
Unlike Genero BDL, Java allows you to ignore the return value of a method (as in C/C++):
StringBuffer sb = new StringBuffer;
sb.append("abc"); -- returns a new StringBuffer object but is
ignored
Genero BDL allows you to call a Java method and ignore the return value:
01
IMPORT JAVA java.util.lang.StringBuffer02
MAIN03
DEFINE sb StringBuffer04
LET sb = StringBuffer.create()05
LET sb = sb.append("abc")06
CALL sb.append("def") -- typical usage07
END MAIN
Java classes can have object and class (static) fields. Java static class fields can be declared as final (read-only). It is not possible to change the object or class fields in BDL even if the field is not declared as static final; you can however read from it:
01
IMPORT JAVA java.lang.Integer02
MAIN03
DISPLAY Integer.MAX_VALUE04
END MAIN
Java and Genero BDL do not have the same built-in data types. Unlike Genero BDL, Java is a strongly typed language: You cannot call a method with a String if it was defined to get an int parameter. To call a Java method, Genero BDL typed values need to be converted to/from Java native types such as byte, int, short, char or data objects such as java.lang.String. If possible, the fglrun runtime system will do this conversion implicitly.
The fglcomp compiler will raise the error -6606, if the BDL data type does not match the Java (primitive) type, using Widening Primitive Conversions. For example, passing a DECIMAL when a Java double (BDL FLOAT) is expected will fail, but passing a SMALLFLOAT (Java float) when a Java double is expected will compile and run.
Genero has advanced proprietary data types such as DECIMAL, which do not have an equivalent primitive type or class in Java. For such BDL types, you need to use a specific Java class provided in the FGLDIR/lib/fgl.jar package, like com.fourjs.fgl.lang.FglDecimal. You can then manipulate the BDL specific value in the Java code. For more details about these specific types, see "Using the BDL DECIMAL type", and similar sections.
Genero also implements structured types with RECORDs, converted to com.fourjs.fgl.lang.FglRecord objects for Java.
The BDL static and dynamic arrays cannot be used to call Java methods. You must use Java Arrays instead.
Note that in some cases you need to explicitly cast with the new CAST() operator. See the section about CAST() operator for more details.
The following table shows the implicit conversions done by the Genero runtime system when a Java method is called, or when a Java method returns a value or object reference:
Genero Data Type | Java Primitive Type, Standard Class or BDL Java Class |
CHAR | java.lang.String |
VARCHAR | java.lang.String |
STRING | java.lang.String |
DATE | com.fourjs.fgl.lang.FglDate |
DATETIME | com.fourjs.fgl.lang.FglDateTime |
INTERVAL | com.fourjs.fgl.lang.FglInterval |
BIGINT | long (64-bit signed integer) |
INTEGER | int (32-bit signed integer) |
SMALLINT | short (16-bit signed integer) |
TINYINT | tinyint (8-bit signed integer) |
FLOAT | double (64-bit signed floating point number) |
float (32-bit signed floating point number) | |
DECIMAL | com.fourjs.fgl.lang.FglDecimal |
MONEY | com.fourjs.fgl.lang.FglDecimal |
BYTE | com.fourjs.fgl.lang.FglByteBlob |
TEXT | com.fourjs.fgl.lang.FglTextBlob |
RECORD | com.fourjs.fgl.lang.FglRecord |
Java Array | Native Java Array |
Unsupported conversions | |
Static Array | N/A |
Dynamic Array | N/A |
Built-in Class Object | N/A |
The DATE data type is a time type specific to Genero BDL. When calling a Java method with a BDL expression evaluating a DATE, the runtime system converts the BDL value to an instance of the com.fourjs.fgl.lang.FglDate class implemented in FGLDIR/lib/fgl.jar. You can then manipulate the DATE from within the Java code.
Note that you must add FGLDIR/lib/fgl.jar to the class path in order to compile Java code with com.fourjs.fgl.lang.FglDate class.
The com.fourjs.fgl.lang.FglDate class implements following:
Method | Description |
String toString() |
Converts the DATE value to a String object representing
the date in ISO format: YYYY-MM-DD |
static void valueOf(String val) |
Creates a new FglDate object from a String object representing a date in ISO format. |
In the Java code, you can convert the com.fourjs.fgl.lang.FglDate to a java.util.Calendar object as in the following example:
public static void useDate(FglDate d) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Calendar cal = Calendar.getInstance();
cal.setTime( sdf.parse(d.toString()) );
...
}
If you need to create an com.fourjs.fgl.lang.FglDate object in BDL, you can use the valueOf() class method as in the following example:
01
IMPORT JAVA com.fourjs.fgl.lang.FglDate02
MAIN03
DEFINE d com.fourjs.fgl.lang.FglDate04
LET d = FglDate.valueOf("2008-12-23")05
DISPLAY d.toString()06
END MAIN
The DATETIME data type is a time type specific to Genero BDL. When calling a Java method with a BDL expression evaluating a DATETIME, the runtime system converts the BDL value to an instance of the com.fourjs.fgl.lang.FglDateTime class implemented in FGLDIR/lib/fgl.jar. You can then manipulate the DATETIME from within the Java code.
Note that you must add FGLDIR/lib/fgl.jar to the class path in order to compile Java code with com.fourjs.fgl.lang.FglDateTime class.
The com.fourjs.fgl.lang.FglDateTime class implements following:
Field | Description |
final static int YEAR |
Time qualifier for year |
final static int MONTH |
Time qualifier for month |
final static int DAY |
Time qualifier for day |
final static int HOUR |
Time qualifier for hour |
final static int MINUTE |
Time qualifier for minute |
final static int SECOND |
Time qualifier for second |
final static int FRACTION |
Time qualifier for fraction (start qualifier) |
final static int FRACTION1 |
Time qualifier for fraction(1) (end qualifier) |
final static int FRACTION2 |
Time qualifier for fraction(2) (end qualifier) |
final static int FRACTION3 |
Time qualifier for fraction(3) (end qualifier) |
final static int FRACTION4 |
Time qualifier for fraction(4) (end qualifier) |
final static int FRACTION5 |
Time qualifier for fraction(5) (end qualifier) |
Method | Description |
String toString() |
Converts the DATETIME value to a String object representing a datetime in ISO format. |
static void valueOf(String val) |
Creates a new FglDateTime object from a String object
representing a datetime value in ISO format: YYYY-MM-DD hh:mm:ss.fff |
static void valueOf(String val, |
Creates a new FglDateTime object from a String object representing a datetime value in ISO format, using the qualifiers passed as parameter. |
static int encodeTypeQualifier( |
Returns the encoded type qualifier for a datetime with to
datetime qualifiers passed: encoded qualifier = (length * 256) + (startUnit * 16) + endUnit Where length defines the total number of significant digits in this time data. For example, with DATETIME YEAR TO MINUTE: startUnit = YEAR length = 12 (YYYYMMDDhhmm) endUnit = MINUTE |
In the Java code, you can convert the com.fourjs.fgl.lang.FglDateTime to a java.util.Calendar object as in the following example:
public static void useDatetime(FglDateTime dt) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd
HH:mm:ss.SSS");
Calendar cal = Calendar.getInstance();
cal.setTime( sdf.parse(dt.toString()) );
...
}
If you need to create an com.fourjs.fgl.lang.FglDateTime object in BDL, you can use the valueOf() class method as in the following example:
01
IMPORT JAVA com.fourjs.fgl.lang.FglDateTime02
MAIN03
DEFINE dt com.fourjs.fgl.lang.FglDateTime04
LET dt = FglDateTime.valueOf("2008-12-23 11:22:33.123")05
LET dt = FglDateTime.valueOf("11:22:33.123", FglDateTime.HOUR, FglDateTime.FRACTION3)06
DISPLAY dt.toString()07
END MAIN
The valueOf() method expects a string representing a complete datetime specification, from year to milliseconds, equivalent to a DATETIME YEAR TO FRACTION(3) data type.
The INTERVAL data type is a time type specific to Genero BDL. When calling a Java method with a BDL expression evaluating an INTERVAL, the runtime system converts the BDL value to an instance of the com.fourjs.fgl.lang.FglInterval class implemented in FGLDIR/lib/fgl.jar. You can then manipulate the INTERVAL from within the Java code.
Note that you must add FGLDIR/lib/fgl.jar to the class path in order to compile Java code with com.fourjs.fgl.lang.FglInterval class.
The com.fourjs.fgl.lang.FglInterval class implements following:
Field | Description |
final static int YEAR |
Time qualifier for year |
final static int MONTH |
Time qualifier for month |
final static int DAY |
Time qualifier for day |
final static int HOUR |
Time qualifier for hour |
final static int MINUTE |
Time qualifier for minute |
final static int SECOND |
Time qualifier for second |
final static int FRACTION |
Time qualifier for fraction (start qualifier) |
final static int FRACTION1 |
Time qualifier for fraction(1) (end qualifier) |
final static int FRACTION2 |
Time qualifier for fraction(2) (end qualifier) |
final static int FRACTION3 |
Time qualifier for fraction(3) (end qualifier) |
final static int FRACTION4 |
Time qualifier for fraction(4) (end qualifier) |
final static int FRACTION5 |
Time qualifier for fraction(5) (end qualifier) |
Method | Description |
String toString() |
Converts the INTERVAL value to a String object representing an interval in ISO format. |
static void valueOf(String val) |
Creates a new FglInterval object from a String object
representing an interval value in ISO format: DD hh:mm:ss.fff |
static void valueOf(String val, |
Creates a new FglDateTime object from a String object representing an interval value in ISO format, using the qualifiers and precision passed as parameter. |
static int encodeTypeQualifier( |
Returns the encoded type qualifier for an interval with
to interval qualifiers and length passed: encoded qualifier = (length * 256) + (startUnit * 16) + endUnit Where length defines the total number of significant digits in this time data. For example, with INTERVAL DAY(5) TO FRACTION3: startUnit = DAY length = 13 (DDDDhhmmssfff) endUnit = FRACTION3 |
In the Java code, you can pass a com.fourjs.fgl.lang.FglInterval object as in the following example:
public static void useInterval(FglInterval inv) throws ParseException {
String s = inv.toString();
...
}
If you need to create an com.fourjs.fgl.lang.FglInterval object in BDL, you can use the valueOf() class method as in the following example:
01
IMPORT JAVA com.fourjs.fgl.lang.FglInterval02
MAIN03
DEFINE inv com.fourjs.fgl.lang.FglInterval04
LET inv = FglInterval.valueOf("-510 12:33:45.123")05
DISPLAY inv.toString()06
END MAIN
The DECIMAL data type is a precision math decimal type specific to Genero BDL. MONEY is equivalent to DECIMAL, from an internal storage point of view. When calling a Java method with a BDL expression evaluating a DECIMAL, the runtime system converts the BDL value to an instance of the com.fourjs.fgl.lang.FglDecimal class implemented in FGLDIR/lib/fgl.jar. You can then manipulate the DECIMAL from within the Java code.
Note that you must add FGLDIR/lib/fgl.jar to the class path in order to compile Java code with com.fourjs.fgl.lang.FglDecimal class.
The com.fourjs.fgl.lang.FglDecimal class implements following:
Method | Description |
String toString() |
Converts the DECIMAL value to a String object. |
static void valueOf(String val) |
Creates a new FglDecimal object from a String object representing a decimal value. |
static void valueOf(int val) |
Creates a new FglDecimal object from an int value. |
static int encodeTypeQualifier( |
Returns the encoded type qualifier for this decimal
according to precision and scale. encoded qualifier = (precision * 256) + scale Use 255 as scale for floating point decimal. |
In the Java code, you can convert the com.fourjs.fgl.lang.FglDecimal to a java.lang.BigDecimal as in following example:
public static FglDecimal divide(FglDecimal d1, FglDecimal d2){
BigDecimal bd1 = new BigDecimal(d1.toString());
BigDecimal bd2 = new BigDecimal(d2.toString());
BigDecimal res = bd1.divide(bd2, BigDecimal.ROUND_FLOOR);
return FglDecimal.valueOf(res.toString());
}
If you need to create an com.fourjs.fgl.lang.FglDecimal object in BDL, you can use the valueOf() class method as in the following example:
01
IMPORT JAVA com.fourjs.fgl.lang.FglDecimal02
MAIN03
DEFINE jdec com.fourjs.fgl.lang.FglDecimal04
LET jdec = FglDecimal.valueOf("123.45")05
DISPLAY jdec.toString()06
END MAIN
The BYTE data type is a Binary Large Object (blob) handle data type specific to Genero BDL. When calling a Java method with a BDL expression evaluating a BYTE, the runtime system converts the BDL LOB handle to an instance of the com.fourjs.fgl.lang.FglByteBlob class implemented in FGLDIR/lib/fgl.jar. You can then manipulate the LOB from within the Java code.
Note that you must add FGLDIR/lib/fgl.jar to the class path in order to compile Java code with com.fourjs.fgl.lang.FglByteBlob class.
The com.fourjs.fgl.lang.FglByteBlob class implements following:
Method | Description |
String toString() |
Returns the HEX string representing the binary data. |
static void valueOf(String val) |
Creates a new FglByteBlob object from a String object representing the binary data in HEX format. |
In the Java code, you can pass a com.fourjs.fgl.lang.FglByteBlob object as in the following example:
public static void useByte(FglByteBlob b) throws ParseException {
String s = b.toString();
...
}
If you need to create an com.fourjs.fgl.lang.FglByteBlob object in BDL, you can use the valueOf() class method as in the following example:
01
IMPORT JAVA com.fourjs.fgl.lang.FglByteBlob02
MAIN03
DEFINE jbyte com.fourjs.fgl.lang.FglByteBlob04
LET jbyte = FglByteBlob.valueOf("0FA5617BDE")05
DISPLAY jbyte.toString()06
END MAIN
The TEXT data type is a Text Large Object (tlob) handle data type specific to Genero BDL. When calling a Java method with a BDL expression evaluating a TEXT, the runtime system converts the BDL LOB handle to an instance of the com.fourjs.fgl.lang.FglTextBlob class implemented in FGLDIR/lib/fgl.jar. You can then manipulate the LOB from within the Java code.
Note that you must add FGLDIR/lib/fgl.jar to the class path in order to compile Java code with com.fourjs.fgl.lang.FglTextBlob class.
The com.fourjs.fgl.lang.FglTextBlob class implements following:
Method | Description |
String toString() |
Converts the large text data to a simple String. |
static void valueOf(String val) |
Creates a new FglTextBlob object from a String. |
In the Java code, you can pass a com.fourjs.fgl.lang.FglTextBlob object as in the following example:
public static void useByte(FglTextBlob t) throws ParseException {
String s = t.toString();
...
}
If you need to create an com.fourjs.fgl.lang.FglTextBlob object in BDL, you can use the valueOf() class method as in the following example:
01
IMPORT JAVA com.fourjs.fgl.lang.FglTextBlob02
MAIN03
DEFINE jtext com.fourjs.fgl.lang.FglTextBlob04
LET jtext = FglTextBlob.valueOf("abcdef..........")05
DISPLAY jtext.toString()06
END MAIN
Native Java types and BDL data types are different. To identify BDL types in java code, you can use the com.fourjs.fgl.lang.FglTypes class implemented in FGLDIR/lib/fgl.jar. You can for example identify the data type of a member of an FglRecord object.
Note that you must add FGLDIR/lib/fgl.jar to the class path in order to compile Java code with com.fourjs.fgl.lang.FglType class.
The com.fourjs.fgl.lang.FglTypes class implements following:
Field | Description |
final static int BYTE |
Identifies the BDL BYTE data type |
final static int CHAR |
Identifies the BDL CHAR data type |
final static int DATE |
Identifies the BDL DATE data type |
final static int DATETIME |
Identifies the BDL DATETIME data type |
final static int DECIMAL |
Identifies the BDL DECIMAL data type |
final static int FLOAT |
Identifies the BDL FLOAT data type |
final static int INT |
Identifies the BDL INT data type |
final static int SMALLFLOAT |
Identifies the BDL SMALLFLOAT data type |
final static int SMALLINT |
Identifies the BDL SMALLINT data type |
final static int VARCHAR |
Identifies the BDL VARCHAR data type |
final static int STRING |
Identifies the BDL STRING data type |
final static int RECORD |
Identifies a BDL RECORD structure |
final static int ARRAY |
Identifies a BDL ARRAY object |
BDL supports structured types with the RECORD definitions. When passing a RECORD to a Java method, the runtime system converts the RECORD to an instance of the com.fourjs.fgl.lang.FglRecord class implemented in FGLDIR/lib/fgl.jar. You can then manipulate the RECORD from within the Java code.
Note that you must add FGLDIR/lib/fgl.jar to the class path in order to compile Java code with com.fourjs.fgl.lang.FglRecord class.
When assigning a RECORD to a com.fourjs.fgl.lang.FglRecord, widening conversion applies implicitly. But when assigning a com.fourjs.fgl.lang.FglRecord to a RECORD, narrowing conversion applies and you must explicitly CAST the original object reference to the type of the RECORD.
The com.fourjs.fgl.lang.FglRecord class implements the following:
Method | Description |
int getFieldCount() |
Returns the number of record members. |
String getFieldName(int p) |
Returns the name of the record member at position p. |
FglTypes getType(int p) |
Returns the FglTypes of the record member at position p. |
String getTypeName(int p) |
Returns the string representation of the BDL type of the record member at position p. |
int getTypeQualifier(int p) |
Returns the encoded type qualifier of the record member at position p. |
int getInt(int p) |
Returns the int value of the record member at position p. |
int getFloat(int p) |
Returns the float value of the record member at position p. |
double getDouble(int p) |
Returns the double value of the record member at position p. |
String getString(int p) |
Returns the String representation of the value of the record member at position p. |
FglDecimal getDecimal(int p) |
Returns the FglDecimal value of the record member at position p. |
FglDate getDate(int p) |
Returns the FglDate value of the record member at position p. |
FglDateTime getDateTime(int p) |
Returns the FglDataTime value of the record member at position p. |
FglInterval getInterval(int p) |
Returns the FglInterval value of the record member at position p. |
FglByteBlob getByteBlob(int p) |
Returns the FglByteBlob value of the record member at position p. |
FglTextBlob getTextBlob(int p) |
Returns the FglTextBlob value of the record member at position p. |
In the Java code, use the query methods of the com.fourjs.fgl.lang.FglRecord to identify the members of the RECORD:
public static void showMemberTypes(FglRecord rec){
int i;
int n = rec.getFieldCount();
for (i = 1; i <= n; i++)
System.out.println( String.valueOf(i)
+ ":" + rec.getFieldName(i) + " / " + rec.getTypeName(i) );
}
To format Genero BDL data types, you can use the com.fourjs.fgl.lang.FglFormat class implemented in FGLDIR/lib/fgl.jar.
Note that you must add FGLDIR/lib/fgl.jar to the class path in order to compile Java code with com.fourjs.fgl.lang.FglFormat class.
The com.fourjs.fgl.lang.FglFormat class provides an interface to the data formatting functions of the Genero BDL runtime system. This class is actually an equivalent of the USING operator in the BDL language.
The com.fourjs.fgl.lang.FglFormat class implements the following:
Method | Description |
static String format(int v, String fmt) |
Formats the INT value provided as Java int, according to fmt. Here fmt must
specify a numeric format with [$@*#&<()+-]
characters. See USING for
more details. |
static String format(double v, String fmt) |
Formats the FLOAT value provided as Java double, according to fmt. Here fmt must
specify a numeric format with [ $ @*#&<()+-. ,]
characters. See USING for
more details. |
static String format(FglDate v, String fmt) |
Formats the DATE value provided as FglDate, according to fmt. Here fmt must
specify a date format with [mdy] characters. See USING
for more details. |
static String format(FglDecimal v, String fmt) |
Formats the DECIMAL value provided as FglDecimal,
according to fmt. Here fmt must
specify a numeric format with [ $ @*# $<()+-. ,]
characters. See USING for
more details. |
Example of Java code using the com.fourjs.fgl.lang.FglFormat class:
public static void formatDecimal(FglDecimal dec){
System.out.println( FglFormat.format(dec,
"$#####&.&&" );
}
A Genero BDL application uses a locale and character set defined by the LANG/LC_ALL environment variables, while Java uses its own charset for the char type (16- bit UNICODE). When passing character strings to/from Java methods or when assigning BDL strings to java.lang.String, the runtime system takes charge of character set conversion.
Java Arrays and traditional BDL static or dynamic arrays are different. In order to interface with Java Arrays, the BDL language has been extended with a new kind of arrays, called "Java Arrays". Java Arrays have to be created with a given length. Like native Java arrays, the length cannot be changed after the array is created. The Java Arrays are passed to Java methods by reference, so the elements of the array can be manipulated in Java. On the other hand, Java Arrays can be created in Java code and returned to BDL. To create a Java Array in BDL you must define a TYPE in order to call the create() type method.
The example below shows how to create a Java Array in BDL, to instantiate a Java Array of INTEGER elements:
01
MAIN02
TYPE int_array_type ARRAY [] OF INTEGER03
DEFINE ja int_array_type04
LET ja = int_array_type.create(100)05
LET ja[10] = 12306
DISPLAY ja[10], ja[20]07
DISPLAY ja.getLength()08
END MAIN
If you want to create a Java Array of structured BDL RECORD elements, you must use the com.fourjs.fgl.lang.FglRecord class:
01
IMPORT JAVA com.fourjs.fgl.lang.FglRecord02
MAIN03
TYPE record_array_type ARRAY [] OF com.fourjs.fgl.lang.FglRecord04
DEFINE ra record_array_type05
TYPE r_t RECORD06
id INTEGER,07
name VARCHAR(100)08
END RECORD09
DEFINE r r_t10
LET ra = record_array_type.create(100)11
LET r.id = 12312
LET r.name = "McFly"13
LET ra[10] = r14
INITIALIZE r TO NULL15
LET r = CAST (ra[10] AS r_t)16
DISPLAY r.*17
END MAIN
You can also create Java Arrays of Java classes. The next example introspects the java.lang.String class by using Java Array of java.lang.reflect.Method to query the list of methods from the java.lang.Class object:
01
IMPORT JAVA java.lang.Class02
IMPORT JAVA java.lang.reflect.Method03
MAIN04
DEFINE c java.lang.Class05
DEFINE ma ARRAY [] OF java.lang.reflect.Method06
DEFINE i INTEGER07
LET c = Class.forName("java.lang.String")08
LET ma = c.getMethods()09
FOR i = 1 TO ma.getLength()10
DISPLAY ma[i].toString()11
END FOR12
END MAIN
You can also create Java arrays in Java, to be returned from a Java method and assigned to a Java Array variable in BDL:
public static int [] createIntegerArray(int size) {
return new int[size];
}
Important consideration has to be taken when assigning object references to different target types or classes. A Widening Reference Conversion occurs when an object reference is converted to a superclass that can accommodate any possible reference of the original type or class. A Narrowing Reference Conversion occurs when an object reference of a superclass is converted to a subtype or subclass of the original object reference. For example, in a vehicle class hierarchy with Vehicle and Car classes, Car is a subclass that inherits from the Vehicle superclass. When assigning a Car object reference to a Vehicle variable, Widening Reference Conversion takes place. When assigning a Vehicle object reference to a Car variable, Narrowing Reference Conversion occurs.
In Genero BDL, as in Java, widening conversion does not require casts and will not produce compilation or runtime errors, but narrowing conversion needs the CAST operator to convert to the target type or class:
CAST( object_reference AS type_or_class )
The next example creates a java.lang.StringBuffer object, and assigns the reference to a java.lang.Object variable (implying Widening Reference Conversion); then the Object reference is assigned back to the StringBuffer variable (implying Narrowing Reference Conversion and CAST operator usage):
01
IMPORT JAVA java.lang.Object02
IMPORT JAVA java.lang.StringBuffer03
MAIN04
DEFINE o java.lang.Object05
DEFINE sb java.lang.StringBuffer06
LET sb = StringBuffer.create()07
LET o = sb -- Widening Reference Conversion08
LET sb = CAST( o AS StringBuffer ) -- Narrowing Reference Conversion needs CAST()09
END MAIN
When manipulating an object reference with a variable defined with a superclass of the real class used to instantiate the object, you sometimes need to identify the real class of the object. This is possible with the INSTANCEOF operator. This operator checks whether the left operand is an instance of the type or class specified by the right operand:
object_reference INSTANCEOF type_or_class
The example below creates a java.lang.StringBuffer object, assigns the reference to a java.lang.Object variable, and tests whether the class type of the object reference is a StringBuffer:
01
IMPORT JAVA java.lang.Object02
IMPORT JAVA java.lang.StringBuffer03
MAIN04
DEFINE o java.lang.Object05
LET o = StringBuffer.create()06
DISPLAY o INSTANCEOF StringBuffer -- Shows 1 (TRUE)07
END MAIN
BDL code can catch Java exceptions with the Genero BDL TRY/CATCH block.
01
IMPORT JAVA java.lang.StringBuffer02
MAIN03
DEFINE sb java.lang.StringBuffer04
LET sb = StringBuffer.create("abcdef")05
TRY06
CALL sb.deleteCharAt(50) -- Throws StringIndexOutOfBoundsException07
CATCH08
DISPLAY "An exception was raised..."09
END TRY10
END MAIN
You will find more examples in the FGLDIR/demo/JavaInterface directory.
01
IMPORT JAVA java.util.regex.Pattern02
IMPORT JAVA java.util.regex.Matcher03
MAIN04
DEFINE p Pattern05
DEFINE m Matcher06
LET p = Pattern.compile("[a-z]+,[a-z]+")07
DISPLAY p.pattern()08
LET m = p.matcher("aaa,bbb")09
IF m.matches() THEN10
DISPLAY "The string matches the pattern..."11
ELSE12
DISPLAY "The string does not match the pattern..."13
END IF14
END MAIN
This little example shows how to create an XLS file, using the Apache POI framework. Note that you must download and install the Apache POI JAR file and make the CLASSPATH environment variable point to the POI JAR in order to compile and run this example. After execution, you should find a file named "itemlist.xls" in the current directory, which can be loaded with Microsoft Excel or Open Office Calc:
01
IMPORT JAVA java.io.FileOutputStream02
IMPORT JAVA org.apache.poi.hssf.usermodel.HSSFWorkbook03
IMPORT JAVA org.apache.poi.hssf.usermodel.HSSFSheet04
IMPORT JAVA org.apache.poi.hssf.usermodel.HSSFRow05
IMPORT JAVA org.apache.poi.hssf.usermodel.HSSFCell06
IMPORT JAVA org.apache.poi.hssf.usermodel.HSSFCellStyle07
IMPORT JAVA org.apache.poi.hssf.usermodel.HSSFFont08
IMPORT JAVA org.apache.poi.ss.usermodel.IndexedColors09
10
MAIN11
DEFINE fo FileOutputStream12
DEFINE workbook HSSFWorkbook13
DEFINE sheet HSSFSheet14
DEFINE row HSSFRow15
DEFINE cell HSSFCell16
DEFINE style HSSFCellStyle17
DEFINE headerFont HSSFFont18
DEFINE i, id INTEGER, s STRING19
20
LET workbook = HSSFWorkbook.create()21
22
LET style = workbook.createCellStyle()23
CALL style.setAlignment(HSSFCellStyle.ALIGN_CENTER)24
CALL style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());25
CALL style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);26
LET headerFont = workbook.createFont()27
CALL headerFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD)28
CALL style.setFont(headerFont);29
30
LET sheet = workbook.createSheet()31
32
LET row = sheet.createRow(0)33
LET cell = row.createCell(0)34
CALL cell.setCellValue("Item Id")35
CALL cell.setCellStyle(style)36
LET cell = row.createCell(1)37
CALL cell.setCellValue("Name")38
CALL cell.setCellStyle(style)39
40
FOR i=1 TO 1041
LET row = sheet.createRow(i)42
LET cell = row.createCell(0)43
CALL cell.setCellType(HSSFCell.CELL_TYPE_NUMERIC)44
LET id = 100 + i45
CALL cell.setCellValue(id)46
LET cell = row.createCell(1)47
LET s = SFMT("Item #%1",i)48
CALL cell.setCellValue(s)49
END FOR50
51
LET fo = FileOutputStream.create("itemlist.xls");52
CALL workbook.write(fo);53
CALL fo.close();54
55
END MAIN