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.topicmap.DuplicateObjectIDException;

import de.uka.ipd.consensus.foundation.ontology.OntologyManagerClassException;
import de.uka.ipd.consensus.foundation.ontology.OntologyManagerDuplicateIdException;
import de.uka.ipd.consensus.foundation.ontology.OntologyManagerException;
import de.uka.ipd.consensus.foundation.query.QueryEngineException;
import de.uka.ipd.consensus.foundation.rating.RatingManager;
import de.uka.ipd.consensus.foundation.rating.RatingManagerException;
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.Attribute;
import de.uka.ipd.consensus.foundation.schema.Occurrence;
import de.uka.ipd.consensus.foundation.schema.Rating;
import de.uka.ipd.consensus.foundation.schema.SuperSubclass;
import de.uka.ipd.consensus.foundation.schema.Synonym;
import de.uka.ipd.consensus.foundation.schema.Topic;
import de.uka.ipd.consensus.foundation.schema.TypeInstance;
import de.uka.ipd.consensus.foundation.schema.User;

/**
 * 
 * TODO
 * @author Thomas Much
 * @version 2006-06-14
 */
public class TopicImpl extends TM4JTopicWrapper implements Topic {
	
	private static final String FLAG_CLASS = "class";


	protected TopicImpl(OntologyManagerImpl omi, org.tm4j.topicmap.Topic tmTopic) {
		super(omi,tmTopic);
	}


	public Collection getInstances(User user) {
		try {
			return this.getOntologyManager().getQueryEngine().getInstances( user, this );
		}
		catch (QueryEngineException e) {
			return Collections.EMPTY_LIST;
		}
	}


	public Collection getTypes(User user) {
		try {
			return this.getOntologyManager().getQueryEngine().getTypes( user, this );
		}
		catch (QueryEngineException e) {
			return Collections.EMPTY_LIST;
		}
//		Collection c = this.getWrappedTopic().getTypes();
//		return TM4JCollectionWrapperSet.getInstance( this.getOntologyManager(), c );
	}


	public TypeInstance getType(User user, String id) {
		Collection types = this.getTypes( user );
		Iterator it = types.iterator();
		while (it.hasNext()) {
			TypeInstance type = (TypeInstance)it.next();
			if (type.getId().equals( id )) {
				return type;
			}
		}
		return null;
	}


	public TypeInstance addType(User adder, Topic type) throws OntologyManagerException,DynamicRightsManagerException {
		if (type == null) {
			throw new OntologyManagerException("Cannot add null as a type.");
		}

		if (this.equals( type )) {
			throw new OntologyManagerException("Cannot add topic as its own type.");
		}

		if (this.getOntologyManager().useClasses()) {
			if (this.isClassTopic()) {
				throw new OntologyManagerClassException("Cannot add type to class " + this );
			}
			if (!type.isClassTopic()) {
				throw new OntologyManagerClassException("Cannot add instance " + type + " as a type");
			}
		}

		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		if (drm != null) {
			drm.checkAddType( adder, this );
		}
		
		Association assoc;
		
		if (this.getOntologyManager().useClasses()) {
			assoc = this.getOntologyManager().addInstanceType( adder, this, type, true );
		}
		else {
			assoc = this.getOntologyManager().addSuperclass( adder, this, type, true );
		}

		return new TypeInstanceImpl( this, type, assoc );
//		this.getWrappedTopic().addType( ((TopicImpl)type).getWrappedTopic() );
	}


	public void removeType(User remover, Topic type) throws OntologyManagerException,DynamicRightsManagerException {
		if (type == null) return;

		org.tm4j.topicmap.Association tmAssoc;
		
		if (this.getOntologyManager().useClasses()) {
			tmAssoc = this.getOntologyManager().getTypeAssociation( this.getWrappedTopic(), ((TopicImpl)type).getWrappedTopic() );
		}
		else {
			tmAssoc = this.getOntologyManager().getSuperclassAssociation( this.getWrappedTopic(), ((TopicImpl)type).getWrappedTopic() );
		}

		if (tmAssoc == null) return;

		Association assoc = TM4JObjectWrapper.wrap( this.getOntologyManager(), tmAssoc );

		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		if (drm != null) {
			TypeInstance typeinstance = new TypeInstanceImpl( this, type, assoc );
			drm.checkRemoveType( remover, typeinstance );
		}

		try {
			RatingManager rm = this.getOntologyManager().getRatingManager();
			if (rm != null) {
				rm.deleteRatings( assoc );
			}
			
			tmAssoc.setMembers( null );
			tmAssoc.dispose();
			tmAssoc.destroy();

			this.incVersion();
			this.getOntologyManager().fireConceptTypeChanged( this, remover, type );
		}
		catch (Exception e) {
			throw new OntologyManagerException( e );
		}
	}


