package de.uka.ipd.consensus.foundation.ontology;

import java.util.Iterator;
import java.util.Properties;

import de.uka.ipd.consensus.foundation.ConsensusFoundationModule;
import de.uka.ipd.consensus.foundation.logging.LoggingManager;
import de.uka.ipd.consensus.foundation.query.QueryEngine;
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.Rateable;
import de.uka.ipd.consensus.foundation.schema.Topic;
import de.uka.ipd.consensus.foundation.schema.User;
import de.uka.ipd.consensus.foundation.user.UserManager;

/**
 * The OntologyManager is the central component as it manages the elements of the ontology. It is reponsible for
 * loading an existing ontology from a persistent backend store on application startup, and it must initialize the QueryEngine
 * as well as export and import handlers.
 * 
 * <p>The OntologyManager implementation can be configured with the key <code>cf.ontologymanager</code>
 * in the <code>ConsensusFoundation.properties</code> configuration file. It's a core component and not optional.</p>
 * 
 * <p>The implementation is loaded by the <code>ConsensusFoundation</code> constructor. For successful instantiation,
 * the OntologyManager implementation must provide a default constructor without arguments.</p>
 * 
 * @author Thomas Much
 * @version 2006-06-14
 * @see de.uka.ipd.consensus.foundation.query.QueryEngine
 * @see ImportHandler
 * @see ExportHandler
 * @see de.uka.ipd.consensus.foundation.ConsensusFoundation
 * @see de.uka.ipd.consensus.foundation.evolution.EvolutionManager
 */
public interface OntologyManager extends ConsensusFoundationModule {

	/**
	 * <p>This method is called by the <code>ConsensusFoundation</code> constructor to initialize the OntologyManager.
	 * <p>An implementation can use this method to restore an existing ontology from a persistent backend or to load
	 * a new ontology from a file (and replace an existing ontology). Import and export handlers should be initialized here, too.</p>
	 * @param config all entries from <code>ConsensusFoundation.properties</code>
	 * @param logging the successfully initialized LoggingManager instance
	 * @param users the the successfully initialized UserManager instance
	 * @throws Exception if any error occurs (startup of the application is aborted in this case)
	 * @see de.uka.ipd.consensus.foundation.ConsensusFoundation#ConsensusFoundation()
	 */
	public void init(Properties config, LoggingManager logging, UserManager users) throws Exception;

	
	/**
	 * Creates a topic of the given type. If the implementation supports classes, the type must be a class topic.
	 * @param creator the user who wants to create the topic
	 * @param id the id (must not be empty) or (usually) <code>null</code> for an automatically computed id
	 * @param name the topic's name (must not be empty or <code>null</code>)
	 * @param type the type topic for the topic or <code>null</code> for a typeless topic
	 * @return the newly created topic
	 * @throws OntologyManagerDuplicateIdException if the id parameter is not <code>null</code> and is already used within the ontology
	 * @throws OntologyManagerException if the topic could not be created
	 * @throws DynamicRightsManagerException if the user is not allowed to create topics of the given type
	 * @see de.uka.ipd.consensus.foundation.schema.Topic#isClassTopic()
	 * @see de.uka.ipd.consensus.foundation.rights.DynamicRightsManager#mayCreateTopic(User, Topic)
	 */
	public Topic createTopic(User creator, String id, String name, Topic type) throws OntologyManagerException,DynamicRightsManagerException;
	
	
	/**
	 * Creates a class topic as a subclass of the given superclass topic. Only supported if the implementation supports classes.
	 * <p>Topic.setClassTopic(true) is called after creation automatically.</p>
	 * @param creator the user who wants to create the class topic
	 * @param id the id (must not be empty) or (usually) <code>null</code> for an automatically computed id
	 * @param name the topic's name (must not be empty or <code>null</code>)
	 * @param cls the superclass topic for the new class topic or <code>null</code> for a root class topic
	 * @return the newly created class topic
	 * @throws OntologyManagerDuplicateIdException if the id parameter is not <code>null</code> and is already used within the ontology
	 * @throws OntologyManagerException if the class topic could not be created
	 * @throws DynamicRightsManagerException if the user is not allowed to create class topics of the given type
	 * @throws java.lang.UnsupportedOperationException if the implementation does not support classes
	 * @see de.uka.ipd.consensus.foundation.schema.Topic#isClassTopic()
	 * @see de.uka.ipd.consensus.foundation.schema.Topic#setClassTopic(boolean)
	 * @see de.uka.ipd.consensus.foundation.rights.DynamicRightsManager#mayCreateClassTopic(User, Topic)
	 */
	public Topic createClassTopic(User creator, String id, String name, Topic cls) throws OntologyManagerException,DynamicRightsManagerException;


