package de.uka.ipd.consensus.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;

import org.tm4j.net.Locator;
import org.tm4j.tologx.QueryEvaluator;
import org.tm4j.tologx.QueryEvaluatorFactory;
import org.tm4j.tologx.TologParserException;
import org.tm4j.tologx.TologProcessingException;
import org.tm4j.tologx.TologResultsSet;

import de.uka.ipd.consensus.foundation.logging.LoggingManager;
import de.uka.ipd.consensus.foundation.ontology.OntologyManager;
import de.uka.ipd.consensus.foundation.query.QueryEngine;
import de.uka.ipd.consensus.foundation.query.QueryEngineException;
import de.uka.ipd.consensus.foundation.query.QueryEngineProcessingException;
import de.uka.ipd.consensus.foundation.query.QueryEngineResultSet;
import de.uka.ipd.consensus.foundation.query.QueryEngineSyntaxException;
import de.uka.ipd.consensus.foundation.rights.DynamicRightsManager;
import de.uka.ipd.consensus.foundation.rights.DynamicRightsManagerException;
import de.uka.ipd.consensus.foundation.schema.Association;
import de.uka.ipd.consensus.foundation.schema.AssociationRole;
import de.uka.ipd.consensus.foundation.schema.Attribute;
import de.uka.ipd.consensus.foundation.schema.Concept;
import de.uka.ipd.consensus.foundation.schema.Topic;
import de.uka.ipd.consensus.foundation.schema.User;

/**
 * 
 * TODO
 * @author Thomas Much
 * @version 2006-06-14
 */
public class QueryEngineImpl implements QueryEngine {

	private OntologyManagerImpl omi;


	public QueryEngineImpl(LoggingManager logging, OntologyManager om) {
		// alternativ Konstruktor mit Properties als zusätzlichem 1. Parameter, oder nur mit OntologyManager, oder ganz ohne Parameter
		this.omi     = (OntologyManagerImpl)om;

//		StringBuffer exclude = new StringBuffer();
//		Iterator it = omi.getInternalTopics().iterator();
//		while (it.hasNext()) {
//			org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();
//			exclude.append( ",not(direct-instance-of($TOPIC," );
//			exclude.append( t.getID() );
//			exclude.append( "))" );
//		}
//		this.EXCLUDE_INTERNAL = exclude.toString();
	}


	public QueryEngineResultSet executeQuery(User user, String query) throws QueryEngineException,DynamicRightsManagerException {
		if (query == null) {
			query = "?";
		}
		else {
			query = query.trim();
			if (!query.endsWith("?")) {
				query += "?";
			}
		}

		DynamicRightsManager drm = omi.getDynamicRightsManager();
		if (drm != null) {
			drm.checkQuery( user, query );
		}

		Object txa = null;
		try {
			txa = omi.openTransaction(user);
			QueryEvaluator qe = QueryEvaluatorFactory.newQueryEvaluator( omi.getTopicMap() );
			TologResultsSet rs = qe.execute( query );
			return new QueryEngineResultSetImpl(omi,rs);
		}
		catch (TologParserException e) {
			throw new QueryEngineSyntaxException(e);
		}
		catch (TologProcessingException e) {
			throw new QueryEngineProcessingException(e);
		}
		catch (Exception e) {
			throw new QueryEngineException(e);
		}
		finally {
			omi.abortTransaction( user, txa );
		}
	}


	public Topic getTopicById(User user, String id) throws QueryEngineException {
		if ((id == null) || (id.length() == 0)) {
			return null;
		}

		Object obj = TM4JObjectWrapper.getCache( id );
		if (obj == null) {
			obj = TM4JObjectWrapper.wrap( omi, omi.getTopicMap().getTopicByID( id ) );
		}

		if (obj instanceof Topic) {
			DynamicRightsManager drm = omi.getDynamicRightsManager();
			if ((drm == null) || drm.mayViewTopic( user, (Topic)obj )) {
				return (Topic)obj;
			}
		}
		
		return null;
	}


