/*
Copyright (C) 2000-2010  Ministere de la culture et de la communication (France), AJLSM
See LICENCE file
*/
package fr.gouv.culture.sdx.documentbase;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Locale;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.xml.XMLConsumer;
import org.apache.excalibur.source.SourceValidity;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import fr.gouv.culture.oai.OAIHarvester;
import fr.gouv.culture.oai.OAIRepository;
import fr.gouv.culture.sdx.document.BinaryDocument;
import fr.gouv.culture.sdx.document.Document;
import fr.gouv.culture.sdx.document.IndexableDocument;
import fr.gouv.culture.sdx.document.ParsableDocument;
import fr.gouv.culture.sdx.exception.SDXException;
import fr.gouv.culture.sdx.exception.SDXExceptionCode;
import fr.gouv.culture.sdx.framework.Framework;
import fr.gouv.culture.sdx.oai.DocumentBaseOAIHarvester;
import fr.gouv.culture.sdx.pipeline.GenericPipeline;
import fr.gouv.culture.sdx.pipeline.Pipeline;
import fr.gouv.culture.sdx.repository.Repository;
import fr.gouv.culture.sdx.repository.RepositoryConnection;
import fr.gouv.culture.sdx.search.lucene.query.SortSpecification;
import fr.gouv.culture.sdx.utils.Utilities;
import fr.gouv.culture.sdx.utils.constants.ContextKeys;
import fr.gouv.culture.sdx.utils.constants.Node;
import fr.gouv.culture.sdx.utils.database.DatabaseBacked;
import fr.gouv.culture.sdx.utils.database.DatabaseEntity;
import fr.gouv.culture.sdx.utils.logging.LoggingUtils;
import fr.gouv.culture.sdx.utils.lucene.LuceneDataStore;
import fr.gouv.culture.sdx.utils.save.SaveParameters;

/**
 * an abstract document base  class handling most common behaviors
 */
public abstract class AbstractDocumentBase extends DatabaseBacked implements DocumentBase {

    /************
     Class members related to interface DocumentBase
     ************/
    /** True if this document base is the default one in the application. */
    protected boolean isDefault = false;
    /** Number of results per page. */
    protected int defaultHitsPerPage = 20;
    /** Maximum number of results to sort. */
    protected int defaultMaxSort = SortSpecification.SORT_UNLIMITED;
    /**The default indexation pipeline*/
    protected Pipeline _indexationPipeline = null;
    /** The repositories that are owned by this document base. */
    protected Hashtable repositories = null;
    /** The default repository for this document base. */
    protected Repository defaultRepository = null;
    /**The oai repository wrapper for this document base*/
    protected OAIRepository oaiRepo = null;
    /**The OAI repositories for this document base*/
    protected Hashtable oaiRepositories = null;
    /**The oai harvester wrapper for this document base*/
    protected OAIHarvester _oaiHarv = null;


    /************
     Class members NOT related to interface DocumentBase
     ************/

    /** The locale for this document base (found either in the configuration file or by using <code>Locale.getDefault()</code>). */
    protected Locale locale = null;
    /** A pool of connections to the repositories. */
    protected Hashtable repoConnectionPool = null;
    //TODO: use inner class when javac compiler bug is fixed-rbp
    /**Database for document metadata and relations*/
    //protected AbstractDocumentBase.LookupDatabase lookupDatabase = this.new LookupDatabase();
    /** The id generator for documents. */
    IDGenerator idGen = null;

    /**********************************************************************************************
     Internal fields for relationships between documents (kind of metadata required/proposed by SDX)
     //TODO : move them to a framework/application/relationship class ? -pb
     ***********************************************************************************************

     /** Internal value for this field : an attached document relationship. */
    protected final String PROPERTY_NAME_ATTACHED = "attached"; //TODO : use value defined in a DB/Document/Relationship  class  ? -pb
    /** Internal value for this field : an original document relationship. */
    protected final String PROPERTY_NAME_ORIGINAL = "original"; //TODO : use value defined in a DB/Document/Relationship  class  ? -pb
    /** Internal value for this field : a sub-document document relationship. */
    protected final String PROPERTY_NAME_SUB = "sub"; //TODO : use value defined in a DB/Document/Relationship  class  ? -pb
    protected final String PROPERTY_NAME_PARENT = "parent";