	/**
	 * Creates an internal topic that is not visible through the topic collections, only by id query. If the implementation supports classes, the type must be a class topic.
	 * <p>If an id is given and an internal topic already exists with that id, the existing topic is returned instead of a newly created one.
	 * <p>Topic.setClassTopic(true) is called after creation automatically if the implementation uses classes.</p>
	 * @param creator the user who wants to create the internal topic
	 * @param id the id (must not be empty), <code>null</code> for an automatically computed id
	 * @param name the topic's name (must not be empty or <code>null</code>)
	 * @param type the type topic for the new topic or <code>null</code> for a typeless topic
	 * @return the newly created topic or an existing one with the given id
	 * @throws OntologyManagerException if the topic could not be created
	 * @see de.uka.ipd.consensus.foundation.schema.Topic#isClassTopic()
	 * @see de.uka.ipd.consensus.foundation.schema.Topic#setClassTopic(boolean)
	 */
	public Topic createInternalTopic(User creator, String id, String name, Topic type) throws OntologyManagerException;
	
	
	/**
	 * Creates an association of the given type. If the implementation supports classes, the type must be a class topic.
	 * @param creator the user who wants to create the association
	 * @param id the id (must not be empty) or (usually) <code>null</code> for an automatically computed id
	 * @param name the association's name (<code>null</code> is treated as the empty string)
	 * @param type the type topic for the association or <code>null</code> for a typeless association
	 * @return the newly created association
	 * @throws OntologyManagerDuplicateIdException if the id parameter is not <code>null</code> and is already used within the ontology
	 * @throws OntologyManagerException if the association could not be created
	 * @throws DynamicRightsManagerException if the user is not allowed to create associations of the given type
	 * @see de.uka.ipd.consensus.foundation.schema.Topic#isClassTopic()
	 * @see de.uka.ipd.consensus.foundation.rights.DynamicRightsManager#mayCreateAssociation(User, Topic)
	 */
	public Association createAssociation(User creator, String id, String name, Topic type) throws OntologyManagerException,DynamicRightsManagerException;
	
	
	/**
	 * Creates an attribute of the given type. If the implementation supports classes, the type must be a class topic.
	 * @param creator the user who wants to create the attribute
	 * @param id the id (must not be empty) or (usually) <code>null</code> for an automatically computed id
	 * @param name the attribute's name (must not be empty or <code>null</code>)
	 * @param value the attribute's value (<code>null</code> is treated as the empty string)
	 * @param type the type topic for the attribute or <code>null</code> for a typeless attribute
	 * @return the newly created attribute
	 * @throws OntologyManagerDuplicateIdException if the id parameter is not <code>null</code> and is already used within the ontology
	 * @throws OntologyManagerException if the attribute could not be created
	 * @throws DynamicRightsManagerException if the user is not allowed to create attributes of the given type
	 * @see de.uka.ipd.consensus.foundation.schema.Attributeable#addAttribute(User, Attribute)
	 * @see de.uka.ipd.consensus.foundation.schema.Topic#isClassTopic()
	 * @see de.uka.ipd.consensus.foundation.rights.DynamicRightsManager#mayCreateAttribute(User, Topic)
	 */
	public Attribute createAttribute(User creator, String id, String name, String value, Topic type) throws OntologyManagerException,DynamicRightsManagerException;

	
	/**
	 * Checks the given rateable object if it is a special association that is usually wrapped inside a TypeInstance, SuperSubclass or Synonym.
	 * If it is, the association is wrapped and one of the three mentioned types is returned. Otherwise, the rateable object is returned unchanged.
	 * @param rateable the rateable object to be checked if it is a special internal association
	 * @return the wrapped association if it is a special internal one, or the unchanged parameter
	 * @see de.uka.ipd.consensus.foundation.schema.TypeInstance
	 * @see de.uka.ipd.consensus.foundation.schema.SuperSubclass
	 * @see de.uka.ipd.consensus.foundation.schema.Synonym
	 */
	public Rateable checkInternalRateable(Rateable rateable);

	
	/**
	 * Deletes a topic permanently from the ontology. This is done without other steps that might be necessary,
	 * for example deleting associations or other elements that are useless without this topic.
	 * <p>To delete topics in an application, use the EvolutionManager that keeps the ontology consistent and checks the user's rights.
	 * The EvolutionManager will then use this method to do the actual deleting.</p>
	 * @param topic the topic to be deleted
	 * @throws OntologyManagerException if the topic cannot be deleted without making the ontology inconsistent
	 * @see de.uka.ipd.consensus.foundation.evolution.EvolutionManager
	 */
	public void deleteTopic(Topic topic) throws OntologyManagerException;
	
	
	/**
	 * Deletes an association permanently from the ontology. This is done without other steps that might be necessary,
	 * for example deleting topics or other elements that are useless without this association.
	 * <p>To delete associations in an application, use the EvolutionManager that keeps the ontology consistent and checks the user's rights.
	 * The EvolutionManager will then use this method to do the actual deleting.</p>
	 * @param assoc the association to be deleted
	 * @throws OntologyManagerException if the association cannot be deleted without making the ontology inconsistent
	 * @see de.uka.ipd.consensus.foundation.evolution.EvolutionManager
	 */
	public void deleteAssociation(Association assoc) throws OntologyManagerException;
	
	
	/**
	 * Deletes an attribute permanently from the ontology. This is done without other steps that might be necessary,
	 * for example deleting associations or other elements that are useless without this attribute.
	 * <p>To delete attributes in an application, use the EvolutionManager that keeps the ontology consistent and checks the user's rights.
	 * The EvolutionManager will then use this method to do the actual deleting.</p>
	 * @param attr the attribute to be deleted
	 * @throws OntologyManagerException if the attribute cannot be deleted without making the ontology inconsistent
	 * @see de.uka.ipd.consensus.foundation.evolution.EvolutionManager
	 */
	public void deleteAttribute(Attribute attr) throws OntologyManagerException;

	
	/**
	 * Returns the QueryEngine that is part of the OntologyManager.
	 * <p>Note that the engine might be available only after <code>initCompleted()</code> has finished.</p>
	 * @return the QueryEngine instance
	 */
	public QueryEngine getQueryEngine();
	