	public Association getAssociationById(User user, String id) throws QueryEngineException {
		if ((id == null) || (id.length() == 0)) {
			return null;
		}

		Object obj = TM4JObjectWrapper.getCache( id );
		if (obj == null) {
			obj = TM4JObjectWrapper.wrap( omi, omi.getTopicMap().getObjectByID( id ) );
		}
		
		if (obj instanceof Association) {
			DynamicRightsManager drm = omi.getDynamicRightsManager();
			if ((drm == null) || drm.mayViewAssociation( user, (Association)obj )) {
				return (Association)obj;
			}
		}
		
		return null;
	}


	public Concept getConceptById(User user, String id) throws QueryEngineException {
		if ((id == null) || (id.length() == 0)) {
			return null;
		}
		
		Object obj = TM4JObjectWrapper.getCache( id );
		if (obj == null) {
			obj = TM4JObjectWrapper.wrap( omi, omi.getTopicMap().getObjectByID( id ) );
		}

		if (obj instanceof Concept) {
			DynamicRightsManager drm = omi.getDynamicRightsManager();
			if (drm != null) {
				if (obj instanceof Topic) {
					if (!drm.mayViewTopic( user, (Topic)obj )) return null;
				}
				else if (obj instanceof Association) {
					if (!drm.mayViewAssociation( user, (Association)obj )) return null;
				}
				else if (obj instanceof Attribute) {
					if (!drm.mayViewAttribute( user, (Attribute)obj )) return null;
				}
			}
			return (Concept)obj;
		}
		
		return null;
	}


	public Collection getTopics(User user) throws QueryEngineException {
		// TODO: Parameter limit und offset ("Topics 101-200 von 1000" usw.)? machbar (wg. Sortierung)?
		DynamicRightsManager drm = omi.getDynamicRightsManager();

		ArrayList list = new ArrayList(50);
		
		Iterator it = omi.getTopicMap().getTopicsIterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();

			if (!omi.isInternalTopicType(t)) {
				if (omi.hasTopicName( t )) {
					Object obj = TM4JObjectWrapper.wrap( omi, t );
					if ((obj instanceof Topic) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
						list.add( obj );
					}
				}
			}
		}
		
		Collections.sort( list );

		return Collections.unmodifiableCollection( list );

//		query_all_topics        = qe.prepareQuery("select $TOPIC,$NAME from topic($TOPIC)" + EXCLUDE_INTERNAL + ",topic-name($TOPIC,$NAME) order by $NAME asc ?");
//			txa = omi.openTransaction(user);
//			TologResultsSet rs = query_all_topics.execute();
//			return TM4JTologResultWrapperSet.getInstance( omi, rs );
	}


	private Collection getTopics(User user, boolean isclass) {
		DynamicRightsManager drm = omi.getDynamicRightsManager();

		ArrayList list = new ArrayList(50);
		
		Iterator it = omi.getTopicMap().getTopicsIterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();

			if (!omi.isInternalTopicType(t)) {
				if (omi.hasTopicName( t )) {
					Object obj = TM4JObjectWrapper.wrap( omi, t );
					if ((obj instanceof Topic) && (((Topic)obj).isClassTopic() == isclass) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
						list.add( obj );
					}
				}
			}
		}
		
		Collections.sort( list );

		return Collections.unmodifiableCollection( list );
	}
	
	
	public Collection getClassTopics(User user) throws QueryEngineException {
		return this.getTopics( user, true );
	}
	
	
	public Collection getInstanceTopics(User user) throws QueryEngineException {
		return this.getTopics( user, false );
	}


	public Collection getAssociations(User user, Topic topic) throws QueryEngineException {
		if (topic == null) {
			return Collections.EMPTY_SET;
		}

		Collection roles = ((TopicImpl)topic).getWrappedTopic().getRolesPlayed();

		if (roles == null) {
			return Collections.EMPTY_SET;
		}
		
		DynamicRightsManager drm = omi.getDynamicRightsManager();

		ArrayList list = new ArrayList(20);
		
		Iterator it = roles.iterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Member      m = (org.tm4j.topicmap.Member)it.next();
			org.tm4j.topicmap.Association a = m.getParent();
			
			if (a != null) {
				org.tm4j.topicmap.Topic t = a.getType();

				if ((t == null) || (!omi.isInternalTopicType(t))) {
					Association assoc = TM4JObjectWrapper.wrap( omi, a );
					if ((drm == null) || drm.mayViewAssociation( user, assoc )) {
						if (!list.contains( assoc )) {
							list.add( assoc );
						}
					}
				}
			}
		}

		Collections.sort( list );

		return Collections.unmodifiableCollection( list );