    /**********************************************************************
     Attribute names for the configuration element in the configuration file
     **********************************************************************/

    /** The implied attribute stating whether the document base is to be used by default or not. */
    protected final String DBELEM_ATTRIBUTE_DEFAULT = "default"; //TODO : make static ? -pb
    /** The implied attribute stating whether the document base is to be used by default or not. */
    protected final static String DBELEM_ATTRIBUTE_MAXSORT = "maxsort";
    /** The implied attribute stating whether the document base is to be used by default or not. */
    protected final static String DBELEM_ATTRIBUTE_HPP = "hpp";
    /** The implied attribute stating whether original documents should be stored or not. */
    protected final String DBELEM_ATTRIBUTE_KEEP_ORIGINAL = "keepOriginalDocuments"; //TODO : make static ? -pb
    /** Attribute required to specify the compound files format for indexation */
    protected final String ATTRIBUTE_COMPOUND_FILES = "useCompoundFiles";
    /** Attribute required to specify the metadata usage */
    String ATTRIBUTE_USE_METADATA = "useMetadata";
    /** Attribute needed to make a choice of optimization method */
    protected final String ATTRIBUTE_AUTO_OPTIMIZE = "autoOptimize";
    
    /** Element for cron configuration (optimization) */
    protected static final String ELEMENT_NAME_OPTIMIZE = "optimization";

    /** Index splitting condition meta name */
    protected static final String ELEMENT_NAME_INDEX_SPLIT = "split";
    /** Attribute used to fix the size limit per index before splitting it */
    protected final String ATTRIBUTE_SPLIT_SIZE = "size";
    /** Very optionnal attribute used to tell wich unit must be use with the size one to determine max index size */
    protected final String ATTRIBUTE_SPLIT_UNIT = "unit";
    /** Attribute used to fix the document limit per index before splitting it */
    protected final String ATTRIBUTE_SPLIT_DOC = "nbDocuments";



    /*************************************************************************
     Child element names of the configuration element in the configuration file
     *************************************************************************/


    /************************************************************************
     Property names for document metadata in SDX
     ************************************************************************/
    /** Metadata usage defaulted to true */
    public boolean useMetadata = true;
    /** A constant for the repository property in the database entities. */
    protected final static String PROPERTY_NAME_REPO = "repo";
    /** A constant for the path property for attached documents. */
    protected final static String PROPERTY_NAME_MIMETYPE = "mimetype";
    protected final static String PROPERTY_NAME_CONTENT_LENGTH = "content-length";
    /** A constant for the path property for the type of document, Binary, HTML, or XML. */
    protected final String PROPERTY_NAME_DOCTYPE = "sdxDocType";
    /** Internal field name for document ids */
    public static final String INTERNAL_FIELD_NAME_SDXDOCID = LuceneDataStore.ID_FIELD; //TODO : use value defined in a DB/Document class  ? -pb
    /** Internal field name for a *fixed* value field. This field can serve several purposes :
     1) permit a search that is able to retrieve all the documents (no 'SELECT *' in Lucene),
     2) act as a hack that permits to allow creation of binary queries when user wants only to provide an unary one
     */
    public static final String INTERNAL_FIELD_NAME_SDXALL = LuceneDataStore.ALL_FIELD; //Definitely Lucene dependant...
    /** Internal value for this field. */
    public static final String INTERNAL_SDXALL_FIELD_VALUE = LuceneDataStore.ALL_VALUE; //... and so is this one
    /** Internal field name for application id. */
    public static final String INTERNAL_FIELD_NAME_SDXAPPID = "sdxappid"; //TODO : use value defined in a DB/Document class  ? -pb
    /** Internal field name for document base id. */
    public static final String INTERNAL_FIELD_NAME_SDXDBID = "sdxdbid"; //TODO : use value defined in a DB/Document class  ? -pb
    /** Internal field name for repository id. */
    public static final String INTERNAL_FIELD_NAME_SDXREPOID = "sdxrepoid"; //TODO : use value defined in a DB/Document class  ? -pb
    /** Internal field name for document type. */
    public static final String INTERNAL_FIELD_NAME_SDXDOCTYPE = "sdxdoctype"; //TODO : use value defined in a DB/Document class  ? -pb
    /** Internal field name for modification date. */
    public static final String INTERNAL_FIELD_NAME_SDXMODDATE = "sdxmoddate"; //TODO : use value defined in a DB/Document class  ? -pb
    /** Internal field name for document length in bytes. */
    public static final String INTERNAL_FIELD_NAME_SDXCONTENTLENGTH = "sdxcontentlength"; //TODO : use value defined in a DB/Document class  ? -pb
    public static final String INTERNAL_FIELD_NAME_SDX_OAI_DELETED_RECORD = "sdxOaiDeletedRecord";

