-
-
- Using the Prolog IDE
-
-
-
Software Engineering for Smart Data Analytics & Smart Data Analytics for Software Engineering
The Prolog Connector establishes the connection of a Java process and a Prolog process and can be used independently from the IDE part (see PDT Architecture).
The Prolog Connector is based on sockets and implements a pure client-server relation: The Java processes send requests, the Prolog processes answer. Communication from Prolog to Java is possible via a notification / observer mechanism: The Java side can declare to be listening for certain events and can explicitly call the Prolog side when notified that the respective events occurred.
If you just want to run Prolog code from Java, without using the developement environment of the PDT, you can use the provided library. All you need is SWI-Prolog (no need for Eclipse or any other libraries).
Each instance of a PrologProcess corresponds to one Prolog process. There are two ways to retrieve an instance depending on how you use the connector (library or Eclipse plugin).
If you use the connector library you can get an instance via
org.cs3.prolog.connector.Connector.newPrologProcess()
which uses SWI Prolog from the system PATH or you can use
org.cs3.prolog.connector.Connector.newPrologProcess(executable)
which uses the specified SWI Prolog executable.
If you want to use the connector within an Eclipse plugin that depends on the PDT we recommend to depend on the org.cs3.pdt.connector
plugin and to use
org.cs3.pdt.connector.PDTConnectorPlugin.getDefault().getPrologProcess(key)
to get an instance.
Queries are passed as simple Java Strings to the responsible methods.
One result of a query is a Java Map containing the bindings for all variables of the query. The variables are the keys of the map.
The type of the variable values differ, depending on the given flag. If not specified otherwise the DEFAULT flag will be used. This means, that the values will be instances of String
or instances of java.util.List
(in case of Prolog lists). The elements in this list can also be of type String
or List
. This is the recommended way if the results only consist of atoms, numbers and lists or if the string representation of a result is sufficient for the context of the program.
There are two methods that execute a query:
org.cs3.prolog.connector.process.PrologProcess.queryOnce(query)
returns the first result of the query or null
if the query fails.
org.cs3.prolog.connector.process.PrologProcess.queryAll(query)
returns all results of the query. If the query fails the returned list will be empty.
Each result is represented as a Map<String, Object>. The String is the name of a variable in the query. The Object is the value of the variable generated by the query. The following listing shows an example.
PrologProcess process = Connector.newPrologProcess(); String query = "father_of(Child, 'Peter')"; Map<String, Object> result = process.queryOnce(query); List<Map<String, Object>> results = process.queryAll(query);
To access the results (that is, the values of the variables from the query) you must know which kind of value to expect:
In the above example we expect the name of Peter's father to be an atom (→ String), so we access it via
String firstChildOfPeter = result.get("Child").toString();
The class QueryUtils contains several utility methods which are useful for constructing queries.
The most important one is
org.cs3.prolog.connector.common.QueryUtils.buildTerm(functor, args...)
which constructs a Prolog term for a functor and an arbitrary number of arguments. Using this method makes the code more readable and avoids String concatenations, especially when using Java variables in the term construction. There is also the method QueryUtils.bT(functor, args…)
that forwards to buildTerm
but has a shorter name.
An example is:
String child = "peter"; String query = QueryUtils.buildTerm("father_of", "Father", child); // this is the same as "father_of(Father, peter)"
Sessions are used to execute queries. For each session there is one corresponding thread in the Prolog process and one socket connection between Java and Prolog. There are two kinds of sessions:
Sessions can be obtained by the getSession…
methods of a PrologProcess
and need to be disposed when they are no longer needed. The query…
methods of a PrologProcess
create a session automatically and dispose it for each query.
If you want to execute lots of queries in a short time it is recommended to create one session and execute the queries on this session instead of using the query…
methods of a PrologProcess
. The following listing displays an example.
PrologSession session = null; try { // create a session for the current process session = process.getSession(); // execute a lot of queries in a short period for (int i = 0; i < 10000; i++) { // use the queryOnce method from the session // this prevents the creation of a new session for every query session.queryOnce(bT("assertz", "p_" + i)); } } catch (PrologProcessException e) { e.printStackTrace(); } finally { // make sure to close the session after the queries are finished if (session != null) { session.dispose(); } }
In addition to the message based interaction of Java and Prolog supported
by the queryOnce
and queryAll
methods, the PDT connector also supports
an event / observer / notification mechanism.
The Java side does not need to poll the Prolog processes to find out
when some event occurred but can declare that it is listening for certain
events. The Prolog side triggers these events if something relevant happens
(e.g. the end of a long running computation). Then the Java side can send
a normal queryOnce
/ queryAll
to retrieve the results.
First you need to create a PrologEventDispatcher for a PrologProcess
.
Then add a PrologEventListener to this dispatcher.
Every listener can be listening to one or more subjects.
currentDispatcher = new PrologEventDispatcher(currentProcess); currentDispatcher.addPrologEventListener("pdt_edit_hook", this);
The code above (excerpt from org.cs3.pdt.editor.internal.editors.CurrentProcessListener
) creates and registers a listener for the pdt_edit_hook
event.
When the related event is sent by the Prolog side, the update
method of each
registered PrologEventListener
will be called with a parameter of type
PrologEvent that contains the event identifier (pdt_edit_hook
in our example)
and some arbitrary additional data from the second parameter of the pif_notify/2
call (see below).
To send a notification from a Prolog process, call the pif_notify/2
predicate.
Its first argument is an even identifier (which must be an atom) and the second argument an
arbitrary value that will be passed to the Java side along with the event.
pif_observe:pif_notify(pdt_edit_hook, 'l:\test.pl 1')
The above tells the Java side that the pdt_edit_hook
event occurred for the file
'l:\test.pl'. For an example see the module pdt_editor_edit_hook
.