All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ubc.cs.JLog.Foundation.jPrologAPI Maven / Gradle / Ivy

/*
    This file is part of JLog.

    Created by Glendon Holst for Alan Mackworth and the 
    "Computational Intelligence: A Logical Approach" text.
    
    Copyright 1998, 2000, 2002 by University of British Columbia and 
    Alan Mackworth.
    
    This notice must remain in all files which belong to, or are derived 
    from JLog.
    
    Check  or 
     for further information
    about JLog, or to contact the authors.
     
    JLog is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    JLog is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with JLog; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    URLs:  or 
*/
//#########################################################################
//	jPrologAPI
//#########################################################################

package ubc.cs.JLog.Foundation;

import java.lang.*;
import java.util.*;
import java.io.*;
import ubc.cs.JLog.Terms.*;
import ubc.cs.JLog.Parser.*;

/**
* This class is encapsulates the Prolog Engine into a single, simple class, suitable for
* programmatic access.
*
* @author       Glendon Holst
* @version      %I%, %G%
*/
public class jPrologAPI
{
 protected class	KeyPairs
 {
  public Object		termkey;
  public Object		objkey;
  
  public KeyPairs(Object tkey,Object okey)
  {
   termkey = tkey;
   objkey = okey;
  };
 };

 protected static final int			QUERY_STATE_WAITING = 0;
 protected static final int			QUERY_STATE_SUCCEEDED = 1;
 protected static final int			QUERY_STATE_FINISHED = 2;

 protected jPrologServices			prolog;
 protected jAPIQueryThread			thread = null;
 
 protected Hashtable				var_translation_keys = null;
 protected jTermTranslation			translations = null;
 
 protected int 					query_state = QUERY_STATE_WAITING;
 
 /**
 * Basic constructor instantiates a bare-bones Prolog engine, and consults the given source
 * string.
 * All the external facilities, except for file services, that the Prolog engine may require 
 * are null.  This is fine for basic Prolog KBs which do not use output 
 * (e.g., write/1, writeln/1, nl/0), do not use input (e.g., read/1), and do not use
 * the animation environment (e.g., animate/2).  A jPrologFileServices instance provides
 * the file services.
 *
 * @param source	The source string to consult (i.e., the initial KB).
 */
 public 	jPrologAPI(String source)
 {
  prolog = new jPrologServices(new jKnowledgeBase(),
                               new pPredicateRegistry(),
							   new pOperatorRegistry());

  prolog.setFileServices(new jPrologFileServices());

  initTranslation();
  initPrologListeners();
  initConsultSource(source);  
 };

 /**
 * Basic constructor instantiates a bare-bones Prolog engine, and consults the given source
 * input stream.
 * All the external facilities, except for file services, that the Prolog engine may require 
 * are null.  This is fine for basic Prolog KBs which do not use output 
 * (e.g., write/1, writeln/1, nl/0), do not use input (e.g., read/1), and do not use
 * the animation environment (e.g., animate/2).  A jPrologFileServices instance provides
 * the file services.
 *
 * @param source	The source input stream to consult (i.e., the initial KB).
 */
 public 	jPrologAPI(InputStream source) throws IOException
 {
  prolog = new jPrologServices(new jKnowledgeBase(),
                               new pPredicateRegistry(),
							   new pOperatorRegistry());

  prolog.setFileServices(new jPrologFileServices());

  initTranslation();
  initPrologListeners();
  initConsultSource(prolog.getFileServices().getTextFromInputStream(source));  
 };

 /**
 * Complete constructor instantiates a bare-bones Prolog engine, sets the external services
 * to those provided, and consults the given source string.
 * If any external facilities are null, then the Prolog source should not use any predicates
 * which require those facilities.
 *
 * @param source	The source string to consult (i.e., the initial KB).
 * @param fs		External file services facilities (e.g., used for load_library/1 etc.)  If
 *					null, an instance of jPrologFileServices is used by default.
 * @param output	External text ouput facilities (e.g., used for writeln/1 etc.)
 * @param input		External text input facilities (e.g., used for read/1 etc.)
 * @param ae		External animation display facilities (e.g., used for animation/2 etc.)
 *					Must be of type aAnimationEnvironment, else an exception will
 *					be thrown when proving animation/2.
 */
 public 	jPrologAPI(String source,iPrologFileServices fs,
							PrintWriter output,
							BufferedReader input,
							Object ae)
 {
  prolog = new jPrologServices(new jKnowledgeBase(),
                               new pPredicateRegistry(),
							   new pOperatorRegistry());

  if (fs != null)
   prolog.setFileServices(fs);
  else
   prolog.setFileServices(new jPrologFileServices());
   
  prolog.setDefaultOutput(output);
  prolog.setDefaultInput(input);
  prolog.setAnimationEnvironment(ae);

  initTranslation();
  initPrologListeners();
  initConsultSource(source);  
 };

