package org.mandarax.xkb.framework;

/**
 *  Copyright (C) 1999-2004  Jens Dietrich (mailto:mandarax@jbdietrich.com)
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import org.jdom.Document;
import org.jdom.Element;
import org.mandarax.kernel.KnowledgeBase;
import org.mandarax.kernel.LogicFactory;
import org.mandarax.util.NullWriter;
import org.mandarax.xkb.XKBDriver;
import org.mandarax.xkb.XKBException;
/**
 * A generic driver that uses XMLAdapter to map objects to XML.<br>
 * The driver can be more or less fault tolerant. The level of fault tolerance
 * can be set using the respective constants. Furthermore, we can set
 * a print writer that the adapter can use to report events or problems that do not lead
 * to exceptions. The fault tolerance concepts applies in particular on the level of
 * knowledge bases. Here the following guidelines should be used:
 * <ul>
 * <li>If the fault tolerance level is medium, each problem should interrupt the export
 * or import and the error should be "bubbled up"
 * <li>If the level is medium, the export or import can continue even id it fails for some clauses.
 * But the log writer should be used to post a descriptive message, and (for export) a comment
 * should be inserted into the document where the clause set is supposed to be.
 * <li>If the level is high, problems with export or import of single clauses are just ignored.
 * </ul>
 * The default fault tolerance level is medium.
 * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
 * @version 3.3.1 <9 August 2004>
 * @since 1.6
 */
public abstract class GenericDriver implements XKBDriver {

      protected PrintWriter out = NullWriter.printWriter;
      protected int faultToleranceLevel = GenericDriver.DEFAULT_FAULT_TOLERANCE;

      // constants - symbolic adapter names
      public static final String   RULE = "rule";
      public static final String   FACT = "fact";
      public static final String   PREREQUISITE = "prereq";
      public static final String   COMPLEX_TERM = "complex term";
      public static final String   CONSTANT_TERM = "constant term";
      public static final String   VARIABLE_TERM = "variable term";
      public static final String   OBJECT = "object";
      public static final String   SIMPLE_PREDICATE = "simple predicate";
      public static final String   SQL_PREDICATE = "sql predicate";
      public static final String   JPREDICATE = "jpredicate";
      public static final String   MANDARAX_LIB_PREDICATE = "mandarax lib predicate";
      public static final String   JFUNCTION = "jfunction";
      public static final String   DYNA_BEAN_FUNCTION = "dynabeanfunction";
      public static final String   SQL_FUNCTION = "sql function";
      public static final String   MANDARAX_LIB_FUNCTION = "mandarax lib function";
      public static final String   DATA_SOURCE = "data source";
      public static final String   KNOWLEDGE_BASE = "knowledge base";
      public static final String   TYPE = "type";
      public static final String   METHOD = "method";
      public static final String   SQL_CLAUSE_SET = "sql clause set";
      public static final String   QUERY = "query";
      public static final String   PROPERTIES = "properties";
      public static final String   PROPERTY = "property";
      public static final String   PROPERTY_VALUE = "value";
      public static final String   PROPERTY_NAME = "name";
      public static final String   COMPARATOR = "comparator";

      // adapters by tag name - useful to locate adapters to import
      private Map adaptersByTagName = new Hashtable();
      private Map adaptersByKindOfObject = new Hashtable();

