Powered by SmartDoc

Prolog Cafe Tutorial: Interoperating with Java

June 2008
Mutsunori BANBARA and Naoyuki TAMURA
Kobe University, JAPAN

Table of Contents

Calling Java from Prolog

Prolog Cafe provides the following built-in predicates that enable us to easily call Java programs from Prolog programs.

java_constructor(+Class(Arg1,...,Argn), ?Term)
Creates a new instance by calling the public constructor represented by Class(Arg1,...,Argn), and unifies the instance with Term. Class is a Java class name with full package path. Arg is an argument to be passed to the constructor call. Each of arguments is automatically converted to a corresponding Java object (See Table 1[Data conversion between Prolog and Java]). Place just an atom as Class instead if the constructor has no argument (n = 0).
java_method(+Class_or_Instance, +Method(Arg1,...,Argn), ?Term)
Invokes the public member method (1) represented by Method(Arg1,...,Argn) of the class, interface, or instance represented by Class_or_Instance, and unifies a return object with Term. Method is a Java method name. Arg is an argument to be passed to the method call. Each of arguments is automatically converted to a corresponding Java object (See Table 1[Data conversion between Prolog and Java]). Place just an atom as Method instead if the method has no argument (n = 0). Method is a instance method if Class_or_Instance is a Java object. Method is a static method if Class_or_Instance is an atom representing a Java class name with full package path. The return object is automatically converted to a corresponding term by the inverse conversion of Table 1[Data conversion between Prolog and Java]. If the return type of Method is void, or the return object is null, Term remains unaltered.
java_get_field(+Class_or_Instance, +Field, ?Term)
Gets the value of the public member field (2) represented by Field of the class, interface, or instance represented by Class_or_Instance, and unifies it with Term. Field is an atom that represents a Java field name. Field is a instance field if Class_or_Instance is a Java object. Field is a static field if Class_or_Instance is an atom representing a Java class name with full package path. The field object is automatically converted to a corresponding term by the inverse conversion of Table 1[Data conversion between Prolog and Java].
java_set_field(+Class_or_Instance, +Field, +Value)
Sets the value to the public member field (3) represented by Field of the class, interface, or instance represented by Class_or_Instance. Field is an atom that represents a Java field name. Value, a value to be set to the field, is automatically converted to a corresponding Java object (See Table 1[Data conversion between Prolog and Java]). Field is a instance field if Class_or_Instance is a Java object. Field is a static field if Class_or_Instance is an atom representing a Java class name with full package path.
Data conversion between Prolog and Java
Prolog Java
integer java.lang.Integer
float java.lang.Double
atom java.lang.String
list java.util.Vector
java_object Java Object

These predicates are available from both the interpreter and the translator. For example, the following goal creates a new java frame instance, set its size to 200 × 200, and make it visible.

| ?- java_constructor('java.awt.Frame', X),
     java_method(X, setSize(200,200), _),
     java_get_field('java.lang.Boolean', 'TRUE', True),
     java_method(X, setVisible(True), _).

X = java.awt.Frame(1402733),
True = java.lang.Boolean(1231) ? 
yes

List 1[queens2.pl]is a Prolog program of N-Queens puzzle. The goal of N-Queens is to place N queens on an N by N board such that no piece attacks another. Let us show how to display the result of N-Queens graphically through cooperation with Java.

The goal show/0 first creates a QueensFrame object and makes it visible. And then the answer of the call queens(8,Xs) is displayed on the frame (see Figure 1[Result of queens2.pl]) by invoking the setQueens(Qs) method. The answer, a list of integers, will be automatically converted into a java.util.Vector object at that time. Finally, fail force to do backtrack for finding an another answer after waiting a minute by invoking the sleep method of java.lang.Thread.

queens2.pl
main :-
	queens(8, Qs), write(Qs), nl, fail.

show :-
	java_constructor('QueensFrame', Frame),
	queens(8, Qs),
	java_method(Frame, setQueens(Qs), _),
	java_method('java.lang.Thread', sleep(1000), _),
	fail.

queens(N,Qs) :-
	range(1,N,Ns),
	queens(Ns,[],Qs).

queens([],Qs,Qs).
queens(UnplacedQs,SafeQs,Qs) :-
	select(UnplacedQs,UnplacedQs1,Q),
	not_attack(SafeQs,Q),
	queens(UnplacedQs1,[Q|SafeQs],Qs).

not_attack(Xs,X) :-
	not_attack(Xs,X,1).

not_attack([],_,_) :- !.
not_attack([Y|Ys],X,N) :-
	X =\= Y+N, X =\= Y-N,
	N1 is N+1,
	not_attack(Ys,X,N1).

select([X|Xs],Xs,X).
select([Y|Ys],[Y|Zs],X) :- select(Ys,Zs,X).

range(N,N,[N]) :- !.
range(M,N,[M|Ns]) :-
	M < N,
	M1 is M+1,
	range(M1,N,Ns).