 /**
 * Complete constructor instantiates a bare-bones Prolog engine, sets the external services
 * to those provided, and consults the given source input stream.
 * If any external facilities are null, then the Prolog source should not use any predicates
 * which require those facilities.
 *
 * @param source	The source input stream to consult (i.e., the initial KB).
 * @param fs		External file services facilities (e.g., used for load_library/1 etc.)  If
 *					null, an instance of jPrologFileServices is used by default.
 * @param output	External text ouput facilities (e.g., used for writeln/1 etc.)
 * @param input		External text input facilities (e.g., used for read/1 etc.)
 * @param ae		External animation display facilities (e.g., used for animation/2 etc.)
 *					Must be of type aAnimationEnvironment, else an exception will
 *					be thrown when proving animation/2.
 */
 public 	jPrologAPI(InputStream source,iPrologFileServices fs,
							PrintWriter output,
							BufferedReader input,
							Object ae) throws IOException
 {
  prolog = new jPrologServices(new jKnowledgeBase(),
                               new pPredicateRegistry(),
							   new pOperatorRegistry());

  if (fs != null)
   prolog.setFileServices(fs);
  else
   prolog.setFileServices(new jPrologFileServices());
   
  prolog.setDefaultOutput(output);
  prolog.setDefaultInput(input);
  prolog.setAnimationEnvironment(ae);

  initTranslation();
  initPrologListeners();
  initConsultSource(prolog.getFileServices().getTextFromInputStream(source));  
 };

 protected void				initTranslation()
 {
  var_translation_keys = new Hashtable();
 
  translations = new jTermTranslation(prolog);
  translations.setDefaults();
 };

 protected void				initPrologListeners()
 {
  prolog.addRetryQueryListener(new jPrologServiceListener()
                       {
                        public void 	handleEvent(jPrologServiceEvent e)
                        {
                         if (e instanceof jUserQueryEvent)
                         {jUserQueryEvent 	uqe = (jUserQueryEvent) e;
                           
                          if (uqe.getResult())
                           jPrologAPI.this.setQueryResultState(true);
                         }
                        }
                       }
                      );
   prolog.addEndQueryListener(new jPrologServiceListener()
                       {
                        public void 	handleEvent(jPrologServiceEvent e)
                        {
						 if (jPrologAPI.this.query_state == QUERY_STATE_WAITING)
                          jPrologAPI.this.setQueryResultState(false);
                        }
                       }
                      );
 };
 
 protected void				initConsultSource(String source)
 {pParseStream		parser = new pParseStream(source,prolog.getKnowledgeBase(),
												prolog.getPredicateRegistry(),
												prolog.getOperatorRegistry());
  prolog.start();

  consultSource(source);
 };

 /**
 * This function returns the credit assignment and copyright informaiton string.
 * It must be preserved by authors of derivative works. If the derivative work displays
 * credit information, use this method to get the JLog specific information.
 *
 * @return 	The credit and information string.
 */ 
 public String 			getRequiredCreditInfo()
 {
  return jPrologServices.getRequiredCreditInfo();
 };

 /**
  * Set the behaviour for unknown predicates.  By default the behaviour is that 
  * missing predicates throw an exception.
  *
  * @param fp 		If fail (default) then missing predicates generate a 
  *					failing exception (i.e., exception thrown), if false 
  *					then the query for that predicate fails (i.e., no exception is thrown).
  */
 public void 					setFailUnknownPredicate(boolean fp)
 {
  prolog.setFailUnknownPredicate(fp);
 };