     // fault tolerance levels
     public static final int LOW_FAULT_TOLERANCE = 0;
     public static final int MEDIUM_FAULT_TOLERANCE = 1;
     public static final int HIGH_FAULT_TOLERANCE = 2;
     public static final int DEFAULT_FAULT_TOLERANCE = MEDIUM_FAULT_TOLERANCE;

/**
 * Constructor.
 */
public GenericDriver() {
      super();
}
/**
 * Install an adapter.
 * @param adapter an adapter
 */
public void install(XMLAdapter adapter) {
      adaptersByTagName.put(adapter.getTagName(),adapter);
      adaptersByKindOfObject.put(adapter.getKindOfObject(),adapter);
}
/**
 * Get an adapter by type, the type is a name as defined in static variables here.
 * @param typeName a text indicating the kind of object the adapter can export/import
 */
public XMLAdapter getAdapterByKindOfObject(String typeName) throws XKBException {
      XMLAdapter adapter = (XMLAdapter)adaptersByKindOfObject.get(typeName);
      if (adapter==null) throw new XKBException("Cannot find adapter for the following kind of object: " + typeName);
      return adapter;
}
/**
 * Get an adapter by tag name.
 * @param tagName a text indicating the name of an xml element
 */
public XMLAdapter getAdapterByTagName(String tagName) throws XKBException {
      XMLAdapter adapter = (XMLAdapter)adaptersByTagName.get(tagName);
      if (adapter==null) throw new XKBException("Cannot find adapter for tag <" + tagName + ">");
      return adapter;
}
/**
 * Get a short text describing the driver.
 * @return a text
 */
public String getDescription() {
	return "Generic driver for knowledge bases";
}
/**
 * Get the location (URL) of the associated DTD.
 * @return a text
 */
public String getDTD() {
	return "?";
}
/**
 * Get the name of the driver.
 * @return a text
 */
public String getName() {
	return "Generic Driver";
}
/**
 * Export a knowledge base, i.e., convert it into an xml document.
 * @return an xml document
 * @param kb a knowledge base
 * @exception an XKBException is thrown if export fails
 */
public Document exportKnowledgeBase(KnowledgeBase kb)  throws XKBException {
	XMLAdapter adapter = getAdapterByKindOfObject(KNOWLEDGE_BASE);
        return new Document(adapter.exportObject(kb,this,new HashMap()));
}
/**
 * Import a knowledge base, i.e., build it from an xml document.
 * @return a knowledge base
 * @param doc an xml document
 * @exception an XKBException is thrown if import fails
 */
public KnowledgeBase importKnowledgeBase(Document doc)  throws XKBException {
	Element root = doc.getRootElement();
        XMLAdapter adapter = getAdapterByTagName(root.getName());
        return (KnowledgeBase)adapter.importObject(root,this,new HashMap(),LogicFactory.getDefaultFactory());
}
/**
 * Import a knowledge base.
 * @return a knowledge base
 * @param e an xml element
 * @exception an XKBException is thrown if import fails
 */
public KnowledgeBase importKnowledgeBase(Element e) throws XKBException {

	KnowledgeBase kb = (KnowledgeBase)this.getAdapterByTagName(e.getName());
	return kb;
}
/**
 * Indicates whether the driver (and the underlying xml format (=dtd))
 * supports auto facts.
 * @return a boolean
 */
public boolean supportsAutoFacts() {
	return false;
}
/**
 * Indicates whether the driver (and the underlying xml format (=dtd))
 * supports clause sets.
 * @return a boolean
 */
public boolean supportsClauseSets(){
	return true;
}
/**
 * Indicates whether the driver (and the underlying xml format (=dtd))
 * supports facts. (some formats might see facts as rules without body)
 * @return a boolean
 */
public boolean supportsFacts() {
	return true;
}
/**
 * Indicates whether the driver (and the underlying xml format (=dtd))
 * supports functions.
 * @return a boolean
 */
public boolean supportsFunctions() {
	return true;
}
/**
 * Indicates whether the driver (and the underlying xml format (=dtd))
 * supports the java semantics (e.g. JFunctions, JPredicate).
 * @return a boolean
 */
public boolean supportsJavaSemantics(){
	return true;
}
/**
 * Indicates whether the driver (and the underlying xml format (=dtd))
 * supports multiple premises.
 * @return a boolean
 */
public boolean supportsMultiplePremises() {
	return true;
}
/**
 * Indicates whether the driver (and the underlying xml format (=dtd))
 * supports multiple premises connected by OR.
 * @return a boolean
 */
public boolean supportsOrPremises() {
	return true;
}
/**
 * Indicates whether the driver (and the underlying xml format (=dtd))
 * supports types.
 * @return a boolean
 */
public boolean supportsTypes(){
	return true;
}
/**
 * Indicates whether the driver (and the underlying xml format (=dtd))
 * supports queries.
 * @return a boolean
 */
public boolean supportsQueries(){
	return adaptersByKindOfObject!=null && adaptersByKindOfObject.containsKey(QUERY);
}
/**
 * Indicates whether the driver (and the underlying xml format (=dtd))
 * supports negation as failure.
 * @return a boolean
 */
public boolean supportsNegationAsFailure(){
	return false;
}
/**
 * Get the log writer.
 * @return a print writer
 */
public PrintWriter getLogWriter(){
	return out==null?NullWriter.printWriter:out;
}
/**
 * Set the log writer.
 * @param pw a print writer
 */
public void setLogWriter(PrintWriter pw){
      out = pw;
}
/**
 * Get the fault tolerance level.
 * @return a number (see the constants defined in GenericDriver)
 */
public int getFaultToleranceLevel(){
      return faultToleranceLevel;
}
/**
 * Set the fault tolerance level.
 * @param level a number (see the constants defined in GenericDriver)
 */
public void setFaultToleranceLevel(int level){
      faultToleranceLevel = level;
}

}