//			final String query = "select $A from association($A),association-role($A,$R),role-player($R," + topic.getId() + "),"
//								+ "{ not(type($A,$ANT)) | type($A,$AT),not(direct-instance-of($AT," + omi.getInternalTopicType().getID() + ")) } ?";
//			txa = omi.openTransaction(user);
//			TologResultsSet rs = qe.execute( query );
//			return TM4JTologResultWrapperSet.getInstance( omi, rs );
	}
	
	
	public Collection getAssociationsOfType(User user, Topic type) throws QueryEngineException {
		org.tm4j.topicmap.Topic tmType;
		if (type != null) {
			tmType = ((TopicImpl)type).getWrappedTopic();
		}
		else {
			tmType = null;
		}

		DynamicRightsManager drm = omi.getDynamicRightsManager();

		ArrayList list = new ArrayList(50);
		
		Iterator it = omi.getTopicMap().getAssociationsIterator();
		
		if (tmType == null) {
			while (it.hasNext()) {
				org.tm4j.topicmap.Association a = (org.tm4j.topicmap.Association)it.next();

				if (a.getType() == null) {
					Association assoc = TM4JObjectWrapper.wrap( omi, a );

					if ((drm == null) || drm.mayViewAssociation( user, assoc )) {
						list.add( assoc );
					}
				}
			}
		}
		else {
			while (it.hasNext()) {
				org.tm4j.topicmap.Association a = (org.tm4j.topicmap.Association)it.next();

				if (tmType.equals( a.getType() )) {
					Association assoc = TM4JObjectWrapper.wrap( omi, a );

					if ((drm == null) || drm.mayViewAssociation( user, assoc )) {
						list.add( assoc );
					}
				}
			}
			
		}
		
		Collections.sort( list );

		return Collections.unmodifiableCollection( list );
		
//		Collection all = omi.getTopicMap().getAssociations();
//		Collection ofType = omi.getTopicMap().getUtils().getAssociationsOfType( tmType, all );
//		return TM4JCollectionWrapperSet.getInstance( omi, ofType );
	}


	public Collection getAssociationRoles(User user, Association assoc) throws QueryEngineException {
		if (assoc == null) {
			return Collections.EMPTY_SET;
		}
		
		Collection members = ((AssociationImpl)assoc).getWrappedAssociation().getMembers();
		
		if (members == null) {
			return Collections.EMPTY_SET;
		}

		DynamicRightsManager drm = omi.getDynamicRightsManager();

		ArrayList list = new ArrayList(10);
		
		Iterator it = members.iterator();

		while (it.hasNext()) {
			org.tm4j.topicmap.Member m = (org.tm4j.topicmap.Member)it.next();
			org.tm4j.topicmap.Topic  t = m.getRoleSpec();

			if (t == null) {
				AssociationRole role = TM4JObjectWrapper.wrap( omi, m );
				list.add( role );
			}
			else if (!omi.isInternalTopicType(t)) {
				if (drm == null) {
					AssociationRole role = TM4JObjectWrapper.wrap( omi, m );
					list.add( role );
				}
				else {
					Object obj = TM4JObjectWrapper.wrap( omi, t );

					if ((obj instanceof Topic) && (drm.mayViewTopic( user, (Topic)obj ))) {
						AssociationRole role = TM4JObjectWrapper.wrap( omi, m );
						list.add( role );
					}
				}
			}
		}

		Collections.sort( list );

		return Collections.unmodifiableCollection( list );

//			final String query = "select $R from association-role(" + assoc.getId() + ",$R),"
//								+ "{ type($R,$T),not(direct-instance-of($T," + omi.getInternalTopicType().getID() + ")) | not(type($R,$T)) } ?";
//			txa = omi.openTransaction(user);
//			TologResultsSet rs = qe.execute( query );
//			return TM4JTologResultWrapperSet.getInstance( omi, rs );
	}


	public Collection getTopicsByBeginning(User user, String beginning) throws QueryEngineException {
		if ((beginning == null) || (beginning.length() == 0)) {
			return this.getTopics(user);
		}
		
		DynamicRightsManager drm = omi.getDynamicRightsManager();
		
		beginning = beginning.toUpperCase();
		
		ArrayList list = new ArrayList(50);
		
		Iterator it = omi.getTopicMap().getTopicsIterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();

			if (!omi.isInternalTopicType(t)) {
				if (omi.getTopicName(t).toUpperCase().startsWith(beginning)) {
					Object obj = TM4JObjectWrapper.wrap( omi, t );
					if ((obj instanceof Topic) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
						list.add( obj );
					}
				}
			}
		}

		Collections.sort( list );
		
		return Collections.unmodifiableCollection( list );
	}
	
	
	private Collection getTopicsByBeginning(User user, String beginning, boolean isclass) {
		if ((beginning == null) || (beginning.length() == 0)) {
			return this.getTopics( user, isclass );
		}
		
		DynamicRightsManager drm = omi.getDynamicRightsManager();
		
		beginning = beginning.toUpperCase();
		
		ArrayList list = new ArrayList(50);
		
		Iterator it = omi.getTopicMap().getTopicsIterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();

			if (!omi.isInternalTopicType(t)) {
				if (omi.getTopicName(t).toUpperCase().startsWith(beginning)) {
					Object obj = TM4JObjectWrapper.wrap( omi, t );
					if ((obj instanceof Topic) && (((Topic)obj).isClassTopic() == isclass) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
						list.add( obj );
					}
				}
			}
		}

		Collections.sort( list );
		
		return Collections.unmodifiableCollection( list );
	}
	
	
	public Collection getClassTopicsByBeginning(User user, String beginning) throws QueryEngineException {
		return this.getTopicsByBeginning( user, beginning, true );
	}
	
	
	public Collection getInstanceTopicsByBeginning(User user, String beginning) throws QueryEngineException {
		return this.getTopicsByBeginning( user, beginning, false );
	}
	
	
	public Collection getTopicsBySubstring(User user, String substring, boolean searchComment) throws QueryEngineException {
		if ((substring == null) || (substring.length() == 0)) {
			return getTopics(user);
		}

		DynamicRightsManager drm = omi.getDynamicRightsManager();
		
		substring = substring.toUpperCase();
		
		ArrayList list = new ArrayList(50);
		
		Iterator it = omi.getTopicMap().getTopicsIterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();

			if (!omi.isInternalTopicType(t)) {
				if (omi.getTopicName(t).toUpperCase().indexOf(substring) >= 0) {
					Object obj = TM4JObjectWrapper.wrap( omi, t );
					if ((obj instanceof Topic) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
						list.add( obj );
					}
				}
				else if (searchComment) {
					Object obj = TM4JObjectWrapper.wrap( omi, t );
					if ((obj instanceof Topic) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
						if (((Topic)obj).getComment().toUpperCase().indexOf(substring) >= 0) {
							list.add( obj );
						}
					}
				}
			}
		}

		Collections.sort( list );
		
		return Collections.unmodifiableCollection( list );
	}
	
	
	private Collection getTopicsBySubstring(User user, String substring, boolean searchComment, boolean isclass) {
		if ((substring == null) || (substring.length() == 0)) {
			return getTopics( user, isclass );
		}

		DynamicRightsManager drm = omi.getDynamicRightsManager();
		
		substring = substring.toUpperCase();
		
		ArrayList list = new ArrayList(50);
		
		Iterator it = omi.getTopicMap().getTopicsIterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();

			if (!omi.isInternalTopicType(t)) {
				if (omi.getTopicName(t).toUpperCase().indexOf(substring) >= 0) {
					Object obj = TM4JObjectWrapper.wrap( omi, t );
					if ((obj instanceof Topic) && (((Topic)obj).isClassTopic() == isclass) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
						list.add( obj );
					}
				}
				else if (searchComment) {
					Object obj = TM4JObjectWrapper.wrap( omi, t );
					if ((obj instanceof Topic) && (((Topic)obj).isClassTopic() == isclass) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
						if (((Topic)obj).getComment().toUpperCase().indexOf(substring) >= 0) {
							list.add( obj );
						}
					}
				}
			}
		}

		Collections.sort( list );
		
		return Collections.unmodifiableCollection( list );
	}


	public Collection getClassTopicsBySubstring(User user, String substring, boolean searchComment) throws QueryEngineException {
		return this.getTopicsBySubstring( user, substring, searchComment, true );
	}


	public Collection getInstanceTopicsBySubstring(User user, String substring, boolean searchComment) throws QueryEngineException {
		return this.getTopicsBySubstring( user, substring, searchComment, false );
	}


	private Collection getClasses(User user, Topic topic, boolean retSuper, boolean retTypeInstance) throws QueryEngineException {
		if (topic == null) {
			return Collections.EMPTY_SET;
		}
		
		Locator subPsi,superPsi;
		
		if (retSuper) {
			subPsi   = omi.getSubclassPSI();
			superPsi = omi.getSuperclassPSI();
		}
		else {
			subPsi   = omi.getSuperclassPSI();
			superPsi = omi.getSubclassPSI();
		}

		Collection subclassMembers = omi.getTopicMap().getUtils().getMembersOfType( ((TopicImpl)topic).getWrappedTopic(), subPsi );

		if ((subclassMembers == null) || subclassMembers.isEmpty()) {
			return Collections.EMPTY_SET;
		}
		
		DynamicRightsManager drm = omi.getDynamicRightsManager();

		ArrayList list = new ArrayList( retSuper ? 10 : 50 );
		
		Iterator it = subclassMembers.iterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Member subclassMember = (org.tm4j.topicmap.Member)it.next();

			org.tm4j.topicmap.Association tmAssoc = subclassMember.getParent();

			Association assoc = TM4JObjectWrapper.wrap( omi, tmAssoc );

			Collection superclassMembers = omi.getTopicMap().getUtils().getMembersOfType( tmAssoc, superPsi );
			
			Iterator its = superclassMembers.iterator();

			while (its.hasNext()) {
				org.tm4j.topicmap.Member superclassMember = (org.tm4j.topicmap.Member)its.next();
				
				Collection players = superclassMember.getPlayers();

				if (players != null) {
					Iterator itp = players.iterator();
					
					while (itp.hasNext()) {
						org.tm4j.topicmap.Topic tmTopic = (org.tm4j.topicmap.Topic)itp.next();

						Object obj = TM4JObjectWrapper.wrap( omi, tmTopic );
						
						if ((obj instanceof Topic) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
							Topic base,other;

							if (retSuper) {
								base  = topic;
								other = (Topic)obj;
							}
							else {
								base  = (Topic)obj;
								other = topic;
							}

							if (retTypeInstance) {
								obj = new TypeInstanceImpl( base, other, assoc );
							}
							else {
								obj = new SuperSubclassImpl( base, other, assoc );
							}

							list.add( obj );
						}
					}
				}
			}
		}
		
		Collections.sort( list );

		return Collections.unmodifiableCollection( list );
	}
	
	
	private Collection getTypeInstances(User user, Topic topic, boolean retSuper) throws QueryEngineException {
		if (topic == null) {
			return Collections.EMPTY_SET;
		}
		
		Locator subPsi,superPsi;
		
		if (retSuper) {
			subPsi   = omi.getInstancePSI();
			superPsi = omi.getClassPSI();
		}
		else {
			subPsi   = omi.getClassPSI();
			superPsi = omi.getInstancePSI();
		}

		Collection subclassMembers = omi.getTopicMap().getUtils().getMembersOfType( ((TopicImpl)topic).getWrappedTopic(), subPsi );

		if ((subclassMembers == null) || subclassMembers.isEmpty()) {
			return Collections.EMPTY_SET;
		}
		
		DynamicRightsManager drm = omi.getDynamicRightsManager();

		ArrayList list = new ArrayList( retSuper ? 10 : 50 );
		
		Iterator it = subclassMembers.iterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Member subclassMember = (org.tm4j.topicmap.Member)it.next();

			org.tm4j.topicmap.Association tmAssoc = subclassMember.getParent();

			Association assoc = TM4JObjectWrapper.wrap( omi, tmAssoc );

			Collection superclassMembers = omi.getTopicMap().getUtils().getMembersOfType( tmAssoc, superPsi );
			
			Iterator its = superclassMembers.iterator();

			while (its.hasNext()) {
				org.tm4j.topicmap.Member superclassMember = (org.tm4j.topicmap.Member)its.next();
				
				Collection players = superclassMember.getPlayers();

				if (players != null) {
					Iterator itp = players.iterator();
					
					while (itp.hasNext()) {
						org.tm4j.topicmap.Topic tmTopic = (org.tm4j.topicmap.Topic)itp.next();

						Object obj = TM4JObjectWrapper.wrap( omi, tmTopic );
						
						if ((obj instanceof Topic) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
							if (retSuper) {
								obj = new TypeInstanceImpl( topic, (Topic)obj, assoc );
							}
							else {
								obj = new TypeInstanceImpl( (Topic)obj, topic, assoc );
							}
							list.add( obj );
						}
					}
				}
			}
		}
		
		Collections.sort( list );

		return Collections.unmodifiableCollection( list );
	}

	
	public Collection getTypes(User user, Topic topic) throws QueryEngineException {
		if (omi.useClasses()) {
			return this.getTypeInstances( user, topic, true );
		}
		else {
			return this.getClasses( user, topic, true, true );
		}
	}


	public Collection getInstances(User user, Topic topic) throws QueryEngineException {
		if (omi.useClasses()) {
			return this.getTypeInstances( user, topic, false );
		}
		else {
			return this.getClasses( user, topic, false, true );
		}
//		if (topic == null) {
//			return Collections.EMPTY_SET;
//		}
//		DynamicRightsManager drm = omi.getDynamicRightsManager();
//		ArrayList list = new ArrayList(50);
//		Iterator it = omi.getTopicMap().getTopicsIterator();
//		org.tm4j.topicmap.Topic tmTopic = ((TopicImpl)topic).getWrappedTopic();
//		while (it.hasNext()) {
//			org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();
//			
//			if (t.isOfType( tmTopic ) && !omi.isInternalTopicType(t)) {
//				if (omi.hasTopicName( t )) {
//					Object obj = TM4JObjectWrapper.wrap( omi, t );
//					if ((obj instanceof Topic) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
//						list.add( obj );
//					}
//				}
//			}
//		}
//		Collections.sort( list );
//		return Collections.unmodifiableCollection( list );

//		Collection subcls = omi.getTopicMap().getUtils().getSubClasses( ((TopicImpl)topic).getWrappedTopic() );
//		
//		Iterator it = subcls.iterator();
//		
//		while (it.hasNext()) {
//			org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();
//
//			if (!omi.isInternalTopicType(t)) {
//				if (omi.hasTopicName( t )) {
//					Object obj = TM4JObjectWrapper.wrap( omi, t );
//					if ((obj instanceof Topic) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
//						list.add( obj );
//					}
//				}
//			}
//		}

//			final String query = "select $TOPIC,$NAME from direct-instance-of($TOPIC," + topic.getId() + "),topic-name($TOPIC,$NAME)" + EXCLUDE_INTERNAL + " order by $NAME asc ?";
//			txa = omi.openTransaction(user);
//			TologResultsSet rs = qe.execute( query );
//			return TM4JTologResultWrapperSet.getInstance( omi, rs );
	}


	private Collection getRoots(User user, Locator psi, boolean isclass) throws QueryEngineException {
		DynamicRightsManager drm = omi.getDynamicRightsManager();

		ArrayList list = new ArrayList(50);
		
		Iterator it = omi.getTopicMap().getTopicsIterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();

			if (!omi.isInternalTopicType(t) && omi.hasTopicName(t)) {
				Collection subclassMembers = omi.getTopicMap().getUtils().getMembersOfType( t, psi );

				if ((subclassMembers == null) || subclassMembers.isEmpty()) {
					Object obj = TM4JObjectWrapper.wrap( omi, t );
					if ((obj instanceof Topic) && (((Topic)obj).isClassTopic() == isclass) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
						list.add( obj );
					}
				}
			}
		}
		
		Collections.sort( list );
		
		return Collections.unmodifiableCollection( list );
	}



	public Collection getInstanceRoots(User user) throws QueryEngineException {
		if (omi.useClasses()) {
			return this.getRoots( user, omi.getInstancePSI(), false );
		}
		else {
			DynamicRightsManager drm = omi.getDynamicRightsManager();

			ArrayList list = new ArrayList(50);
			
			Iterator it = omi.getTopicMap().getTopicsIterator();
			
			while (it.hasNext()) {
				org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();

				if (!omi.isInternalTopicType(t) && omi.hasTopicName(t)) {
//					Collection types = t.getTypes();
//					if ((types == null) || (types.size() == 0)) {
					
					Collection subclassMembers = omi.getTopicMap().getUtils().getMembersOfType( t, omi.getSubclassPSI() );

					if ((subclassMembers == null) || subclassMembers.isEmpty()) {
						Object obj = TM4JObjectWrapper.wrap( omi, t );
						if ((obj instanceof Topic) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
							list.add( obj );
						}
					}
				}
			}
			
			Collections.sort( list );
			
			return Collections.unmodifiableCollection( list );

//			query_root_instances    = qe.prepareQuery("select $TOPIC,$NAME from topic($TOPIC),not(instance-of($TOPIC,$A)),topic-name($TOPIC,$NAME) order by $NAME asc ?");
//				txa = omi.openTransaction(user);
//				TologResultsSet rs = query_root_instances.execute();
//				return TM4JTologResultWrapperSet.getInstance( omi, rs );
		}
	}


	public Collection getSuperclasses(User user, Topic topic) throws QueryEngineException {
		if (omi.useClasses()) {
			return this.getClasses( user, topic, true, false );
		}
		else {
			return Collections.EMPTY_SET;
		}
	}


	public Collection getSubclasses(User user, Topic topic) throws QueryEngineException {
		if (omi.useClasses()) {
			return this.getClasses( user, topic, false, false );
		}
		else {
			return Collections.EMPTY_SET;
		}
	}


	public Collection getClassRoots(User user) throws QueryEngineException {
		if (omi.useClasses()) {
			return this.getRoots( user, omi.getSubclassPSI(), true );
		}
		else {
			return Collections.EMPTY_SET;
		}
	}


	public int getTopicCount(User user) {
		DynamicRightsManager drm = omi.getDynamicRightsManager();

		int count = 0;

		Iterator it = omi.getTopicMap().getTopicsIterator();

		while (it.hasNext()) {
			org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();

			if (!omi.isInternalTopicType(t)) {
				if (drm != null) {
					Object obj = TM4JObjectWrapper.wrap( omi, t );
					if ((obj instanceof Topic) && drm.mayViewTopic( user, (Topic)obj )) {
						count++;
					}
				}
				else {
					count++;
				}
			}
		}
		
		return count;

// return this.omi.getTopicMap().getTopicCount();

//		txa = omi.openTransaction(user);
//		TologResultsSet rs = qe.execute("select count($TOPIC) from topic($TOPIC)" + EXCLUDE_INTERNAL + " ?");
//		if (rs.getNumRows() > 0) {
//			Number count = (Number)rs.getRow(0).get(0);
//			return count.intValue();
//		}
	}
	
	
	private int getTopicCount(User user, boolean isclass) {
		DynamicRightsManager drm = omi.getDynamicRightsManager();

		int count = 0;

		Iterator it = omi.getTopicMap().getTopicsIterator();

		while (it.hasNext()) {
			org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();

			if (!omi.isInternalTopicType(t)) {
				Object obj = TM4JObjectWrapper.wrap( omi, t );
				if ((obj instanceof Topic) && (((Topic)obj).isClassTopic() == isclass) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
					count++;
				}
			}
		}
		
		return count;
	}


	public int getClassTopicCount(User user) {
		return this.getTopicCount( user, true );
	}
	
	
	public int getInstanceTopicCount(User user) {
		return this.getTopicCount( user, false );
	}


	public int getAssociationCount(User user) {
		DynamicRightsManager drm = omi.getDynamicRightsManager();

		int count = 0;
		
		Iterator it = omi.getTopicMap().getAssociationsIterator();

		while (it.hasNext()) {
			org.tm4j.topicmap.Association a = (org.tm4j.topicmap.Association)it.next();
			org.tm4j.topicmap.Topic       t = a.getType();
			
			if ((t == null) || (!omi.isInternalTopicType(t))) {
				if (drm != null) {
					Association assoc = TM4JObjectWrapper.wrap( omi, a );
					if (drm.mayViewAssociation( user, assoc )) {
						count++;
					}
				}
				else {
					count++;
				}
			}
		}
		
		return count;

//		return this.omi.getTopicMap().getAssociations().size();

//			final String queryTypedAssoc   = "select count($ASSOCIATION) from association($ASSOCIATION),type($ASSOCIATION,$TYPE),not(direct-instance-of($TYPE," + this.omi.getInternalTopicType().getID() + ")) ?";
//			final String queryUntypedAssoc = "select count($ASSOCIATION) from association($ASSOCIATION),not(type($ASSOCIATION,$TYPE)) ?";
//			txa = omi.openTransaction( user );
//			TologResultsSet rs = qe.execute( queryTypedAssoc );
//			if (rs.getNumRows() > 0) {
//				Number num = (Number)rs.getRow(0).get(0);
//				count = num.intValue();
//			}
//			rs = qe.execute( queryUntypedAssoc );
//			if (rs.getNumRows() > 0) {
//				Number num = (Number)rs.getRow(0).get(0);
//				count += num.intValue();
//			}
	}


	public String getVersion() {
		return "2006-06-14 tm (" + this.getClass().getName() + ")";
	}


	public String getQuerySyntaxName() {
		return "Tolog";
	}

	
	public String getQuerySyntaxVersion() {
		return "1.0";
	}


	public String getQuerySyntaxURL() {
		return "http://www.ontopia.net/omnigator/docs/query/tutorial.html";
	}
}