 /**
  * Get the behaviour for unknown predicates.  
  *
  * @return			If false, then exceptions are thrown for missing predicates, otherwise
  *                 the query for that predicate fails (no exception thrown).
  */
 public boolean 				getFailUnknownPredicate()
 {
  return prolog.getFailUnknownPredicate();
 };
 
 /**
  * Associates a variable with translation keys.  When the variable binding hashtable is
  * passed to the query method, the default behaviour is to translate the bound values
  * according to their type, but if a special translation is preferred, then the key value
  * is used to determine the mapping.  These keys are used to find the preferred iObjectToTerm 
  * or iTermToObject conversion in the TermTranslation object, .
  *
  * @param v		The name of the variable.
  * @param tkey		The translation lookup key to use for translating the value bound to this
  *					variable to a prolog term.  Set to null to use the default translation.
  * @param okey		The translation lookup key to use for translating the value bound to this
  *					variable back to a Java object. Set to null for the default translation.
  */
 public void				setVariableTranslationKeys(String v,Object tkey,Object okey)
 {
  if (tkey == null && okey == null)
   var_translation_keys.remove(v);
  else
   var_translation_keys.put(v,new KeyPairs(tkey,okey));
 };
 
 /**
  * Get the current translation unit used to convert queries and results between Prolog terms 
  * and Java objects.  Modifying the conversion units associated with the returned
  * translation object will affect how the API processes query input and results.
  *
  * @return		The jTermTranslation object used for translating between 
  *				objects and terms.
  */
 public jTermTranslation	getTranslation()
 {
  return translations;
 };
    
 /**
  * Sets the translation unit used to convert queries and results between Prolog terms and 
  * Java objects.
  *
  * @param tt		The jTermTranslation object to use for translating between 
  *					objects and terms.
  */
 public void				setTranslation(jTermTranslation tt)
 {
  translations = tt;
 };

 /**
 * Consult a source string.
 * 
 * @param source	The KB source string to consult.
 */
 public synchronized void				consultSource(String source)
 {pParseStream		parser = new pParseStream(source,prolog.getKnowledgeBase(),
												prolog.getPredicateRegistry(),
												prolog.getOperatorRegistry());

  joinForcedQueryCompletion();
  
  if (thread != null && thread.isAlive())
  {
   thread.broadcasted_stop();
   
   try
   { 
    thread.join();
   }
   catch (InterruptedException e)
   {
   }
  }

  {jAPIConsultThread	cthread;
   
   if (!prolog.start(cthread = new jAPIConsultThread(prolog,source)))
   {
    throw new NoThreadAvailableException("consult failed, other events pending.");
   }
   else
   {
    try
    { 
     cthread.join();
    }
    catch (InterruptedException e)
    {
    }
    {RuntimeException		ex = cthread.getResultException();
  
     if (ex != null)
      throw ex;
    }
   }
  } 
 };
    
 /**
 * Initiates a query.
 * 
 * @param query		The query string (must be non-empty).
 * @return			Returns a hashtable with all the variables in the query, and their bindings.
 *					Each key in the hashtable is a named variable, and the object type of the
 *					associated value is translated using the
 *					jTermTranslation object associated with this API object (to translate from
 *					Object to jTerm).  Returns null if the query fails.
 */
 public Hashtable			query(String query)
 {
  return query(query,null);
 };

 /**
 * Initiates a query, and stops the query after the first result.
 * 
 * @param query		The query string (must be non-empty).
 * @return			Returns a hashtable with all the variables in the query, just as the 
 *					query method does.  Returns null if the query fails.
 */
 public synchronized Hashtable			queryOnce(String query)
 {Hashtable			result;
 
  result = query(query,null);

  stop();
  
  return result;
 };