	public boolean isOfType(Topic type) {
		if (!this.getOntologyManager().useClasses() || this.isClassTopic()) {
			if (type == null) {
				Collection subclassMembers = this.getOntologyManager().getTopicMap().getUtils().getMembersOfType( this.getWrappedTopic(), this.getOntologyManager().getSubclassPSI() );
				return ((subclassMembers == null) || subclassMembers.isEmpty());
			}
			else {
				org.tm4j.topicmap.Association tmAssoc = this.getOntologyManager().getSuperclassAssociation( this.getWrappedTopic(), ((TopicImpl)type).getWrappedTopic() );
				return (tmAssoc != null);
			}
		}
		else {
			if (type == null) {
				Collection instanceMembers = this.getOntologyManager().getTopicMap().getUtils().getMembersOfType( this.getWrappedTopic(), this.getOntologyManager().getInstancePSI() );
				return ((instanceMembers == null) || instanceMembers.isEmpty());
			}
			else {
				org.tm4j.topicmap.Association tmAssoc = this.getOntologyManager().getTypeAssociation( this.getWrappedTopic(), ((TopicImpl)type).getWrappedTopic() );
				return (tmAssoc != null);
			}
		}
//		org.tm4j.topicmap.Topic tmType;
//		if (type == null) {
//			tmType = null;
//		}
//		else {
//			tmType = ((TopicImpl)type).getWrappedTopic();
//		}
//		return this.getWrappedTopic().isOfType( tmType );
	}


	public Collection getAssociations(User user) {
		try {
			return this.getOntologyManager().getQueryEngine().getAssociations( user, this );
		}
		catch (QueryEngineException e) {
			this.getLoggingManager().error("Error getting topic associations: " + e);
			return Collections.EMPTY_SET;
		}
//		Collection c = this.getOntologyManager().getTopicMap().getUtils().getAssociations( this.getWrappedTopic() );
//		return TM4JCollectionWrapperSet.getInstance( this.getOntologyManager(), c );
	}


	public Rating addRating(User rater, double value) throws RatingManagerException,DynamicRightsManagerException {
		Rating r = this.getRating( rater );
		if (r != null) {
			r.setValue( value );
		}
		else {
			RatingManager rm = this.getOntologyManager().getRatingManager();
			if (rm != null) {
				r = rm.createRating( rater, value );
				rm.addRating( this, r );
			}
		}
		return r;
	}


	public Collection getRatings() {
		if (this.getOntologyManager().getRatingManager() != null) {
			return this.getOntologyManager().getRatingManager().getRatings( this );
		}
		else {
			return Collections.EMPTY_SET;
		}
	}


	public double getAverageRatingValue() {
		if (this.getOntologyManager().getRatingManager() != null) {
			return this.getOntologyManager().getRatingManager().getAverageRatingValue( this );
		}
		else {
			return Rating.NEUTRAL;
		}
	}


	public int getRatingsCount() {
		if (this.getOntologyManager().getRatingManager() != null) {
			return this.getOntologyManager().getRatingManager().getRatingsCount( this );
		}
		else {
			return 0;
		}
	}


