/* This software was developed within the context of the project Metaphor for Science
	Museums (Mesmuses) in the framework of the Information Society Technology
	Programme, part of the Fifth Research And Technological Development
	Framework Programme of the European Community, under the Consortium
	Agreement dated <June 30, 2001>, between INRIA, Finsiel Spa, IMSS,
	Valoris , CSI, ICS-FORTH, ENSTB, EDW International.
*/
//Optimize not keep path

/*
 *  CHANGE LOG:
 *  Each entry starts with a date, who did the changed and what has been done.
 *  Separated by ':'.
 *
 *  14.11.2002 : Karsten Tolle : Changed the RDF_DAG structure and therefore
 *                               adopted the Broker.
 */


package gr.forth.ics.vrp.corevrp.broker;

import java.util.*;
import gr.forth.ics.vrp.corevrp.model.*;
import gr.forth.ics.vrp.corevrp.validator.*;
import gr.forth.ics.vrp.corevrp.vocabulary.*;

/**
 *
 * @author Ta Tuan Anh
 * @version 2.5  (30 Januar 2003)
 */
public class RDF_SemanticBroker {

	private Model basic_model;

	private RDF_Validator validator;

	/* for the reason:
		  getting all classes, resources, ... is a time consumming task,
	  we create a Collection to contain them
	*/
	private Collection allClasses;
	private Collection allProperties;
	// all resources: data, container, statement
	private Collection allResources;

	// all classes literal and xml datatype
	private Collection attributeClasses;
	// all meta classes
	private Collection metaClasses;
	// all property classes
	private Collection propertyClasses;
	// all container classes
	private Collection containerClasses;
	// all statement classes
	private Collection statementClasses;

	/* for a transitive procedure we create hashmaps to evite the repeat,
		  class -> all super classes
	  class -> all sub classes
	  class -> all transitive properties (class is domain)
	  class -> all transitive properties (class is range)
	  class -> all transitive instances

	  This is a kind of cache, while a transitive information is calculated,
	  we put it in the relevant map
	*/
	private HashMap superClasses = new HashMap();
	private HashMap subClasses = new HashMap();
	private HashMap domainProperties = new HashMap();
	private HashMap rangeProperties = new HashMap();
	private HashMap instanceResources = new HashMap();
	private HashMap superProperties = new HashMap();
	private HashMap subProperties = new HashMap();

	private HashMap namespaces = new HashMap();

	public RDF_SemanticBroker (Model mdl, RDF_Validator validator)
	{
		this.basic_model = mdl;
		this.validator = validator;
		allClasses = basic_model.getClasses();
		allProperties = basic_model.getProperties();
		metaClasses = basic_model.getMetaClasses();
		propertyClasses = basic_model.getMetaProperties();
		// resource here can be a data or container or statement
		allResources = new ArrayList();
		for (Iterator i=basic_model.getNodes().iterator(); i.hasNext(); )
		{
			Object next = i.next();
			if ( !(next instanceof RDF_Class) && !(next instanceof RDF_Property)
					&& (next instanceof RDF_Resource)  ) {
				allResources.add(next);
			}
		}

		attributeClasses = new HashSet();
		if (basic_model.contains(rdfschema.Literal))
		{
			RDF_Class literal = (RDF_Class)basic_model.getResource(rdfschema.Literal);
			attributeClasses.add(literal);
			attributeClasses.addAll(getSubClasses(literal, true, false));
		}
		for (Iterator i=XMLSchema_DT.XMLSchemaDatatypes.iterator(); i.hasNext(); )
		{
			String xmltype = (String)i.next();
			if (basic_model.contains(xmltype))
			{
				RDF_Class xmlcls = (RDF_Class)basic_model.getResource(xmltype);
				attributeClasses.add(xmlcls);
				attributeClasses.addAll(getSubClasses(xmlcls, true, false));
			}
		}
		containerClasses = new HashSet();
		if (basic_model.contains(rdfschema.Container))
		{
			RDF_Class rdf_container = (RDF_Class)basic_model.getResource(rdfschema.Container);
			containerClasses.add(rdf_container);
			containerClasses.addAll(getSubClasses(rdf_container, true, false));
		}
		statementClasses = new HashSet();
		if (basic_model.contains(rdf.Statement))
		{
			RDF_Class rdf_statement = (RDF_Class)basic_model.getResource(rdf.Statement);
			statementClasses.add(rdf_statement);
			statementClasses.addAll(getSubClasses(rdf_statement, true, false));
		}
		initNamespaces();
   }