    /**
     * Returns <code>true</code> if this document base is the default in the application.
     */
    public boolean isDefault() {
        return this.isDefault;
    }
    
    /**
     * Return the number (int) of OAI repositories managed by the document base.
     */
    public int getOAIRepositoriesSize(){
    	return this.oaiRepositories.size();
    }

    /**
     * Return <code>true</code> or <code>false</code> if this document base should use metadata or not
     * @return boolean
     */
    final public boolean isUseMetadata(){
    	return useMetadata;
    }

    /* (non-Javadoc)
     * @see fr.gouv.culture.sdx.documentbase.DocumentBase#getDefaultHitsPerPage()
     */
    public int getDefaultHitsPerPage() {
        return this.defaultHitsPerPage;
    }
    /* (non-Javadoc)
     * @see fr.gouv.culture.sdx.documentbase.DocumentBase#getDefaultMaxSort()
     */
    public int getDefaultMaxSort() {
        return this.defaultMaxSort;
    }

    /**Returns the default indexation pipeline used for indexation in this document base. */
    public Pipeline getIndexationPipeline() {
        try {
            return this._indexationPipeline.newInstance();
        } catch (SDXException e) {
            LoggingUtils.logException(super.getLog(), e);
            return null;
        }
    }

    //TODO : I can't understand a default result down here. Either the mimetype is actually retrieved
    //either it's not -pb
    /**Supplies the mimeType for the the document if it exists
     * otherwise the binary document default mimetype is returned
     * @param doc	The document.
     */
    public String getMimeType(Document doc) throws SDXException {

    	DatabaseEntity ent = null;
    	String mimeType = BinaryDocument.DEFAULT_MIMETYPE;//default mime type
    	Utilities.checkDocument(super.getLog(), doc);

    	//if we use metadata, try to get the mime type from the database
    	if(isUseMetadata()){
    		try {
    			ent = _database.getEntity(doc.getId());
    		} catch (SDXException e) {
    			return BinaryDocument.DEFAULT_MIMETYPE;
    		}
    		if (ent != null){
    			ent.enableLogging(super.getLog());
    			mimeType = ent.getProperty(PROPERTY_NAME_MIMETYPE);
    			if (!Utilities.checkString(mimeType)) mimeType = BinaryDocument.DEFAULT_MIMETYPE;
    		}
    	}

    	return mimeType;

    }


