Query Interpreter

The table is dynamically queried. For this purpose we use the Java class Interpreter of the Jekejeke Prolog programming interface. The Jekejeke Prolog runtime must be present when compiling the class. The query interpreter Java class has no interaction with the end-user. We will only be able to nanually first test it when the Terminal class is ready. In the following we will provide a little walkthrough and explain how the responsibilities of this class are implemented. The full class listing can be found in the appendix.

Let’s turn to the main duty of the query interpreter class. The interpreter class should dynamically build a query for us. The query building process will be dependent on some search criteria that the end user has provided. We will distinguish string search criteria and number search criteria. For the string search criteria we will only provide instantiation search. And for the number search criteria we will provide arithmetic interval search. The search criteria are handed over to the query interpreter via a couple of setters. Here we see some example setters for the string criteria name and part of the number criteria age:

    /**
     * <p>Set the name search criteria.</p>
     *
     * @param n The name search criteria.
     */
    public void setName(String n) {
        name = n;
    }

    /**
     * <p>Set the age from search criteria.</p>
     *
     * @param af The age from search criteria.
     */
    public void setAgeFrom(String af) {
        agefrom = af;
    }

The query building can be invoked via the method makeQuery(). The query building starts with creating the query predicate employee/4 and placing variables in each argument position. This corresponds to an unrestricted query. Depending on the search criteria the query will then be successively restricted. The code for the initialization of the query term looks as follows:

    /**
     * <p>Create the query term.</p>
     *
     * @param vars The query variables.
     * @return The query term.
     */
    public Object makeQuery(TermVar[] vars) {
ArrayList<AbstractTerm> literals = new ArrayList<AbstractTerm>();
literals.add(new TermCompound(inter, "employee",
vars[COLUMN_FIRSTNAME], vars[COLUMN_NAME],
vars[COLUMN_AGE], vars[COLUMN_SALARY]));

Now the idea is that a blank input for a search criteria means, that this search criteria is not used. It will not mean that we search for a fact where the argument matches blank, it will have the meaning of don’t care. In case of a string search criteria we insert a unification condition in the front of the query that is currently built. Here is the corresponding code seen for the search criteria firstname:

        if (!"".equals(name))
            literals.add(0, new TermCompound(inter, "=",
                    firstname, vars[COLUMN_FIRSTNAME]));

In case of a number search criteria we append constant arithmetic predicates at the end of the query that is currently built. Our interval search has a “from” criteria and a “to” criteria. Both criteria can be empty. This allows us to provide open interval search or even to use a number criteria not at all in a search. Here is the corresponding code seen for part of the search criteria age:

        if (!"".equals(agefrom))
            literals.add(new TermCompound(inter, "=<",
                    Integer.valueOf(agefrom), vars[COLUMN_AGE]));

We now turn to the last responsibility of our query interpreter. The query interpreter should not only be able to build a query, but also to execute one. We have opted for a list oriented interface between the query interpreter and its clients. Thus when we execute a query we will get the results in the form of a list of rows. To obtain the rows the query interpreter will open a call-in to the Jekejeke Prolog system:

    /**
     * <p>List the rows of the search result.</p>
     *
     * @param vars      The query variables.
     * @param queryTerm The query term.
     * @return The rows.
     * @throws InterpreterException Problem evaluating the query.
     * @throws InterpreterMessage Problem evaluating the query.
     */
    public Object[][] listRows(TermVar[] vars, Object queryTerm)
            throws InterpreterException, InterpreterMessage {
        ArrayList<Object[]> res = new ArrayList<Object[]>();
        CallIn callin = inter.iterator(queryTerm);

Each row will be a list of values. We will use the objects that Jekejeke Prolog will provide us. This means we will get Strings from atom fact arguments and BigIntegers from integer fact arguments. The query interpreter will then use backtracking over the query term to populate the result with the rows. The backtracking is initiated via the method unfoldFirst(). And continued via the method unfoldNext().

        while (callin.hasNext()) {
callin.next();
Object[] row = new Object[]{
vars[Query.COLUMN_FIRSTNAME].deref(),
vars[Query.COLUMN_NAME].deref(),
vars[Query.COLUMN_AGE].deref(),
vars[Query.COLUMN_SALARY].deref()
};
res.add(row);
        }

This approach will retrieve all solutions into a data structure. This is of course not necessary for the Terminal application, since each solution will be one by one sent to the standard output and can then be forgotten. On the other hand as we will later see, when the solutions are displayed in a table, it is handy to have them all in a data structure so that the table data can easily be retrieved for rendering. Variations on the theme would include returning only a limited number of solutions, returning solutions beginning with a specific position row number, or table models that dynamically retrieve solutions.

Comments