   public class RDF_ResourceComparator implements Comparator {
   	public int compare(Object o1, Object o2)
		{
   		return ((Resource)o1).getID().compareTo(((Resource)o2).getID());
   	}
   	public boolean equals(Object obj)
   	{
   		return false;
   	}
   }

   public RDF_Resource getResourceByID(String id)
   {
   		Object obj = basic_model.getResource(id);
		if (obj instanceof RDF_Resource)
			return (RDF_Resource) obj;
		else return null;
   }

   // interface for getting information from a class

   public Collection getAllClasses(boolean sorted)
   {
		Collection clt;
		if (sorted) clt = new TreeSet(new RDF_ResourceComparator());
		else clt = new ArrayList();
		/*for (Iterator i = allClasses.iterator(); i.hasNext(); )
		{
			RDF_Class cls = (RDF_Class)i.next();
			String uri = cls.getID();
			if (uri.equals(rdfschema.Class)||uri.equals(rdfschema.Literal)||uri.equals(rdf.Property)
				||uri.startsWith(XMLSchema_DT.NS)) continue;
			//if (uri.startsWith(rdfschema.NS)||uri.startsWith(rdf.NS)
			//		||uri.startsWith(XMLSchema_DT.NS)) continue;
			clt.add(cls);
		}*/

		clt.addAll(allClasses);

		/*if (basic_model.contains(rdfschema.Class))
		{
			RDF_Class rdfs_cls = (RDF_Class)basic_model.getResource(rdfschema.Class);
			clt.remove(rdfs_cls);
			clt.removeAll(getSubClasses(rdfs_cls, true, false));
		}
		if (basic_model.contains(rdf.Property))
		{
			RDF_Class rdf_prop = (RDF_Class)basic_model.getResource(rdf.Property);
			clt.remove(rdf_prop);
			clt.removeAll(getSubClasses(rdf_prop, true, false));
		}*/
		return clt;
   }

   public RDF_Class getMetaClass(RDF_Class cls)
   {
		RDF_Class meta = null;
		for (Iterator i=cls.gettype().iterator(); i.hasNext(); )
		{
			meta = (RDF_Class) i.next();
			if (isMetaClass(meta)) break;
		}
		return meta;
	}

	private HashSet getSubFromDAG(RDF_DAG dag, Object uri){
		ArrayList nodeValues = (ArrayList) dag.getdag().get(uri);
		return (HashSet)nodeValues.get(0);
	}

	private HashSet getSuperFromDAG(RDF_DAG dag, Object uri)
   {
     	ArrayList nodeValues = (ArrayList) dag.getdag().get(uri);
   		return (HashSet) nodeValues.get(1);
   }

   public Collection getSubClasses(RDF_Class cls, boolean transitive, boolean sorted)
	{
		Collection clt;
		if (sorted) clt = new TreeSet(new RDF_ResourceComparator());
		else clt = new ArrayList();
		if (transitive&&subClasses.containsKey(cls))
		{
			clt.addAll((Collection)subClasses.get(cls));
			return clt;
		}
		RDF_DAG dag = validator.getsubClassOfDAG();
		HashSet subURIs=getSubFromDAG(dag, cls);
   		for (Iterator i=subURIs.iterator(); i.hasNext(); )
                        clt.add(i.next());
			// clt.add(basic_model.getResource((String)i.next()));
		if (transitive)
		{
			HashSet subCls = new HashSet();
			for (Iterator i=clt.iterator(); i.hasNext(); )
				subCls.addAll(getSubClasses((RDF_Class)i.next(), true, false));
			clt.addAll(subCls);
			subClasses.put(cls, clt);
		}
		return clt;
   }