    protected void configurePipeline(Configuration configuration) throws ConfigurationException {
        //at this point, we should have a <sdx:pipeline> element
        this._indexationPipeline = new GenericPipeline();
        this._indexationPipeline = (Pipeline) super.setUpSdxObject(_indexationPipeline);
        //getting the pipeline configuration
        //TODO : what if configuration.getChild(ELEMENT_NAME_INDEX) returns null ? -pb*
        //A:it wont't based upon the avalon architecture, please see the avalon javadocs:)-rbp
        //My question was about code design : a code profiler doesn't know of Avalon docs
        //but it does know about potential null pointers -pb
        Configuration pipeConf = configuration.getChild(DocumentBase.ConfigurationNode.INDEX, true).getChild(DocumentBase.ConfigurationNode.PIPELINE, false);
        //testing if we have something
        /*null possibility is now handled in AbstractPipeline configuration*/
        //creating the pipeline and assigning the class field
        if (pipeConf == null) {
            //no <sdx:index> or <sdx:pipeline> element
            String[] args = new String[1];
            args[0] = configuration.getLocation();
            SDXException sdxE = new SDXException(SDXExceptionCode.ERROR_NO_PIPELINE_CONFIG, args);
            //we log a warning if no pipeline decl. but continue as piplines can be provided externally
            LoggingUtils.logWarn(super.getLog(), sdxE.getMessage());
        } else
            this._indexationPipeline.configure(pipeConf);
    }

    /**Does param checks for subclasses
     *
     * @param doc
     * @param consumer
     * @throws SDXException
     */
    public void getDocument(ParsableDocument doc, XMLConsumer consumer) throws SDXException {
        //checking the document
        Utilities.checkDocument(super.getLog(), doc);
        //verifying the consumer
        Utilities.checkXmlConsumer(super.getLog(), consumer);
        //sub-classes will do the stuff...
    }

    /**Does param checks for subclasses
     *
     * @param doc
     * @param os
     * @throws SDXException
     */
    public void getDocument(Document doc, OutputStream os) throws SDXException {
        //checking the document
        Utilities.checkDocument(super.getLog(), doc);
        //verifying the stream
        Utilities.checkOutputStream(super.getLog(), os);
        //sub-classes will do the stuff...
    }

    /**Does param checks for subclasses
     *
     * @param doc
     * @return
     * @throws SDXException
     */
    public InputStream getDocument(Document doc) throws SDXException {
        //checking the document
        Utilities.checkDocument(super.getLog(), doc);
        return null; //TODO : wow ! -pb
    }

    /** Returns the default repository for this document base.
     * @return The default repository object.
     */
    public Repository getDefaultRepository() {
        return defaultRepository;
    }