	/**
	 * Opens a transaction for the ontology backend. Pass a suitable scope object, for example a HTTP request object.
	 * <p>The return value is a transaction object if this call started a new transaction or <code>null</code> if another transaction is already running for this scope.
	 * You'll have to pass the exact return value to the commit/rollback method.
	 * <p>Note that the implementation is not required to use the scope information if the backend does not support scoped transactions.
	 * If the backend does not support transactions at all, you'll always get <code>null</code> as the return value, which you can safely pass
	 * to the other transaction methods.</p>
	 * @param scope the scope how long the transaction will probably last
	 * @return a transaction object or <code>null</code>
	 * @throws OntologyManagerException if the transaction could not be opened
	 */
	public Object openTransaction(Object scope) throws OntologyManagerException;
	
	
	/**
	 * Commits the transaction for the given scope. You must pass the exact return value from <code>openTransaction()</code> to this method.
	 * @param scope the scope for this transaction
	 * @param ta the return value from <code>openTransaction()</code>
	 * @throws OntologyManagerException if the transaction could not be committed successfully
	 * @see #openTransaction(Object)
	 */
	public void commitTransaction(Object scope, Object ta) throws OntologyManagerException;
	
	
	/**
	 * Rolls the transaction back for the given scope. You must pass the exact return value from <code>openTransaction()</code> to this method.
	 * @param scope the scope for this transaction
	 * @param ta the return value from <code>openTransaction()</code>
	 * @throws OntologyManagerException if the transaction could not be rolled back successfully
	 * @see #openTransaction(Object)
	 */
	public void rollbackTransaction(Object scope, Object ta) throws OntologyManagerException;
	
	
	/**
	 * This is a "quick rollback" for the transaction if no changes have been made to the ontology. Use with caution,
	 * especially if the implementation does not support scopes and other scopes might have changed the ontology.
	 * @param scope the scope for this transaction
	 * @param ta the return value from <code>openTransaction()</code>
	 * @see #openTransaction(Object)
	 */
	public void abortTransaction(Object scope, Object ta);

	
	/**
	 * Registers an ExportHandler instance with the OntologyManager. Usually you don't call this method
	 * explicitely but declare the handler classes in <code>ConsensusFoundation.properties</code>.
	 * <p>For each file format, only one export handler can be registered. Registering a format again replaces the older handler.</p>
	 * @param handler the ExportHandler to be added to the OntologyManager's list of export handlers
	 * @see ExportHandler
	 */
	public void addExportHandler(ExportHandler handler);
	
	
	/**
	 * Returns an export handler for a certain file format.
	 * @param format the required file format (e.g. "xtm")
	 * @return the ExportHandler or <code>null</code> if no export handler exists for the required format
	 * @see #addExportHandler(ExportHandler)
	 */
	public ExportHandler getExportHandler(String format);
	
	
	/**
	 * Returns all export handlers.
	 * @return an Iterator over all export handlers
	 * @see #addExportHandler(ExportHandler)
	 */
	public Iterator getExportHandlers();
	
	
	/**
	 * Returns the number of registered export handlers.
	 * @return the number of export handlers
	 * @see #addExportHandler(ExportHandler)
	 */
	public int getExportHandlerCount();

	
	/**
	 * Registers an ImportHandler instance with the OntologyManager. Usually you don't call this method
	 * explicitely but declare the handler classes in <code>ConsensusFoundation.properties</code>.
	 * <p>For each file format, only one import handler can be registered. Registering a format again replaces the older handler.</p>
	 * @param handler the ImportHandler to be added to the OntologyManager's list of import and merge handlers
	 * @see ImportHandler
	 */
	public void addImportHandler(ImportHandler handler);
	
	
	/**
	 * Returns an import handler for a certain file format.
	 * @param format the required file format (e.g. "xtm")
	 * @return the ImportHandler or <code>null</code> if no import handler exists for the required format
	 * @see #addImportHandler(ImportHandler)
	 */
	public ImportHandler getImportHandler(String format);


