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:
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.
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
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.
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.
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.
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.