JPL 3.x Prolog API overview



Introduction

This is an overview of an interface which allows SWI-Prolog programs to dynamically create and manipulate Java objects.

Here are some significant features of the interface and its implementation:


JPL types (Java types, as seen by Prolog)

All Java values and object references which are passed between Prolog engines and Java VMs via JPL's Prolog API are seen as instances of types within this simplified JPL type system:

a datum   (this term is introduced, out of necessity, to refer jointly to values and refs)
is a value    (values are copied between Prolog and the JVM)
is a boolean
or a char
or a long, int, short or byte
or a double or float
or a string   (an instance of java.lang.String)
or a void     (an artificial value returned by calls to Java void methods)
or a ref
is null
or an object    (held within the JVM, and represented in Prolog by a canonical reference)
is an array
or a class instance (other than of java.lang.String)

representation of Java values and references within Prolog

Instances of JPL types are represented within Prolog as follows:
boolean has two values, represented by @(true) and @(false)

char values are represented by corresponding Prolog integers

int, short and byte values are represented by corresponding Prolog integers

long values are represented as Prolog integers if possible (32-bit in current SWI-Prolog), else as jlong(Hi,Lo) where Hi is an integer corresponding to the top32 bits of the long, and Lo similarly represents the lower 32 bits

double and float values are represented as Prolog floats (which are equivalent to Java doubles) (there may be minor rounding, normalisation or loss-of-precision issues when a Java float is widened to a Prolog float then narrowed back again, but what the heck)

string values (immutable instances of java.lang.String) are represented as Prolog atoms (in UTF-8 encoding)

null has only one value, represented as @(null)

void has only one value, represented as @(void)

array and class instance references are currently represented as @(Tag), where Tag ia an atom whose name encodes a JNI global reference value; this may change, but won't affect Prolog programs which respect the opacity of references


representation of Java types within Prolog (1): structured notation

The JPL Prolog API allows Prolog applications to inspect, manipulate, and reason about the types of Java values, references, methods etc., and this section describes how these types themselves (as opposed to instances thereof) are represented.  Predicates which pass these type representations include jpl_class_to_type/2, jpl_classname_to_type/2, jpl_datum_to_type/2, jpl_is_object_type/1, jpl_is_type/1, jpl_object_to_type/2, jpl_primitive_type/1, jpl_ref_to_type/2, jpl_type_to_class/2. jpl_type_to_classname/2.
void is represented as void
null is represented as null
the primitive types are represented as boolean, char, byte, short, int, long, float, double
classes are represented as class(package_parts,classname_parts)
e.g.  class([java,util],['Date'])
array types are represented as array(type)
e.g.  array(boolean)
e.g.  array(class([java,lang],['String'])
This structured notation for Java types is designed to be convenient for composition and decomposition by matching (unification).

representation of Java types within Prolog (2): descriptor notation

The descriptor notation for Java types is one of two textual notations employed by the JVM and the Java class libraries; JPL (necessarily) supports both (and supports conversion between all three representations).

Examples:

'Z' denotes boolean

'B' denotes byte

'C' denotes char

'S' denotes short

'I' denotes int

'J' denotes long

'F' denotes float

'D' denotes double

'Ljava/util/Date;' (for example) denotes the Java class java.util.Date

'[type' denotes an array of type

'(argument_types)return_type' denotes the type of a method


representation of Java types within Prolog (3): classname notation

The classname notation for Java types is the other textual notation employed by the JVM and the Java class libraries.  It is a (seemingly unnecessary) variation on the descriptor notation, used by a few JNI routines.  It has the slight advantage that, in the case of simple class types only, it resembles the Java source text notation for classes.  This representation is supported only because certain JNI functions use it; it is used within JPL's implementation of jpl_call/4 etc.  You may encounter this notation when tracing JPL activity, but otherwise you need not know about it.

Examples:

'java.util.Vector' denotes the Java class java.util.Vector
'[B' denotes an array of boolean
'[Ljava.lang.String;' denotes an array of string

Using the JPL 3.x Prolog API

creating instances of Java classes

To create an instance of a Java class from within Prolog, call jpl_new(+Class,+Params,-Ref) with a classname, a list of actual parameters for the constructor, and a variable to be bound to the new reference, e.g.
jpl_new( 'javax.swing.JFrame', ['frame with dialog'], F)
which binds F to a new object reference, e.g.
 @('J#0008272420')
(not that the details of this structure are of any necessary concern to the Prolog programmer or to the applications she writes).
NB for convenience, this predicate is overloaded: Class can also be a class type in structured notation, e.g. array(boolean).


calling methods of Java objects or classes

The object reference generated by the jpl_new/3 call (above) can be passed to other JPL API predicates such as
jpl_call( +Ref, +Method, +Params, -Result)
 e.g.
jpl_call( F, setVisible, [@(true)], _)
which calls the setVisible method of the object to which F refers, effectively passing it the Java value true.

(This call should display the new JFrame in the top left corner of the desktop.)

Note the anonymous variable passed as the fourth argument to jsp_call/4.  A variable in this position receives the result of the method call: either a value or a reference.  Since SetVisible() is a void method, the call returns the (artificial) reference @(void).

Some may prefer to code this call thus:

jpl_call( F, setVisible, [@true], @void)
which documents the programmer's understanding that this is a void method (and fails if it isn't :-).
 