	/**
	 * Returns an import handler for a certain file format that can merge ontologies in this format.
	 * @param format the required file format (e.g. "xtm")
	 * @return the ImportHandler or <code>null</code> if no import handler exists that can merge ontologies in the required format
	 * @see #addImportHandler(ImportHandler)
	 */
	public ImportHandler getMergeHandler(String format);
	
	
	/**
	 * Returns all import handlers.
	 * @return an Iterator over all import handlers
	 * @see #addImportHandler(ImportHandler)
	 */
	public Iterator getImportHandlers();
	
	
	/**
	 * Returns all import handlers that can merge ontologies.
	 * @return an Iterator over all import handlers that can merge ontologies
	 * @see #addImportHandler(ImportHandler)
	 */
	public Iterator getMergeHandlers();
	
	
	/**
	 * Returns the number of registered import handlers.
	 * @return the number of import handlers
	 * @see #addImportHandler(ImportHandler)
	 */
	public int getImportHandlerCount();
	
	
	/**
	 * Returns the number of registered import handlers that can merge ontologies.
	 * @return the number of import handlers that can merge ontologies
	 * @see #addImportHandler(ImportHandler)
	 */
	public int getMergeHandlerCount();

	
	/**
	 * If the ontology needs reorganization from time to time, the implementation can clean up the ontology when an application calls this method.
	 * The implementation should try to reorganize the ontology asynchronously and not to pause the running application.
	 * @return <code>true</code> if the reorganization was started due to this call or <code>false</code> if an older reorganization is still running
	 * @throws OntologyManagerException if the reorganization could not be finished successfully
	 */
	public boolean reorg() throws OntologyManagerException;
	

	/**
	 * Registers a new OntologyListener.
	 * @param listener the OntologyListener to be added
	 */
	public void addOntologyListener(OntologyListener listener);
	
	
	/**
	 * Removes an OntologyListener, if it is registered with the OntologyManager (otherwise this call is ignored).
	 * @param listener the OntologyListener to be removed
	 */
	public void removeOntologyListener(OntologyListener listener);
}
