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.lang.reflect.Array;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.jdom.Element;
import org.mandarax.kernel.ClauseSet;
import org.mandarax.kernel.Function;
import org.mandarax.kernel.LogicFactory;
import org.mandarax.kernel.Predicate;
import org.mandarax.kernel.Query;
import org.mandarax.kernel.Term;
import org.mandarax.util.logging.LogCategories;
import org.mandarax.xkb.XKBException;

/**
 * An abstract class implementing XMLAdapter. Some utility methods are implemented here.
 * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
 * @version 3.3.1 <9 August 2004>
 * @since 1.6 
 */
public abstract class AbstractXMLAdapter implements XMLAdapter,LogCategories {
      protected LogicFactory lfactory = LogicFactory.getDefaultFactory();
/**
 * Utility method that locates the appropriate driver and exports an object using this driver.
 * @param obj an object
 * @param kindOfObject the kind of object, usually a constant defined in GenericDriver
 * @param driver the generic driver
 * @param cache a cache used in order to associate the same
 * id with various occurences of the same object
 * @return an element
 * @exception an XKBException is thrown if export fails
 */
protected Element exportObject(Object obj,String kindOfObject,GenericDriver driver,Map cache) throws  XKBException {
      XMLAdapter adapter = driver.getAdapterByKindOfObject(kindOfObject);
      return adapter.exportObject(obj,driver,cache);
}
/**
 * Utility method to export an array of objects and add the elements to the parent.
 * @param objs the objects
 * @param parent the parent element
 * @param kindOfObject the kind of object, usually a constant defined in GenericDriver
 * @param driver the generic driver
 * @param cache a cache used in order to associate the same
 * id with various occurences of the same object
 * @exception an XKBException is thrown if export fails
 */
protected void exportChildren(Object[] objs,Element parent,String kindOfObject,GenericDriver driver,Map cache) throws  XKBException {
      XMLAdapter adapter = driver.getAdapterByKindOfObject(kindOfObject);
      for (int i=0;i<objs.length;i++) {
            Element e = adapter.exportObject(objs[i],driver,cache);
            parent.addContent(e);
      }
}
/**
 * Utility method to export a collection of objects and add the elements to the parent.
 * @param objs the objects
 * @param parent the parent element
 * @param kindOfObject the kind of object, usually a constant defined in GenericDriver
 * @param driver the generic driver
 * @param cache a cache used in order to associate the same
 * id with various occurences of the same object
 * @exception an XKBException is thrown if export fails
 */
protected void exportChildren(Collection objs,Element parent,String kindOfObject,GenericDriver driver,Map cache) throws  XKBException {
      XMLAdapter adapter = driver.getAdapterByKindOfObject(kindOfObject);
      for (Iterator it = objs.iterator();it.hasNext();) {
            Element e = adapter.exportObject(it.next(),driver,cache);
            parent.addContent(e);
      }
}
/**
 * Utility method to import a child (the first child) with a certain name (not the tag name but the symbolic name
 * as defined in GenericDriver) of the given parent element.
 * @param e the parent element
 * @param name the name of the child tag
 * @param driver the generic driver
 * @param cache a cache used to identify objects that have an id
 * @param lfactory the logic factory used to create objects
 * @return an object
 * @exception an XKBException is thrown if import fails
 */
protected Object importChild(Element parent,String name,GenericDriver driver,Map cache,LogicFactory lfactory) throws XKBException {
      XMLAdapter adapter = driver.getAdapterByKindOfObject(name);
      String tagName = adapter.getTagName();
      Element child = parent.getChild(tagName);
      return adapter.importObject(child,driver,cache,lfactory);
}
/**
 * Utility method to import the children with a certain name (not the tag name but the symbolic name
 * as defined in GenericDriver) of the given parent element. The name can be null, in this case all
 * child elements will be imported. The target type argument specifies the type of the objects
 * in the array. Note that we return an object , not an array. But this object can be casted to
 * an array of the target type, e.g. like in<br>
 * <code>Term[] terms = importChildren(e,null,driver,cache,lfactory,Term.class)</code>
 * @param e the parent element
 * @param name the name of the child tag
 * @param driver the generic driver
 * @param cache a cache used to identify objects that have an id
 * @param lfactory the logic factory used to create objects
 * @param targetType the expected type of elements in the array
 * @return an object that can be casted to an array of elements of the target type
 * @exception an XKBException is thrown if import fails
 */
protected Object importChildren(Element parent,String name,GenericDriver driver,Map cache,LogicFactory lfactory,Class targetType) throws XKBException {
      XMLAdapter adapter = name==null?null:driver.getAdapterByKindOfObject(name);
      String tagName = name==null?null:adapter.getTagName();
      List children = name==null?parent.getChildren():parent.getChildren(tagName);
      Object array = java.lang.reflect.Array.newInstance(targetType,children.size());
      for (int i=0;i<children.size();i++) {
            if (name==null) adapter = driver.getAdapterByTagName(((Element)children.get(i)).getName());
            Array.set(array,i,adapter.importObject((Element)children.get(i),driver,cache,lfactory));
      }
      return array;
}
/**
 * Utility method that checks the type of an object and throws an exception
 * if the object is not an instance of the respective class.
 * @param obj an object
 * @param clazz a class
 */
protected void check(Object obj,Class clazz) throws XKBException {
      if (obj==null) throw new XKBException("Adapter " + getClass() + " cannot export/import null");
      if (!(clazz.isAssignableFrom(obj.getClass()))) throw new XKBException("Adapter " + getClass() + " cannot export/import " + obj + " since it does not have the following type: " + clazz);
}
/**
 * Get the type of the term, e.g. GenericDriver.COMPLEX_TERM or GenericDriver.VARIABLE_TERM.
 * @param t a term
 * @return a string
 */
protected String getTermType(Term t) {
      if (t instanceof org.mandarax.kernel.VariableTerm) return GenericDriver.VARIABLE_TERM;
      if (t instanceof org.mandarax.kernel.ConstantTerm) return GenericDriver.CONSTANT_TERM;
      if (t instanceof org.mandarax.kernel.ComplexTerm) return GenericDriver.COMPLEX_TERM;
      return null;
}
/**
 * Get the type of the function, e.g. GenericDriver.JFunction or GenericDriver.SQL_FUNCTION.
 * @param f a function
 * @return a string
 */
protected String getFunctionType(Function f) {
      if (f instanceof org.mandarax.kernel.meta.JFunction) return GenericDriver.JFUNCTION;
      if (f instanceof org.mandarax.kernel.meta.DynaBeanFunction) return GenericDriver.DYNA_BEAN_FUNCTION;
      if (f instanceof org.mandarax.sql.SQLFunction) return GenericDriver.SQL_FUNCTION;
      if (f instanceof org.mandarax.lib.AbstractFunction) return GenericDriver.MANDARAX_LIB_FUNCTION;
      return null;
}
/**
 * Get the type of the predicate, e.g. GenericDriver.JPREDICATE or GenericDriver.SQL_PREDICATE.
 * @param p a predicate
 * @return a string
 */
protected String getPredicateType(Predicate p) {
      if (p instanceof org.mandarax.kernel.meta.JPredicate) return GenericDriver.JPREDICATE;
      if (p instanceof org.mandarax.kernel.SimplePredicate) return GenericDriver.SIMPLE_PREDICATE;
      if (p instanceof org.mandarax.sql.SQLPredicate) return GenericDriver.SQL_PREDICATE;
      if (p instanceof org.mandarax.lib.AbstractPredicate) return GenericDriver.MANDARAX_LIB_PREDICATE;
      return null;
}
/**
 * Add additional properties to an element representing a clause set.
 * @param e an element
 * @param cs a clause set
 */
protected void addProperties(Element e,ClauseSet cs) {
	Element eProperties = null;
	for (Enumeration en = cs.propertyNames();en.hasMoreElements();) {
		String nextName = en.nextElement().toString();
		if (eProperties==null) eProperties = new Element(GenericDriver.PROPERTIES);
		Element eProperty = new Element(GenericDriver.PROPERTY);
		eProperty.setAttribute(GenericDriver.PROPERTY_NAME,nextName);
		eProperty.setAttribute(GenericDriver.PROPERTY_VALUE,cs.getProperty(nextName));
		eProperties.addContent(eProperty);
	}
	if (eProperties!=null) e.addContent(eProperties);
}
/**
 * Add additional properties to an element representing a query.
 * @param e an element
 * @param q a query
 */
protected void addProperties(Element e,Query q) {
	Element eProperties = null;
	for (Enumeration en = q.propertyNames();en.hasMoreElements();) {
		String nextName = en.nextElement().toString();
		if (eProperties==null) eProperties = new Element(GenericDriver.PROPERTIES);
		Element eProperty = new Element(GenericDriver.PROPERTY);
		eProperty.setAttribute(GenericDriver.PROPERTY_NAME,nextName);
		eProperty.setAttribute(GenericDriver.PROPERTY_VALUE,q.getProperty(nextName));
		eProperties.addContent(eProperty);
	}
	if (eProperties!=null) e.addContent(eProperties);
}
/**
 * Load the properties into the clause set.
 * @param e an element (the super element of the properties element)
 * @param cs a clause set
 */
protected void loadProperties(Element e,ClauseSet cs) {
	Element eProperties = e.getChild(GenericDriver.PROPERTIES);
	if (eProperties!=null) {
		List children = eProperties.getChildren(GenericDriver.PROPERTY);
		for (Iterator iter = children.iterator();iter.hasNext();) {
			Element eProperty = (Element)iter.next();
			cs.setProperty(eProperty.getAttributeValue(GenericDriver.PROPERTY_NAME),eProperty.getAttributeValue(GenericDriver.PROPERTY_VALUE));
		}
	}
}
/**
 * Load the properties into the query.
 * @param e an element (the super element of the properties element)
 * @param q a query
 */
protected void loadProperties(Element e,Query q) {
	Element eProperties = e.getChild(GenericDriver.PROPERTIES);
	if (eProperties!=null) {
		List children = eProperties.getChildren(GenericDriver.PROPERTY);
		for (Iterator iter = children.iterator();iter.hasNext();) {
			Element eProperty = (Element)iter.next();
			q.setProperty(eProperty.getAttributeValue(GenericDriver.PROPERTY_NAME),eProperty.getAttributeValue(GenericDriver.PROPERTY_VALUE));
		}
	}
}
}