   public Collection getSuperClasses(RDF_Class cls, boolean transitive, boolean sorted)
   {
		Collection clt;
		if (sorted) clt = new TreeSet(new RDF_ResourceComparator());
		else clt = new ArrayList();
		if (transitive&&superClasses.containsKey(cls))
		{
			clt.addAll((Collection)superClasses.get(cls));
			return clt;
		}
		RDF_DAG dag = validator.getsubClassOfDAG();
		HashSet superURIs=getSuperFromDAG(dag, cls);
   		for (Iterator i=superURIs.iterator(); i.hasNext(); )
                        clt.add(i.next());
			//clt.add(basic_model.getResource((String)i.next()));
		if (transitive)
		{
			HashSet superCls = new HashSet();
			for (Iterator i=clt.iterator(); i.hasNext(); )
				superCls.addAll(getSuperClasses((RDF_Class)i.next(), true, false));
			clt.addAll(superCls);
			superClasses.put(cls, clt);
		}
		return clt;
   }

   public Collection getRootClasses(boolean sorted)
   {
   		Collection clt;
   		if (sorted) clt = new TreeSet(new RDF_ResourceComparator());
   		else clt = new ArrayList();
   		Collection roots = validator.getsubClassOfDAG().getroots();
		for (Iterator i=roots.iterator(); i.hasNext(); )
		{
			//String uri = (String)i.next();
			// get also metaclass
			// if (uri.equals(rdfschema.Class)||uri.equals(rdf.Property)) continue;
			clt.add(i.next());
		}
   		return clt;
   }

   public boolean isRootClass(RDF_Class cls)
   {
   		return validator.getsubClassOfDAG().getroots().contains(cls);
   }

   public boolean isLeafClass(RDF_Class cls)
   {
   		RDF_DAG dag = validator.getsubClassOfDAG();
   		return getSubFromDAG(dag, cls).isEmpty();
   }

   public boolean isMetaClass(RDF_Class cls)
   {
   		return metaClasses.contains(cls);
   }
   public boolean isPropertyClass(RDF_Class cls)
   {
   		return propertyClasses.contains(cls);
   }
   public boolean isContainerClass(RDF_Class cls)
   {
   		return containerClasses.contains(cls);
   }
   public boolean isStatementClass(RDF_Class cls)
   {
   		return containerClasses.contains(cls);
   }
   public boolean isSchemaClass(RDF_Class cls)
   {
   		return isMetaClass(cls)||isPropertyClass(cls);
   }


   public Collection getInstanceResources(RDF_Class cls, boolean transitive, boolean sorted)
   {
	   // get instance resources of a none-schema class (normal or container class)
	   Collection clt;
	   if (sorted) clt = new TreeSet(new RDF_ResourceComparator());
	   else clt = new ArrayList();
	   // may be instance resource is in the transitive map
	   if (transitive&&instanceResources.containsKey(cls))
	   {
	   		clt.addAll((Collection)instanceResources.get(cls));
	   		return clt;
	   }
	   for (Iterator i = allResources.iterator(); i.hasNext(); )
	   {
			RDF_Resource resrc = (RDF_Resource) i.next();
			Collection types = resrc.gettype();
			if (types.contains(cls)) clt.add(resrc);
	   }
	   if (transitive)
	   {
	   		HashSet instances = new HashSet();
	   		for (Iterator i=getSubClasses(cls, false, false).iterator(); i.hasNext(); )
	   			instances.addAll(getInstanceResources((RDF_Class)i.next(), true, false));
	   		clt.addAll(instances);
	   		instanceResources.put(cls, clt);
	   }
	   return clt;
   }

   public Collection getClassesOfMetaclass(RDF_Class cls, boolean sorted)
   {
      Collection clt;
      if (sorted) clt = new TreeSet(new RDF_ResourceComparator());
      else clt = new ArrayList();
      for (Iterator i = allClasses.iterator(); i.hasNext(); )
      {
   		RDF_Class inst = (RDF_Class) i.next();
   		Collection types = inst.gettype();
   		if (types.contains(cls)) clt.add(inst);
      }
      return clt;
   }