 /**
 * Initiates a query, pre-binding variables based on the key:value pairs in the
 * given hashtable.  For each entry (Var:Value) in values, a variable named Var is created
 * and the variable instance is bound to the translated jTerm of Value.  See 
 * jTermTranslation for more information about how translation between
 * Java objects and Prolog terms is performed.  By default, the jPrologAPI class uses the
 * default translation, but the user may set the preferred jTermTranslation object.
 * 
 * @param query		The query string (must be non-empty).
 * @param bindings	A hashtable mapping variable names (key) to their values (value).  If
 *					null, then performs the same as query(String) above.
 * @return			Returns a hashtable of variable bindings (just like 
 *					query(String) above).  The values are translated using the
 *					translation object associated with this API object (to translate from
 *					jTerm to Object).  Returns null if the query fails.
 */
 public synchronized Hashtable			query(String query,Hashtable bindings)
 {Hashtable		bind_terms = null;
 
  if (bindings != null)
  {Enumeration			e = bindings.keys();
  
   bind_terms = new Hashtable();
   
   while (e.hasMoreElements())
   {String		key = (String) e.nextElement();
    Object		value = bindings.get(key);
    KeyPairs	kpairs;
    jTerm		term;
	
	if ((kpairs = (KeyPairs) var_translation_keys.get(key)) != null && kpairs.termkey != null)
	 term = translations.createTermFromObject(value,kpairs.termkey);
	else
	 term = translations.createTermFromObject(value);

    bind_terms.put(key,term);
   }
  }  

  joinForcedQueryCompletion();
  
  if (!prolog.start(thread = new jAPIQueryThread(prolog,bind_terms,query)))
  {
   throw new NoThreadAvailableException("query failed, other events pending.");
  }
   
  return waitForCompletion();
 };

 /**
 * Initiates a query, pre-binding variables based on the key:value pairs in the
 * given hashtable, but stops the query after the first result.
 * 
 * @param query		The query string (must be non-empty).
 * @param bindings	A hashtable mapping variable names (key) to their values (value).  
 *					Just as the corresponding query method.
 * @return			Returns a hashtable of variable bindings (just like 
 *					query method above.  Returns null if the query fails.
 */
 public synchronized Hashtable			queryOnce(String query,Hashtable bindings)
 {Hashtable		result;
 
  result = query(query,bindings);
  
  stop();
  
  return result;  
 };

 /**
 * Retries the previous query.
 * 
 * @return			Returns a hashtable of variable bindings (just like 
 *					query(String) above).  Returns null if the query retry fails.
 */
 public synchronized Hashtable		retry()
 {
  query_state = QUERY_STATE_WAITING;

  if (thread != null && thread.isAlive())
   thread.retry();
  else
   return null;
  
  return waitForCompletion();
 };

 public synchronized void 		stop()
 {
  if (thread != null && thread.isAlive())
   thread.broadcasted_stop();
   
  query_state = QUERY_STATE_FINISHED;
 };

 protected synchronized Hashtable 	waitForCompletion()
 {
  while (query_state == QUERY_STATE_WAITING)
  {
   try
   {
    wait();   
   }
   catch (InterruptedException e)
   {
    return null;
   }
  }
  
  {RuntimeException		ex = thread.getResultException();
  
   if (ex != null)
    throw ex;
  }
  {Hashtable		ht = null;
   
   if (query_state == QUERY_STATE_SUCCEEDED)
    ht = thread.getResultHashtable();

   query_state = QUERY_STATE_WAITING;
  
   // translation term values to objects
   if (ht != null)
   {Enumeration		e = ht.keys();
   
    while (e.hasMoreElements())
    {String		key = (String) e.nextElement();
     jTerm		value = (jTerm) ht.get(key);
     KeyPairs	kpairs;
     Object		obj;
	
	 if ((kpairs = (KeyPairs) var_translation_keys.get(key)) != null && kpairs.objkey != null)
	  obj = translations.createObjectFromTerm(value,kpairs.objkey);
	 else
	  obj = translations.createObjectFromTerm(value);

     ht.put(key,obj);
    }
   }
  
   return ht;
  }
 };

 protected synchronized void	setQueryResultState(boolean result)
 {
  if (query_state == QUERY_STATE_WAITING)
  {
   query_state = (result ? QUERY_STATE_SUCCEEDED : QUERY_STATE_FINISHED);
   notify();
  }
 };
 
 protected synchronized void	joinForcedQueryCompletion()
 {
  if (thread != null && thread.isAlive())
  {
   query_state = QUERY_STATE_FINISHED;

   thread.broadcasted_stop();
   
   try
   { 
    thread.join();
   }
   catch (InterruptedException e)
   {
   }
  }

  query_state = QUERY_STATE_WAITING;
 }
};




© 2015 - 2025 Weber Informatics LLC | Privacy Policy