8.3 Java representation of ECLiPSe data
The Java-ECLiPSe Interface uses a set of conventions and Java classes so that data
types common in ECLiPSe can be represented. Representing ECLiPSe data types is useful for:
-
Constructing compound goals to be sent to ECLiPSe for execution.
- Deconstructing the results of ECLiPSe's computation, which are returned as a compound goal.
- Communicating compound terms and other data via queues.
More details on these tasks will be provided in Sections
8.4 and 8.5, but it is first necessary to understand how ECLiPSe data is represented in Java.
8.3.1 General correspondence between ECLiPSe and Java data types
Not all ECLiPSe data types are represented: for example, at present
the ECLiPSe type rational has no Java equivalent. However, all
the most useful ECLiPSe types have a corresponding Java class. Table
8.1 gives the general correspondence between those
ECLiPSe data types which are supported and the Java classes or
interfaces which are used to represent them. The ECLiPSe types/Java
classes which appear in the table are those which map to or from
the ECLiPSe external data representation (EXDR) definition.
ECLiPSe data type |
Java class/interface |
atom |
Atom |
compound |
CompoundTerm |
integer |
java.lang.Integer |
|
java.lang.Long |
list |
java.util.Collection |
float |
java.lang.Double |
|
java.lang.Float |
string |
java.lang.String |
variable |
null |
Table 8.1:
The correspondence between ECL
iPS
e and Java data types. The Java classes are within the
com.parctechnologies.eclipse package unless otherwise specified.
The general rule is that you can send data to ECLiPSe by passing
the relevant method an instance of the Java class corresponding to the
ECLiPSe data type you want on the ECLiPSe side. When data comes
back from ECLiPSe it will be an instance of java.lang.Object
but this can be cast to the relevant Java class, which must either be
known beforehand or determined e.g. using the getClass() method.
There are also a number of peculiarities for certain cases which we
now explain.
8.3.2 Atoms and compound terms
Atoms are simple: these are constructed in Java using the constructor
of the Atom class: the string parameter of the constructor
becomes the atom symbol. Although the Java interface CompoundTerm is
listed above, compound terms (except lists) are usually constructed
using the CompoundTermImpl class, which implements CompoundTerm. Incidentally, Atom also implements CompoundTerm, even though strictly speaking ECLiPSe atoms are not compound. Here is an
example of CompoundTermImpl at work:
// Construct a term in Java to represent the Eclipse term foo(a, b, 3).
private static CompoundTerm construct_term()
{
Atom a = new Atom("a");
Atom b = new Atom("b");
Integer numberThree = new Integer(3);
CompoundTerm theTerm = new CompoundTermImpl("foo", a, b, numberThree);
return(theTerm);
}
This method is taken from the example Java program DataExample1.java
which can be found in the examples directory <eclipse_dir>/doc/examples/JavaInterface. The rest of the program sends
ECLiPSe a goal which tells it to write the term created by construct_term() to stdout.
In this example we use an CompoundTermImpl constructor whose
first parameter is a string which becomes the functor of the term. The
remaining parameters are instances of java.lang.Object. They may
be instances of any class or interface which appears in Table
8.1. These become the arguments of the term. CompoundTermImpl has a number of other convenient constructors. See
the API documentation for details of these.
Note that the object returned by construct_term() is declared
not as a CompoundTermImpl but as a CompoundTerm. CompoundTerm is the Java interface for objects
representing compound terms. Anything which implements CompoundTerm
can be sent to ECLiPSe as a compound term.
Instead of using CompoundTermImpl, you may wish to implement
CompoundTerm yourself. The benefit of this is that you can pass
any object implementing CompoundTerm to an rpc invocation, and
it can supply a functor and arguments without the unnecessary creation
of another object. To do this you may wish to subclass AbstractCompoundTerm.
Whenever you want to construct a list for ECLiPSe or deconstruct a
list coming from ECLiPSe, you use the java.util.Collection
interface. Look at the following method, taken from the example Java
program DataExample2.java (which can be found in the examples
directory <eclipse_dir>/doc/examples/JavaInterface).
// Construct a collection in Java to represent the Eclipse
// list [1, foo(3.5), bar].
private static Collection construct_collection()
{
Collection theCollection = new LinkedList();
theCollection.add(new Integer(1));
theCollection.add(new CompoundTermImpl("foo", new Double(3.5)));
theCollection.add(new Atom("bar"));
return(theCollection);
}
If you study, compile and run DataExample2.java you will see
that the collection is indeed translated into the required ECLiPSe
list. You will also see that order is maintained in the sense that the
order of elements as they appear in the ECLiPSe list will equal the
collection's iterator order (the converse is true if the data is
coming from ECLiPSe to Java).
Also note that the ECLiPSe empty list ([]) is represented in
Java by the constant java.util.Collections.EMPTY_LIST.
8.3.4 Floats and Doubles
The ECLiPSe data type float is always converted to java.lang.Double in Java. However, ECLiPSe can be sent an instance
of java.lang.Double or java.lang.Float: both will be
converted to float in ECLiPSe. One value of java.lang.Double and java.lang.Float has no counterpart in
ECLiPSe: the not-a-number (NaN) value. Infinite values can be sent
in either direction.
ECLiPSe can be sent instances of either java.lang.Integer
(32-bit integers) or java.lang.Long (64-bit integers). Both of
these will be translated to type integer on the ECLiPSe side.
When ECLiPSe sends data to Java, it will decide between the two
classes depending on the number of bits needed for the integer. So for
example, if the number is small enough to fit in an Integer, that is
what will be returned. Note that therefore, the type of number
coming back from ECLiPSe cannot be relied upon to be of one type or
the other if it could fall on either side of the 32-/64-bit boundary.
If you require a set of numbers coming from ECLiPSe to be all of
one Java type e.g. long, then the best approach is to cast the object
returned by ECLiPSe to an instance of java.lang.Number and
then invoke the appropriate conversion method e.g. longValue().
The Java null token is used to represent any variables being
sent to ECLiPSe. All variables coming from ECLiPSe will appear as
null. The limitations of this are discussed in more detail in
Section 8.4.
8.3.7 The equals() method
The equals() method has been overridden for AbstractCompoundTerm and therefore also for Atom and CompoundTermImpl. The implementation returns true iff the
parameter Object implements CompoundTerm and its functor
and arity are equal to those of the AbstractCompoundTerm, and
pairwise invocations of equals() return true between each
of the AbstractCompoundTerm's arguments and the corresponding
argument of the parameter object.