   public Collection getPropertiesOfClass(RDF_Class cls, boolean sorted)
   {
      Collection clt;
      if (sorted) clt = new TreeSet(new RDF_ResourceComparator());
      else clt = new ArrayList();
      for (Iterator i = allProperties.iterator(); i.hasNext(); )
      {
   		RDF_Property inst = (RDF_Property) i.next();
   		Collection types = inst.gettype();
   		if (types.contains(cls)) clt.add(inst);
      }
      return clt;
   }

   public Collection getPropertiesWithDomain(RDF_Class cls, boolean transitive)
   {
	   // may be in the cache
	   if (transitive&&domainProperties.containsKey(cls))
	   		return (Collection)domainProperties.get(cls);
   	   Collection clt = new ArrayList();
	   for (Iterator i = allProperties.iterator(); i.hasNext(); )
	   {
	   		RDF_Property prop = (RDF_Property)i.next();
	   		Collection domains = prop.getdomain();
	   		if (domains.contains(cls)) clt.add(prop);
	   }
	   if (transitive)
	   {
	   		HashSet domainProps = new HashSet();
	   		for (Iterator i=getSuperClasses(cls, false, false).iterator(); i.hasNext(); )
	   			domainProps.addAll(getPropertiesWithDomain((RDF_Class)i.next(), true));
	   		clt.addAll(domainProps);
	   		domainProperties.put(cls, clt);
	   }
	   return clt;
   }

   public Collection getPropertiesWithRange(RDF_Class cls, boolean transitive)
   {
	   // may be in the cache
	   if (transitive&&rangeProperties.containsKey(cls))
	   		return (Collection)rangeProperties.get(cls);
	   Collection clt = new ArrayList();
	   for (Iterator i = allProperties.iterator(); i.hasNext(); )
	   {
	   		RDF_Property prop = (RDF_Property) i.next();
	   		Collection ranges = prop.getrange();
	   		if (ranges.contains(cls)) clt.add(prop);
	   }
	   if (transitive)
	   {
	   		HashSet rangeProps = new HashSet();
	   		for (Iterator i=getSuperClasses(cls, false, false).iterator(); i.hasNext(); )
	   			rangeProps.addAll(getPropertiesWithRange((RDF_Class)i.next(), true));
	   		clt.addAll(rangeProps);
	   		rangeProperties.put(cls, clt);
	   }
	   return clt;
   }

   // interface for getting information from a property

   public Collection getAllProperties(boolean sorted)
   {
      if (sorted)
      {
      	Collection clt;
      	clt = new TreeSet(new RDF_ResourceComparator());
   		clt.addAll(allProperties);
	   	return clt;
      }else
      	return allProperties;
   }

   public Collection getRangeOfProperty(RDF_Property prop, boolean transitive, boolean sorted)
   {
	   	Collection clt;
	   	if (sorted) clt = new TreeSet(new RDF_ResourceComparator());
	   	else clt = new ArrayList();
	   	for (Iterator i=prop.getrange().iterator(); i.hasNext(); )
   			clt.add(i.next());
   		if (transitive)
   		{
   			HashSet subCls = new HashSet();
   			for (Iterator i=clt.iterator(); i.hasNext(); )
   				subCls.addAll(getSubClasses((RDF_Class)i.next(), true, false));
   			clt.addAll(subCls);
   		}
   		return clt;
   }

   public Collection getDomainOfProperty(RDF_Property prop, boolean transitive, boolean sorted)
   {
      	Collection clt;
      	if (sorted) clt = new TreeSet(new RDF_ResourceComparator());
      	else clt = new ArrayList();
      	for (Iterator i=prop.getdomain().iterator(); i.hasNext(); )
   			clt.add(i.next());
   		if (transitive)
   		{
   			HashSet subCls = new HashSet();
   			for (Iterator i=clt.iterator(); i.hasNext(); )
   				subCls.addAll(getSubClasses((RDF_Class)i.next(), true, false));
   			clt.addAll(subCls);
   		}
   		return clt;
   }