    /** Gets a repository in this document base.
     *
     * @param id The repository's id, if <code>null</code> for the default repository is returned
     * @return The repository object
     */
    public Repository getRepository(String id) throws SDXException {
        if (id == null)
            return this.defaultRepository;
        else {
            Repository repo = (Repository) repositories.get(id);
            if (repo == null) {
                String[] args = new String[2];
                args[0] = id;
                args[1] = super.getId();
                throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_UNKNOWN_REPOSITORY, args, null);
            } else
                return repo;
        }
    }

    /**Creates an OAIRepository for the documentbase
    *
    * @param	repo	String	The id of the repository to create
    * @return	OAIRepository
    */
   public OAIRepository createOAIRepository(String repo) throws ConfigurationException {
	   String[] args = {repo};
	   LoggingUtils.logDebug(_logger, SDXExceptionCode.OAI_REPOSITORY_CREATED, args);
	   return getOAIRepository(repo);
   }
    
    /**Creates an OAIRepository for the documentbase
     *
     * @return
     */
    public OAIRepository createOAIRepository() throws ConfigurationException {
 	   	LoggingUtils.logDebug(_logger, SDXExceptionCode.OAI_DEFAULT_REPOSITORY_CREATED, null);
    	return getDefaultOAIRepository();
    }
    
    /**Returns the specified OAI repository
     * @param	repo	String	The repository id
     * @return	OAIRepository
     */
    public OAIRepository getOAIRepository(String repo) {
    	String[] args = {repo,this._id};
 	   	LoggingUtils.logDebug(_logger, SDXExceptionCode.OAI_SERVING_REPOSITORY, args);
    	if ( this.oaiRepositories==null || this.oaiRepositories.isEmpty() ) {
    		String[] args2 = {this._id};
    		LoggingUtils.logWarn(_logger, SDXExceptionCode.OAI_NO_OAI_REPOSITORY_AVAILABLE, args2);
    		return null;
    	}
    	else {
    		Hashtable oair = null;
    		if ( repo.equals("default") && this.oaiRepositories.size()==1 ) {
    			String[] args2 = {this._id};
        		LoggingUtils.logWarn(_logger, SDXExceptionCode.OAI_SERVING_SINGLE_REPOSITORY, args2);
        		oair = (Hashtable) this.oaiRepositories.get( this.oaiRepositories.keySet().toArray()[0] );
        	}
        	else if ( Utilities.checkString(repo) ) {
        		String[] args2 = {this._id};
        		LoggingUtils.logWarn(_logger, SDXExceptionCode.OAI_SERVING_SINGLE_REPOSITORY, args2);
        		oair = (Hashtable) this.oaiRepositories.get(repo);
    		}
    		else {
    			String[] args2 = {repo};
        		LoggingUtils.logWarn(_logger, SDXExceptionCode.OAI_NO_OAI_REPOSITORY_AVAILABLE, args2);
    			oair = (Hashtable) this.oaiRepositories.get("default");
    		}
    		if(oair!=null && !oair.isEmpty()){
    			return (OAIRepository) oair.get("OAIRepository");
    		}
    		else return null;
    	}
    }

    /**
     * @see #getDefaultOAIRepository()
     */
    public OAIRepository getOAIRepository() {
        return getDefaultOAIRepository();
    }
    /**Returns the default OAI repository
     * @return OAIRepository
     */
    public OAIRepository getDefaultOAIRepository(){
    	return getOAIRepository("default");
    }

    /**
     * Returns the OAI harvester
     * @return OAIHarvester
     */
    public OAIHarvester getOAIHarvester() {
        return this._oaiHarv;
    }

    /**
     * Adds an OAI repository to the repositories list.
     * @param _repo
     * @param _conf
     * @param _repoid
     */
    protected void addOAIRepository(OAIRepository _repo, Configuration _conf, String _repoid){
    	if(!Utilities.checkString(_repoid)) _repoid = this.oaiRepositories.size() + "";
    	Hashtable oair = new Hashtable(1); // hashtable pour enregistrer l'entrepot OAI *et* sa configuration originale
        oair.put("OAIRepository", _repo);
        oair.put("configuration", _conf);
        this.oaiRepositories.put( _repoid, oair );
        this.oaiRepo = _repo;
    }

    /**
     * Adds an indexable document to the list of the OAI deleted records.
     * This method is called by the delete document process.
     * @param doc	The indexable document to add to the list
     * @throws SDXException
     * @see #managedOaiDeletedRecord(IndexableDocument, String)
     */
    protected void addOaiDeletedRecord(IndexableDocument doc) throws SDXException {
    	managedOaiDeletedRecord(doc, "add");
    }

    /**
     * Remove an indexable document to the list of the OAI deleted records.
     * This method is called by the indexation document process.
     * @param doc	The indexable document to remove from the list
     * @throws SDXException
     * @see #managedOaiDeletedRecord(IndexableDocument, String)
     */
    protected void removeOaiDeletedRecord(IndexableDocument doc) throws SDXException {
    	managedOaiDeletedRecord(doc, "remove");
    }
    
    /**
     * Manages the list of OAI deleted documents
     * @param action	Actions are: <code>add</code> or <code>remove</code>
     * @throws SDXException
     */
    protected void managedOaiDeletedRecord(IndexableDocument doc, String action) 
    	throws SDXException 
	{
    	String[] args = {doc.getId(),action};
		LoggingUtils.logDebug(_logger, SDXExceptionCode.OAI_MANAGE_DELETED_RECORD, args);
    	if ( Utilities.checkString(action) && doc!=null && doc.getId()!=null && this.oaiRepositories!=null && !this.oaiRepositories.isEmpty() ) {
    		OAIRepository _oair=null;
    		String _id=doc.getId();
    		for (Enumeration r = this.oaiRepositories.elements(); r.hasMoreElements();) {
    			_oair = (OAIRepository) ((Hashtable)r.nextElement()).get("OAIRepository");
		       if (_oair != null) {
		    	   if("add".equals(action)) {
		    		   _oair.addDeletedRecord(_id); // ajout du doc dans la liste des doc oai supprimes
		    	   }
		    	   else if("remove".equals(action)) {
		    		   _oair.removeDeletedRecord(_id); // suppression du doc dans la liste des doc oai supprimes
		    	   }
		       }
    		}
    		_oair=null; _id=null;
    	}
    }

    /**
     * Optimizes the database of the document base.
     * @throws SDXException
     */
    protected synchronized void optimizeDatabase() throws SDXException {
        if (_database != null)
            _database.optimize();
    }

    /**Optimizes the repositories of the document base.
     * This method should be called before
     * releasePooledRepositoryConnections();
     * so we have something to optimize.
     */
    protected synchronized void optimizeRepositories() throws SDXException {
        if (this.repoConnectionPool != null && !this.repoConnectionPool.isEmpty()) {
            Enumeration repos = this.repositories.elements();
            if (repos != null) {
                while (repos.hasMoreElements()) {
                    //getting the next repo
                    Repository repo = (Repository) repos.nextElement();
                    //optimizing it
                    /*NOTE:it is the responsibility of the repository
                    *to ensure that unnecessary optimizations are not
                    *executed with the Repository.optimize() method is called
                    */
                    if (repo != null)
                        repo.optimize();
                }
            }
        }
    }

    protected synchronized void releasePooledRepositoryConnections() throws SDXException {

        Enumeration openConnections = null;
        if (this.repoConnectionPool != null)
            openConnections = this.repoConnectionPool.keys();
        if (openConnections != null) {
            while (openConnections.hasMoreElements()) {
                String repoId = (String) openConnections.nextElement();
                RepositoryConnection conn = (RepositoryConnection) this.repoConnectionPool.get(repoId);
                //getting the repository for the connection
                Repository repo = getRepository(repoId);
                //releasing each connection
                if (repo != null && conn != null) {
                    conn.commit();
                    repo.releaseConnection(conn);
                }
                //removing the connection from the pool
                this.repoConnectionPool.remove(repoId);
            }
        }
    }

    //TODOD this method should handle both an repo id and an actual repository object as it may be provided externally, not within this class
    protected synchronized RepositoryConnection getPooledRepositoryConnection(String repoId) throws SDXException {
        if (this.repoConnectionPool == null) this.repoConnectionPool = new Hashtable();

        RepositoryConnection conn = null;

        if (Utilities.checkString(repoId)) {
            if (repoConnectionPool.containsKey(repoId)) {
                conn = (RepositoryConnection) this.repoConnectionPool.get(repoId);
            } else {
                //getting the repository
                Repository repo = (Repository) this.repositories.get(repoId);
                if (repo != null) {
                    //retrieving a connection
                    conn = repo.getConnection();
                    //putting the connection in the pool
                    if (conn != null)
                        this.repoConnectionPool.put(repoId, conn);
                }
            }
        }

        return conn;

    }

    protected DatabaseEntity createEntityForDocMetaData(Document doc, Repository repository, String[] parentDocId) throws SDXException {
    	if(useMetadata){
    		//creating a document entry in repository look-up index
    		DatabaseEntity docLookupEntry = new DatabaseEntity(doc.getId());
    		docLookupEntry.enableLogging(super.getLog());

    		//saving the repository
    		if (repository != null)
    			docLookupEntry.addProperty(PROPERTY_NAME_REPO, repository.getId());
    		//saving the document type
    		docLookupEntry.addProperty(PROPERTY_NAME_DOCTYPE, doc.getDocType());
    		//saving the mime type
    		if (Utilities.checkString(doc.getMimeType()))
    			docLookupEntry.addProperty(PROPERTY_NAME_MIMETYPE, doc.getMimeType());

    		String length = Integer.toString(doc.getLength());
    		if (Utilities.checkString(length))
    			docLookupEntry.addProperty(PROPERTY_NAME_CONTENT_LENGTH, length);

    		docLookupEntry.addProperties(PROPERTY_NAME_PARENT, parentDocId);

    		return docLookupEntry;
    	} else return null;

    }

    /**
     * Deletes a document and any attached document(s) if not used by any other document(s).
     *
     * @param	doc The document to delete.
     * @param   handler A content handler to feed with information.

     * @throws fr.gouv.culture.sdx.exception.SDXException
     */
    public synchronized void delete(Document doc, ContentHandler handler) throws SDXException, SAXException, ProcessingException {
        Utilities.checkDocument(super.getLog(), doc);
        Document[] docs = new Document[1];
        docs[0] = doc;
        delete(docs, handler);
    }


    /**This method does  a special delete. Only the document and it's lookup information are
     * deleted any relations will remain
     * @param doc
     * @param handler
     * @throws SDXException
     * @throws SAXException
     * @throws ProcessingException
     */
    protected void deletePhysicalDocument(Document doc, Repository repo, ContentHandler handler) throws SDXException, SAXException, ProcessingException {
        //ensuring we have valid objects
        Utilities.checkDocument(super.getLog(), doc);
        RepositoryConnection conn = null;
        DatabaseEntity ent = null;
        if (repo != null) {
            conn = getPooledRepositoryConnection(repo.getId());

            /*Getting the database entity corresponding to this document*/
            if(isUseMetadata())
            	ent = super._database.getEntity(doc.getId());
            /*
            * STEP 1 : delete the document's entry from the look-up index
            */
            //TODO: use inner class when javac compiler bug is fixed-rbp
//          super.lookupDatabase.deleteDocMetaDataFromRelationsIndex(doc);

            /*
            * STEP 2 : deleteing the document itself
            */
            if (conn != null)
                repo.delete(doc, conn);
        }

        //sending sax events
        if (handler != null) {
            AttributesImpl atts = new AttributesImpl();
            atts.addAttribute("", Node.Name.ID, Node.Name.ID, Node.Type.CDATA, doc.getId());
            if (repo != null)
                atts.addAttribute("", Node.Name.REPO, Node.Name.REPO, Node.Type.CDATA, repo.getId());
            atts.addAttribute("", Node.Name.BASE, Node.Name.BASE, Node.Type.CDATA, this.getId());
            String appId = Utilities.getStringFromContext(ContextKeys.SDX.Application.ID, super.getContext());
            if (Utilities.checkString(appId))
                atts.addAttribute("", Node.Name.APP, Node.Name.APP, Node.Type.CDATA, appId);
            if (ent != null) {
                String mimeType = ent.getProperty(PROPERTY_NAME_MIMETYPE);
                if (Utilities.checkString(mimeType))
                    atts.addAttribute("", Node.Name.MIMETYPE, Node.Name.MIMETYPE, Node.Type.CDATA, mimeType);
                String length = ent.getProperty(PROPERTY_NAME_CONTENT_LENGTH);
                if (Utilities.checkString(length))
                    atts.addAttribute("", Node.Name.BYTE_LENGTH, Node.Name.BYTE_LENGTH, Node.Type.CDATA, length);
            }
            handler.startElement(Framework.SDXNamespaceURI, Node.Name.DOCUMENT, Framework.SDXNamespacePrefix + ":" + Node.Name.DOCUMENT, atts);
            handler.endElement(Framework.SDXNamespaceURI, Node.Name.DOCUMENT, Framework.SDXNamespacePrefix + ":" + Node.Name.DOCUMENT);
        }

    }

    /** Providing access to the id generator as this maybe be useful when building documents externally*/
    public IDGenerator getIdGenerator() {
        return idGen;
    }

    protected boolean initToSax(){
        if(!super.initToSax())
            return false;
        else{
            this._xmlizable_objects.put("Indexation_Pipeline",this._indexationPipeline);
            if(this.defaultRepository != null)
                this._xmlizable_objects.put("Default_Repository",this.defaultRepository.getId());

            //this._xmlizable_objects.put(Utilities.getElementName(IDGenerator.CLASS_NAME_SUFFIX),this.idGen);
            this._xmlizable_objects.put("Is_default",String.valueOf(this.isDefault()));
            this._xmlizable_objects.put("OAI_Repository",this.oaiRepo); //TODO implement toSAX for OAIRepository
            this._xmlizable_objects.put("OAI_Harvester",this._oaiHarv); //TODO: implement toSAX for OAIHarvester
            if(idGen != null){
                this._xmlizable_objects.put("IDGenerator_Class",this.idGen.getClass().getName());
                this._xmlizable_objects.put("IDGenerator_Prefix",this.idGen.getPrefix());
                this._xmlizable_objects.put("IDGenerator_Suffix",this.idGen.getSuffix());
            }else{
                this._xmlizable_objects.put("IDGenerator_Class","none");
                this._xmlizable_objects.put("IDGenerator_Prefix","");
                this._xmlizable_objects.put("IDGenerator_Suffix","");
            }

            return true;
        }
    }

    /**Init the LinkedHashMap _xmlizable_volatile_objects with the objects in order to describ them in XML
     * Some objects need to be refresh each time a toSAX is called*/
    protected void initVolatileObjectsToSax() {
        super.initVolatileObjectsToSax();
        this._xmlizable_objects.put("Is_default",String.valueOf(this.isDefault()));
    }


    /** Save the DocumentBase data objects
     * @see fr.gouv.culture.sdx.utils.save.Saveable#backup(fr.gouv.culture.sdx.utils.save.SaveParameters)
     */
    public void backup(SaveParameters save_config) throws SDXException {
        if(save_config != null)
            if(save_config.isAllElementSelected())
            {
                // add the DocumentBase id
                save_config.setAttribute(Node.Name.ID,this.getId());

                //add the oai harvester timestamp
                if(this._oaiHarv instanceof DocumentBaseOAIHarvester){
                    SaveParameters saveparams = new SaveParameters(DocumentBase.ConfigurationNode.OAI_HARVESTER,save_config);
                    ((DocumentBaseOAIHarvester)this._oaiHarv).backup(saveparams);
                }
            }
    }
    /** restore the DocumentBase data objects
     * @see fr.gouv.culture.sdx.utils.save.Saveable#restore(fr.gouv.culture.sdx.utils.save.SaveParameters)
     */
    public void restore(SaveParameters save_config) throws SDXException {
        if(save_config != null)
            if(save_config.isAllElementSelected())
            {
                //check the document base directory
                File db_dir = new File(save_config.getStoreCompletePath());
                if(!db_dir.exists())
                    throw new SDXException(db_dir.getAbsolutePath() + " not found.");

                //restore the oai harvester timestamp
                if(this._oaiHarv instanceof DocumentBaseOAIHarvester){
                    SaveParameters saveparams = (SaveParameters)save_config.getChild(DocumentBase.ConfigurationNode.OAI_HARVESTER);
                    ((DocumentBaseOAIHarvester)this._oaiHarv).restore(saveparams);
                }
            }
    }

    /**Returns the source validity of the document base.
     * The source validity is used by the cache of Cocoon.
     * @return	DocumentBaseSourceValidity
     */
    public SourceValidity getSourceValidity() {
    	return new DocumentBaseSourceValidity(this);
    }
}