QueensFrame.java
import java.awt.*;
import java.util.Vector;

public class QueensFrame extends Frame {
    QueensPanel queensPanel;

    public QueensFrame() {
	setSize(500, 500);
	queensPanel = new QueensPanel();
	add(queensPanel);
	setVisible(true);
    }

    public void setQueens(Vector queens) {
	queensPanel.setQueens(queens);
    }
}

class QueensPanel extends Panel {
    Vector qs = null;

    public void setQueens(Vector queens) {
	qs = queens;
	repaint();
    }

    public void paint(Graphics g) {
	if (qs == null)
	  return;
	int c = getSize().height / qs.size();
	Color[] bg = { Color.white, Color.gray };
	for (int i = 0; i < qs.size(); i++) {
	    for (int j = 0; j < qs.size(); j++) {
		g.setColor(bg[(i + j) % 2]);
		g.fillRect(i * c, j * c, c, c);
	    }
	    g.setColor(Color.orange);
	    int j = ((Integer)qs.get(i)).intValue()-1;
	    int d = c / 10;
	    g.fillOval(i*c+d, j*c+d, c-2*d, c-2*d);
	}
    }
}
Result of queens2.pl
  1. The public member methods of the class or interface includes those declared by the class or interface and those inherited from superclasses and superinterfaces.
  2. The public member fields of the class or interface includes those declared by the class or interface and those inherited from superclasses and superinterfaces.
  3. (2)

Calling Prolog from Java

At first, we give a brief introduction to the Prolog Cafe implementation of Prolog data, called term. Please refer to Prolog Cafe API Specification in detail.

Each term is translated into a java object of classes in Figure 2[Term representation]: VariableTerm, IntegerTerm, DoubleTerm, SymbolTerm, ListTerm, and StructureTerm. The Term class, which has an abstract method unify, is a common superclass of these classes. The JavaObjectTerm class is used to represent any java instance as a term.

Term representation

Basic procedure for calling a Prolog predicate p from Java is as follows:

  1. Create a PrologControl object e as Prolog engine.
  2. Create a predicate object p to be executed. (4)
  3. Create an array of terms a as the arguments to be passed to the predicate call.
  4. Set the predicate p and its arguments a to the engine e by invoking the setPredicate method.
  5. Start finding the first answer by invoking the method call of e. Return true if the goal succeeds, otherwise false.
  6. Restart finding the another answer by invoking the method redo of e. Return true if the goal succeeds, otherwise false.

This procedure using call and redo is based on the well-known Prolog Box Control Flow Model.

For example, List 3[Queens.java] shows code for executing a goal queens(8,Qs) from Java. Each of the answers will be repeatedly displayed after being converted to a java string.

Other examples are available in the $PLCAFEDIR/examples/java directory.

Queens.java
import jp.ac.kobe_u.cs.prolog.lang.*;

public class Queens {
    static void queens(int n) {
	PrologControl prolog = new PrologControl();
	Predicate queens = new PRED_queens_2();
	Term arg1 = new IntegerTerm(n);
	Term arg2 = new VariableTerm();
	Term[] args = { arg1, arg2 };
	prolog.setPredicate(queens, args);
	for (boolean r = prolog.call(); r; r = prolog.redo()) {
	    System.out.println(arg2.toString());
	}
    }

    public static void main(String[] args) {
	queens(8);
    }
}
  1. A predicate f/n is translated into a java file PRED_f_n.java. For example, the predicate queens/2 in the queens2.pl is translated into a file PRED_queens_2.java.

Making a User-Defined Built-in Predicate

As mentioned above, a predicate f/n is translated into a PRED_f_n.java. Conversely, it is possible to make an user-defined built-in predicate by creating a java program named PRED_f_n.java.

Let us, for example, show how to make a built-in predicate msgbox/1 that launches a new pop-up window for confirmation.

PRED_msgbox_1.java
import jp.ac.kobe_u.cs.prolog.lang.*;
import jp.ac.kobe_u.cs.prolog.builtin.*;
import javax.swing.*; // Added

public class PRED_msgbox_1 extends Predicate {

    public Term arg1;

    public PRED_msgbox_1(Term a1, Predicate cont) {
        arg1 = a1;
        this.cont = cont;
    }

    public PRED_msgbox_1(){}

    public void setArgument(Term[] args, Predicate cont) {
        arg1 = args[0];
        this.cont = cont;
    }

    public int arity() { return 1; }

    public String toString() {
        return "msgbox(" + arg1 + ")";
    }

    public Predicate exec(Prolog engine) {
        engine.setB0();
        Term a1;
        a1 = arg1;
	a1 = a1.dereference();                     // Added
	String msg = a1.toString();                // Added
	JFrame frame = new JFrame();               // Added
	frame.setSize(300, 100);                   // Added
	JOptionPane.showMessageDialog(frame, msg); // Added
        return cont;
    }
}
The output of msgbox(hello)