   public Collection getSubProperties(RDF_Property prop, boolean transitive, boolean sorted)
   {
	   Collection clt;
	   if (sorted) clt = new TreeSet(new RDF_ResourceComparator());
	   else clt = new ArrayList();
	   if (transitive&&subProperties.containsKey(prop))
	   {
	   	clt.addAll((Collection)subProperties.get(prop));
	   	return clt;
	   }
	   RDF_DAG dag = validator.getsubPropertyOfDAG();
	   HashSet subURIs=getSubFromDAG(dag, prop);
	   for (Iterator i=subURIs.iterator(); i.hasNext(); )
	   	clt.add(i.next());
                //clt.add(basic_model.getResource((String)i.next()));
	   if (transitive)
	   {
	   	HashSet subProps = new HashSet();
	   	for (Iterator i=clt.iterator(); i.hasNext(); )
	   		subProps.addAll(getSubProperties((RDF_Property)i.next(), true, false));
	   	clt.addAll(subProps);
	   	subProperties.put(prop, clt);
	   }
	   return clt;
   }

   public Collection getSuperProperties(RDF_Property prop, boolean transitive, boolean sorted)
   {
	   Collection clt;
	   if (sorted) clt = new TreeSet(new RDF_ResourceComparator());
	   else clt = new ArrayList();
	   if (transitive&&superProperties.containsKey(prop))
	   {
	   	clt.addAll((Collection)superProperties.get(prop));
	   	return clt;
	   }
	   RDF_DAG dag = validator.getsubPropertyOfDAG();
	   HashSet superURIs=getSuperFromDAG(dag, prop);
	   for (Iterator i=superURIs.iterator(); i.hasNext(); )
	   	clt.add(i.next());
                //clt.add(basic_model.getResource((String)i.next()));
	   if (transitive)
	   {
	   	HashSet superProps = new HashSet();
	   	for (Iterator i=clt.iterator(); i.hasNext(); )
	   		superProps.addAll(getSuperProperties((RDF_Property)i.next(), true, false));
	   	clt.addAll(superProps);
	   	superProperties.put(prop, clt);
	   }
	   return clt;
   }

   public RDF_Class getClassTypeOfProperty(RDF_Property prop)
   {
		RDF_Class meta = null;
		for (Iterator i=prop.gettype().iterator(); i.hasNext(); )
		{
			meta = (RDF_Class)i.next();
			if (isPropertyClass(meta)) break;
		}
		return meta;
   }

   public Collection getRootProperties(boolean sorted)
   {
   		Collection clt;
   		if (sorted) clt = new TreeSet(new RDF_ResourceComparator());
   		else clt = new ArrayList();
   		Collection roots = validator.getsubPropertyOfDAG().getroots();
	   	for (Iterator i=roots.iterator(); i.hasNext(); )
   		{
   			//String uri = (String)i.next();
   			clt.add(i.next());
   		}
   		return clt;
   }

   public boolean isRootProperty(RDF_Property prop)
   {
   		return validator.getsubPropertyOfDAG().getroots().contains(prop);
   }

   public boolean isLeafProperty(RDF_Property prop)
   {
   		RDF_DAG dag = validator.getsubPropertyOfDAG();
   		return getSubFromDAG(dag, prop).isEmpty();
   }

   // interface for getting information from a resource

   public Collection getAllResources(boolean sorted)
   {
	   if (sorted)
	   {
	   	Collection clt;
	   	clt = new TreeSet(new RDF_ResourceComparator());
		clt.addAll(allResources);
		return clt;
	   }else
	   	return allResources;
   }

   public Collection getClassTypes(RDF_Resource resrc, boolean sorted)
   {
	   Collection clt;
	   if (sorted) clt = new TreeSet(new RDF_ResourceComparator());
	   else clt = new ArrayList();
	   Collection types = resrc.gettype();
	   for (Iterator i=types.iterator(); i.hasNext(); )
	   {
	   		Resource r = (Resource)i.next();
			if (allClasses.contains(r)) clt.add(r);
	   }
	   return clt;
   }