If the +Ref argument represents a class, then the named static method of that class  is called.


fetching field values of Java objects or classes

The jpl_get/3 API predicate can retrieve the value of an instance field or a static field, e.g.
jpl_get( 'java.awt.Color', pink, Pink)
which binds the Prolog variable Pink to a reference to the predefined java.awt.Color "constant" which is held in the static final .pink field of the java.awt.Color class.

More generally, jpl_get/3 has the following interface:

jpl_get( +Class_or_Object, +Field, -Datum)
If the first argument represents a class, then a static field of that class with FieldName is accessed.


setting field values of Java objects or classes

Object and class fields can be set (i.e. have values or references assigned to them) by the jpl_set/3 API procedure, which has the following interface:
jpl_set( +Class_or_Object, +Field, +Datum)
where Datum must be a value or reference of a type suitable for assignment to the named field of the class or object.


a slightly longer example

This code fragment
    findall(
        Ar,
        (   current_prolog_flag( N, V),
            term_to_atom( V, Va),
            jpl_new( '[Ljava.lang.String;', [N,Va], Ar)
        ),
        Ars
    ),
    jpl_new( '[[Ljava.lang.String;', Ars, Ac),
    jpl_datums_to_array( [name,value], Ah),
    jpl_new( 'javax.swing.JFrame', ['current_prolog_flag'], F),
    jpl_call( F, getContentPane, [], CP),
    jpl_new( 'javax.swing.JTable', [Ac,Ah], T),
    jpl_new( 'javax.swing.JScrollPane', [T], SP),
    jpl_call( CP, add, [SP,'Center'], _),
    jpl_call( F, setSize, [600,400], _),
builds an array of arrays of strings containing the names and values of the current SWI-Prolog "flags", and displays it in a JTable within a ScrollPane within a JFrame:
In addition to JPL API calls, this code calls jpl_datums_to_array/2, a utility which converts any list of valid representations of Java values (or objects) into a new Java array, whose base type is the most specialised type of which all list members are instances, and which is defined thus:
jpl_datums_to_array( Ds, A) :-
    ground( Ds),
    jpl_datums_to_most_specific_common_ancestor_type( Ds, T),
    jpl_new( array(T), Ds, A).
Having found the "most specific common ancestor type" (my phrase :-), a new array of this type is created, whose elements are initialised to the successive members of the list of datums.

This illustrates another mode of operation of jpl_new/3:

jpl_new( +ArrayType, +InitialValues, -ArrayRef)
See the relevant Appendix for fuller details of the API procedures.

Don't forget the possibility of writing and manipulating new Java classes to serve your Prolog applications: this interface is not designed to make Java programming redundant :-)



jpl_new( +X, +Argz, -V) :-
X can be:
if X denotes a primitive type and Argz is castable to a value of that type, then V is that value (a pointless mode of operation, but somehow complete...)
if X denotes an array type and Argz is a non-negative integer, then V is a new array of that many elements, initialised to the appropriate default value
if X denotes an array type and Argz is a list of datums, each of which is (independently) castable to the array element type, then V is a new array of as many elements as Argz has members, initialised to the results of casting the respective members of Argz
if X denotes a non-array object type and Argz is a list of datums, then V is the result of an invocation of that type's most specifically-typed constructor to whose respective parameters the members of Argz are assignable


jpl_call( +X, +Method, +Args, -R) :-
X can be:
  • a type, class object or classname (for static methods of the denoted class, or for static or instance methods of java.lang.Class)
  • a class instance or array (for static or instance methods)
  • Method can be:
  • an atomic method name (if this name is ambiguous, as a result of method overloading, then it will be resolved by considering the types of Args, as far as they can be inferred)
  • an integral method index (untested: for static overload resolution)
  • a methodID/1 structure (ditto)
  • Args must be Finally, an attempt will be made to unify R with the returned result.


    jpl_set( +X, +Field, +V) :-
    basically, sets the Fspec-th field of object X to value V

    X can be:

    Field can be:
    V must be ground (although one day we may pass variables to JPL?!)


    jpl_get( +X, +Field, -V) :-
    X can be:
    Field can be
    Immediately before jpl_get/4 returns, an attempt will be made to unify V with the internally computed result.

    exceptions thrown by Java

    Uncaught exceptions thrown by the JVM in the course of handling a JPL 3.x Prolog API call are mapped onto Standard Prolog exceptions, e.g.
    jpl_new( 'java.util.Date', [yesterday], D)
    raises the Prolog exception
    java_exception('java.lang.IllegalArgumentException', @'J#0008408972')
    because, as the exception suggests, yesterday is not a valid constructor argument.
     
    Java exceptions are always returned as Prolog exceptions with this structure:
    java_exception( classname, reference_to_exception_object)

    testing

    For a rudimentary test, run
    ?- jpl_demo.
    and wait patiently for some Swing windows to appear (but not too patiently, in case something is wrong...)


    to do

    Apart from any bugs I don't know about, this interface is usable and useful as it stands.  Nevertheless there are some things "to do" at some stage in the future, e.g.
    Paul Singleton
    drafted 10th November 2000
    revised 14th December 2000
    revised 11th March 2003
    revised 18th February 2004