	public Rating getRating(User user) {
		if (this.getOntologyManager().getRatingManager() != null) {
			return this.getOntologyManager().getRatingManager().getRating( this, user );
		}
		else {
			return null;
		}
	}

	
	public void addAttribute(User setter, Attribute attr) throws OntologyManagerException,DynamicRightsManagerException {
		if (attr == null) return;

		if (attr.getAttributeable() != null) {
			throw new OntologyManagerException( attr + " is already assigned to a topic.");
		}
		
		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		if (drm != null) {
			drm.checkAddAttribute( setter, attr, this );
		}
		
		this.getOntologyManager().addAttribute( setter, this, attr );
		this.incVersion();
	}

	
	public void removeAttribute(User remover, Attribute attr) throws OntologyManagerException,DynamicRightsManagerException {
		if (attr == null) return;

		if (!this.equals( attr.getAttributeable() )) return;

		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		if (drm != null) {
			drm.checkRemoveAttribute( remover, attr );
		}
		
		this.getOntologyManager().removeAttribute( remover, this, attr );
		this.incVersion();
	}


	public Attribute getAttribute(User getter, String id) {
		if ((id == null) || (id.length() == 0)) return null;
		
		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		
		org.tm4j.topicmap.TopicMapObject tmo = this.getOntologyManager().getTopicMap().getObjectByID( id );
		
		Object obj = TM4JObjectWrapper.wrap( this.getOntologyManager(), tmo );
		
		if ((obj != null) && (obj instanceof Attribute)) {
			Attribute attr = (Attribute)obj;
			
			if (this.equals(attr.getAttributeable()) && ((drm == null) || (drm.mayViewAttribute( getter, attr )))) {
				return attr;
			}
		}
		
		return null;
	}


	public Collection getAttributes(User getter) {
		OntologyManagerImpl  omi = this.getOntologyManager();
		DynamicRightsManager drm = omi.getDynamicRightsManager();
		
		Collection c = omi.getPlayers( this.getWrappedTopic(), omi.getAttributeTopicType(), omi.getAttributeTopicType() );
		
		if ((c == null) || c.isEmpty()) {
			return Collections.EMPTY_SET;
		}

		ArrayList list = new ArrayList(10);
		
		Iterator it = c.iterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();

			Object obj = TM4JObjectWrapper.wrap( this.getOntologyManager(), t );
			
			if (obj instanceof Attribute) {
				Attribute attr = (Attribute)obj;
				
				if (drm == null) {
					list.add( attr );
				}
				else {
					Topic type = attr.getType();
					
					if ((type == null) || drm.mayViewTopic( getter, type )) {
						if (drm.mayViewAttribute( getter, attr )) {
							list.add( attr );
						}
					}
				}
			}
		}

		Collections.sort( list );