   public HashMap getLinksWithSubject(RDF_Resource resrc)
   {
   		// return a hashmap of links and its property type
		HashMap map = new HashMap();
		for (Iterator i=getClassTypes(resrc, false).iterator(); i.hasNext(); )
		{
			RDF_Class cls = (RDF_Class)i.next();
			for (Iterator k=getPropertiesWithDomain(cls, true).iterator(); k.hasNext(); )
			{
				RDF_Property prop = (RDF_Property)k.next();
				for (Iterator l=prop.getlink().iterator(); l.hasNext(); )
				{
					Link link = (Link)l.next();
					if (link.getsubject().equals(resrc))
						map.put(link, prop);
				}
			}
		}
		return map;
	}

	public HashMap getLinksWithObject(RDF_Resource resrc)
	{
		// return a hashmap of links and its property type
		HashMap map = new HashMap();
		for (Iterator i=getClassTypes(resrc, false).iterator(); i.hasNext(); )
		{
			RDF_Class cls = (RDF_Class)i.next();
			for (Iterator k=getPropertiesWithRange(cls, true).iterator(); k.hasNext(); )
			{
				RDF_Property prop = (RDF_Property)k.next();
				for (Iterator l=prop.getlink().iterator(); l.hasNext(); )
				{
					Link link = (Link)l.next();
					if (link.getobject().equals(resrc))
						map.put(link, prop);
				}
			}
		}
		return map;
	}

	// information about namespaces

	private String getURINamespacePrefix(String uri) {
		 int ind = uri.lastIndexOf('#');
		if ( (ind != -1) &&  (ind != (uri.length() - 1)) )
			return uri.substring(0, ++ind) ;
		else return "";
	}


	public boolean isBasicResource(RDF_Resource resrc)
	{
			// return true if resource is in the rdfs, rdf, xmls namespaces
			String uri=resrc.getID();
			return (uri.startsWith(rdfschema.NS)||uri.startsWith(rdf.NS)
					||uri.startsWith(XMLSchema_DT.NS));
	}

	public boolean isAttributeClass(RDF_Class cls)
	{
// 		String uri=cls.getID();
// 		if (uri.equals(rdfschema.Literal)||uri.startsWith(XMLSchema_DT.NS)) return true;
		// use attribute classes for checking
		return attributeClasses.contains(cls);
	}

	private int count = 1; //use for ext namespace

	private void initNamespaces()
	{
			namespaces.put(basic_model.getBaseURI(), "ns");
			if (basic_model.getNamespaces().containsKey(rdfschema.NS))
				namespaces.put(rdfschema.NS, "rdfs");
			if (basic_model.getNamespaces().containsKey(rdf.NS))
				namespaces.put(rdf.NS, "rdf");
			if (basic_model.getNamespaces().containsKey(XMLSchema_DT.NS))
				namespaces.put(XMLSchema_DT.NS, "xmls");
	}

	public String getLabelFor(RDF_Resource resrc)
	{
			if (resrc == null) return "";
			String label = resrc.getID();
			if (resrc instanceof RDF_Class || resrc instanceof RDF_Property)
		{
			String ns = "";
			String prefix = getURINamespacePrefix(label);
			if (!prefix.equals(""))
			{
				if (!namespaces.containsKey(prefix))
				{
					namespaces.put(prefix, "ext"+count);
					count++;
				}
				ns = namespaces.get(prefix)+":";
			}
			int ind = label.lastIndexOf('#');
			if (ind!=-1) label = ns+label.substring(ind+1);
		}else // statement, container or normal resource
		{
			// anonyme resource
			String prefix = getURINamespacePrefix(label);
			String ns = (String)namespaces.get(prefix);
			if (ns!=null)
			{
				int ind = label.lastIndexOf("#");
				label = ns+"#"+label.substring(ind+1);
				if (label.lastIndexOf("genID")!=-1) label += " (anonymous)";
			}
		}
		return label;
   }

   public String getNamespaceInfor()
   {
   		String str = "Namespaces: ";
		for (Iterator i=namespaces.keySet().iterator(); i.hasNext(); )
		{
			String ns = (String)i.next();
			str += namespaces.get(ns)+"=\""+ns+"\"; ";
		}
		return str;
   }
}