		return Collections.unmodifiableCollection( list );
	}


	public Collection getAttributes(User getter, Topic type) {
		OntologyManagerImpl  omi = this.getOntologyManager();
		DynamicRightsManager drm = omi.getDynamicRightsManager();
		
		Collection c = omi.getPlayers( this.getWrappedTopic(), omi.getAttributeTopicType(), omi.getAttributeTopicType() );
		
		if ((c == null) || c.isEmpty()) {
			return Collections.EMPTY_SET;
		}

		ArrayList list = new ArrayList(10);
		
		Iterator it = c.iterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)it.next();

			Object obj = TM4JObjectWrapper.wrap( this.getOntologyManager(), t );
			
			if (obj instanceof Attribute) {
				Attribute attr = (Attribute)obj;
				Topic     at   = attr.getType();
				
				if (type == null) {
					if ((at == null) && ((drm == null) || drm.mayViewAttribute( getter, attr ))) {
						list.add( attr );
					}
				}
				else if (type.equals( at ) && ((drm == null) || drm.mayViewAttribute( getter, attr ))) {
					list.add( attr );
				}
			}
		}

		Collections.sort( list );

		return Collections.unmodifiableCollection( list );
	}


	public Collection getOccurrences(User user) {
		OntologyManagerImpl  omi = this.getOntologyManager();
		DynamicRightsManager drm = omi.getDynamicRightsManager();

		Collection c = this.getWrappedTopic().getOccurrences();
		
		if ((c == null) || c.isEmpty()) {
			return Collections.EMPTY_SET;
		}
		
		ArrayList list = new ArrayList(10);
		
		Iterator it = c.iterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Occurrence o = (org.tm4j.topicmap.Occurrence)it.next();
			org.tm4j.topicmap.Topic      t = o.getType();
			
			if (t == null) {
				Occurrence occ = TM4JObjectWrapper.wrap( omi, o );
				list.add( occ );
			}
			else if (!omi.isInternalTopicType(t)) {
				Object obj = TM4JObjectWrapper.wrap( omi, t );
				if ((obj instanceof Topic) && ((drm == null) || drm.mayViewTopic( user, (Topic)obj ))) {
					Occurrence occ = TM4JObjectWrapper.wrap( omi, o );
					list.add( occ );
				}
			}
		}

		Collections.sort( list );

		return Collections.unmodifiableCollection( list );
	}


	public Collection getOccurrences(Topic type) {
		Collection c = this.getWrappedTopic().getOccurrences();
		
		if ((c == null) || c.isEmpty()) {
			return Collections.EMPTY_SET;
		}

		OntologyManagerImpl omi = this.getOntologyManager();
		
		org.tm4j.topicmap.Topic tmType;
		
		if (type == null) {
			tmType = null;
		}
		else {
			tmType = ((TopicImpl)type).getWrappedTopic();
		}
		
		ArrayList list = new ArrayList(10);
		
		Iterator it = c.iterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Occurrence o = (org.tm4j.topicmap.Occurrence)it.next();
			org.tm4j.topicmap.Topic      t = o.getType();
			
			if (t == null) {
				if (tmType == null) {
					Occurrence occ = TM4JObjectWrapper.wrap( omi, o );
					list.add( occ );
				}
			}
			else if (t.equals( tmType )) {
				Occurrence occ = TM4JObjectWrapper.wrap( omi, o );
				list.add( occ );
			}
		}
		
		Collections.sort( list );

		return Collections.unmodifiableCollection( list );
	}


	public Occurrence getOccurrence(User user, String id) {
		if ((id == null) || (id.length() == 0)) {
			return null;
		}
		
		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();

		org.tm4j.topicmap.TopicMapObject tmo = this.getOntologyManager().getTopicMap().getObjectByID( id );
		
		Object obj = TM4JObjectWrapper.wrap( this.getOntologyManager(), tmo );
		
		if ((obj != null) && (obj instanceof Occurrence)) {
			Occurrence occ = (Occurrence)obj;
			
			if (drm != null) {
				Topic type = occ.getType();
				
				if ((type != null) && (!drm.mayViewTopic( user, type ))) {
					return null;
				}
			}
			
			return occ;
		}
		else {
			return null;
		}
	}


	public Occurrence addOccurrence(User adder, String data, Topic type) throws OntologyManagerException,DynamicRightsManagerException {
		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		if (drm != null) {
			drm.checkChangeOccurrence( adder );
		}
		
		if (data == null) {
			data = "";
		}
		
		try {
			org.tm4j.topicmap.Occurrence tmOcc = this.getWrappedTopic().createOccurrence( null );
			
			if (type != null) {
				tmOcc.setType( ((TopicImpl)type).getWrappedTopic() );
			}
			
			tmOcc.setData( data );
			
			Occurrence occ = TM4JObjectWrapper.wrap( this.getOntologyManager(), tmOcc );

			this.incVersion();
			this.getOntologyManager().fireOccurrenceCreated( occ, adder, this );
			
			return occ;
		}
		catch (DuplicateObjectIDException e) {
			throw new OntologyManagerDuplicateIdException( e );
		}
		catch (Exception e) {
			throw new OntologyManagerException( e );
		}
	}


	public void removeOccurrence(User remover, Occurrence occ) throws OntologyManagerException,DynamicRightsManagerException {
		if (occ == null) return;

		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		if (drm != null) {
			drm.checkChangeOccurrence( remover );
		}
		
		if (!this.equals( occ.getTopic() )) {
			throw new OntologyManagerException("Cannot remove " + occ + " from different topic");
		}
		
		Collection c = this.getWrappedTopic().getOccurrences();
		
		if ((c == null) || c.isEmpty()) return;
		
		try {
			c.remove( ((OccurrenceImpl)occ).getWrappedOccurrence() );
			
			org.tm4j.topicmap.Occurrence[] occs = null;
			
			if (!c.isEmpty()) {
				occs = new org.tm4j.topicmap.Occurrence[ c.size() ];
				
				int i = 0;
				
				Iterator it = c.iterator();
				
				while (it.hasNext()) {
					occs[i] = (org.tm4j.topicmap.Occurrence)it.next();
					i++;
				}
			}
			
			this.getWrappedTopic().setOccurrences( occs );
			
			this.incVersion();
			this.getOntologyManager().fireOccurrenceRemoved( occ, remover, this );
		}
		catch (Exception e) {
			throw new OntologyManagerException( e );
		}
	}


	public Collection getSynonyms(User user) {
		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		
		Collection c = this.getOntologyManager().getTopicMap().getUtils().getMembersOfType( this.getWrappedTopic(), this.getOntologyManager().getSynonymPSI() );
		
		if ((c == null) || c.isEmpty()) {
			return Collections.EMPTY_SET;
		}

		ArrayList list = new ArrayList(20);

		Iterator it = c.iterator();
		
		while (it.hasNext()) {
			org.tm4j.topicmap.Member m = (org.tm4j.topicmap.Member)it.next();
			
			Collection players = m.getPlayers();
			
			if (players != null) {
				Iterator itp = players.iterator();
				
				org.tm4j.topicmap.Topic other = null;
				
				while (itp.hasNext()) {
					org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)itp.next();
					
					if (!this.getWrappedTopic().equals( t )) {
						other = t;
						break;
					}
				}
				
				if (other != null) {
					Object obj = TM4JObjectWrapper.wrap( this.getOntologyManager(), other );
					if (obj instanceof Topic) {
						Topic topic = (Topic)obj;
						Association assoc = TM4JObjectWrapper.wrap( this.getOntologyManager(), m.getParent() );
						if ((drm == null) || drm.mayViewTopic( user, topic )) {
							Synonym synonym = new SynonymImpl( this, topic, assoc );
							list.add( synonym );
						}
					}
				}
			}
		}

		Collections.sort( list );

		return Collections.unmodifiableCollection( list );
	}


	public Synonym getSynonym(User user, String id) {
		if ((id == null) || (id.length() == 0)) {
			return null;
		}
		Object tmo = this.getOntologyManager().getTopicMap().getObjectByID( id );
		Object obj = TM4JObjectWrapper.wrap( this.getOntologyManager(), tmo );
		if (obj instanceof AssociationImpl) {
			AssociationImpl assoc = (AssociationImpl)obj;
			Collection c = this.getOntologyManager().getTopicMap().getUtils().getMembersOfType( assoc.getWrappedAssociation(), this.getOntologyManager().getSynonymPSI() );
			if ((c == null) || c.isEmpty()) {
				return null;
			}

			Iterator it = c.iterator();
			if (it.hasNext()) {
				org.tm4j.topicmap.Member m = (org.tm4j.topicmap.Member)it.next();
				
				Collection players = m.getPlayers();

				if (players != null) {
					Iterator itp = players.iterator();
					
					org.tm4j.topicmap.Topic other = null;
					boolean foundThis = false;
					
					while (itp.hasNext()) {
						org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)itp.next();

						if (this.getWrappedTopic().equals( t )) {
							foundThis = true;
						}
						else {
							other = t;
						}
					}

					if (foundThis && (other != null)) {
						Object tobj = TM4JObjectWrapper.wrap( this.getOntologyManager(), other );
						if (tobj instanceof Topic) {
							DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
							Topic topic = (Topic)tobj;
							if ((drm == null) || drm.mayViewTopic( user, topic )) {
								return new SynonymImpl( this, topic, assoc );
							}
						}
					}
				}
			}
		}
		return null;
	}


	private org.tm4j.topicmap.Association getSynonymAssociation(Topic topic) {
		assert(topic != null);
		
		Collection c = this.getOntologyManager().getTopicMap().getUtils().getMembersOfType( this.getWrappedTopic(), this.getOntologyManager().getSynonymPSI() );
		
		if ((c == null) || c.isEmpty()) return null;

		Iterator it = c.iterator();

		while (it.hasNext()) {
			org.tm4j.topicmap.Member m = (org.tm4j.topicmap.Member)it.next();
			
			Collection players = m.getPlayers();
			
			if (players != null) {
				Iterator itp = players.iterator();
				
				while (itp.hasNext()) {
					org.tm4j.topicmap.Topic t = (org.tm4j.topicmap.Topic)itp.next();
					
					if (((TopicImpl)topic).getWrappedTopic().equals( t )) {
						return m.getParent();
					}
				}
			}
		}

		return null;
	}


	public boolean isSynonymous(Topic topic) {
		if (topic == null) return false;
		if (this.equals( topic )) return true;
		
		return (this.getSynonymAssociation( topic ) != null);
	}


	public Synonym addSynonym(User adder, Topic topic) throws OntologyManagerException,DynamicRightsManagerException {
		if (topic == null) return null;
		if (this.equals( topic )) return null;

		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		if (drm != null) {
			drm.checkAddSynonym( adder, this );
		}
		
		org.tm4j.topicmap.Association tmAssoc = this.getSynonymAssociation( topic );

		if (tmAssoc != null) {
			Association assoc = TM4JObjectWrapper.wrap( this.getOntologyManager(), tmAssoc );
			return new SynonymImpl( this, topic, assoc );
		}
		else {
			try {
				tmAssoc = this.getOntologyManager().getTopicMap().createAssociation( null, null, this.getOntologyManager().getSynonymoustermsTopicType(), null );

				this.getOntologyManager().createSourceLocator( tmAssoc );

				org.tm4j.topicmap.Member member = tmAssoc.createMember( null );

				this.getOntologyManager().createSourceLocator( member );
				
				member.setRoleSpec( this.getOntologyManager().getSynonymTopicType() );
				
				member.addPlayer( this.getWrappedTopic() );
				member.addPlayer( ((TopicImpl)topic).getWrappedTopic() );

				Association assoc = TM4JObjectWrapper.wrap( this.getOntologyManager(), tmAssoc );

				Synonym synonym = new SynonymImpl( this, topic, assoc );

				this.incVersion();
				this.getOntologyManager().fireSynonymAdded( synonym, adder );
				
				return synonym;
			}
			catch (DuplicateObjectIDException e) {
				throw new OntologyManagerDuplicateIdException( e );
			}
			catch (Exception e) {
				throw new OntologyManagerException( e );
			}
		}
	}


	public void removeSynonym(User remover, Topic topic) throws OntologyManagerException,DynamicRightsManagerException {
		if (topic == null) return;
		if (this.equals( topic )) return;

		org.tm4j.topicmap.Association tmAssoc = this.getSynonymAssociation( topic );

		if (tmAssoc == null) return;

		Association assoc = TM4JObjectWrapper.wrap( this.getOntologyManager(), tmAssoc );
		
		Synonym synonym = new SynonymImpl( this, topic, assoc );

		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		if (drm != null) {
			drm.checkRemoveSynonym( remover, synonym );
		}
		
		try {
			RatingManager rm = this.getOntologyManager().getRatingManager();
			if (rm != null) {
				rm.deleteRatings( assoc );
			}
			
			tmAssoc.setMembers( null );
			tmAssoc.dispose();
			tmAssoc.destroy();

			this.incVersion();
			this.getOntologyManager().fireSynonymRemoved( synonym, remover );
		}
		catch (Exception e) {
			throw new OntologyManagerException( e );
		}
	}
	
	
	protected void setFlag(String flag) throws OntologyManagerException {
		flag += ";";
		org.tm4j.topicmap.Occurrence occ = this.getOccurrence( this.getOntologyManager().getValueTopicType() );
		try {
			if (occ != null) {
				String flags = occ.getData();
				if (flags != null) {
					if (flags.indexOf( flag ) >= 0) return;
					flags += flag;
				}
				else {
					flags = flag;
				}
				occ.setData( flags );
			}
			else {
				this.getWrappedTopic().createOccurrence( null, null, this.getOntologyManager().getValueTopicType(), flag, EMPTY_SCOPE );
			}
		}
		catch (Exception e) {
			throw new OntologyManagerException("Error setting topic flag: " + e);
		}
	}

	
	protected void removeFlag(String flag) throws OntologyManagerException {
		org.tm4j.topicmap.Occurrence occ = this.getOccurrence( this.getOntologyManager().getValueTopicType() );
		if (occ != null) {
			String flags = occ.getData();
			if (flags != null) {
				int idx = flags.indexOf( flag + ";" );
				if (idx >= 0) {
					flags = flags.substring( 0, idx ) + flags.substring( idx + flag.length() + 1 );
					try {
						occ.setData( flags );
					}
					catch (Exception e) {
						throw new OntologyManagerException("Error removing topic flag: " + e);
					}
				}
			}
		}
	}


	protected boolean hasFlag(String flag) {
		org.tm4j.topicmap.Occurrence occ = this.getOccurrence( this.getOntologyManager().getValueTopicType() );
		if (occ != null) {
			String flags = occ.getData();
			if (flags != null) {
				return (flags.indexOf( flag + ";" ) >= 0);
			}
		}
		return false;
	}


	public void setClassTopic(boolean isclass) throws OntologyManagerException {
		if (isclass) {
			this.setFlag( FLAG_CLASS );
		}
		else {
			this.removeFlag( FLAG_CLASS );
		}
	}


	public boolean isClassTopic() {
		return this.hasFlag( FLAG_CLASS );
	}


	public SuperSubclass getSuperclass(User user, String id) {
		Collection classes = this.getSuperclasses( user );
		Iterator it = classes.iterator();
		while (it.hasNext()) {
			SuperSubclass cls = (SuperSubclass)it.next();
			if (cls.getId().equals( id )) {
				return cls;
			}
		}
		return null;
	}
	

	public Collection getSuperclasses(User user) {
		try {
			return this.getOntologyManager().getQueryEngine().getSuperclasses( user, this );
		}
		catch (QueryEngineException e) {
			return Collections.EMPTY_LIST;
		}
	}
	
	
	public Collection getSubclasses(User user) {
		try {
			return this.getOntologyManager().getQueryEngine().getSubclasses( user, this );
		}
		catch (QueryEngineException e) {
			return Collections.EMPTY_LIST;
		}
	}


	public SuperSubclass addSuperclass(User adder, Topic cls) throws OntologyManagerException,DynamicRightsManagerException {
		if (!this.getOntologyManager().useClasses()) {
			throw new UnsupportedOperationException();
		}

		if (cls == null) {
			throw new OntologyManagerException("Cannot add null as a superclass.");
		}

		if (this.equals( cls )) {
			throw new OntologyManagerException("Cannot add topic as its own superclass.");
		}
		
		if (!this.isClassTopic()) {
			throw new OntologyManagerClassException("Cannot add superclass to instance " + this );
		}
		
		if (!cls.isClassTopic()) {
			throw new OntologyManagerClassException("Cannot add instance " + cls + " as a superclass");
		}

		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		if (drm != null) {
			drm.checkAddClass( adder, this );
		}

		Association assoc = this.getOntologyManager().addSuperclass( adder, this, cls, true );

		return new SuperSubclassImpl( this, cls, assoc );
	}
	
	
	public void removeSuperclass(User remover, Topic cls) throws OntologyManagerException,DynamicRightsManagerException {
		if (!this.getOntologyManager().useClasses()) {
			throw new UnsupportedOperationException();
		}
		
		if (cls == null) return;

		org.tm4j.topicmap.Association tmAssoc = this.getOntologyManager().getSuperclassAssociation( this.getWrappedTopic(), ((TopicImpl)cls).getWrappedTopic() );

		if (tmAssoc == null) return;

		Association assoc = TM4JObjectWrapper.wrap( this.getOntologyManager(), tmAssoc );

		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		if (drm != null) {
			SuperSubclass supersubclass = new SuperSubclassImpl( this, cls, assoc );
			drm.checkRemoveClass( remover, supersubclass );
		}

		try {
			RatingManager rm = this.getOntologyManager().getRatingManager();
			if (rm != null) {
				rm.deleteRatings( assoc );
			}
			
			tmAssoc.setMembers( null );
			tmAssoc.dispose();
			tmAssoc.destroy();

			this.incVersion();
			this.getOntologyManager().fireTopicClassChanged( this, remover, cls );
		}
		catch (Exception e) {
			throw new OntologyManagerException( e );
		}
	}


	public String toString() {
		String s = this.getName();
		return "Topic " + (s.length() > 0 ? ("\"" + s + "\""): "(unnamed)") + ", id=" + this.getId();
	}

}
