org.silverpeas.attachment.repository.DocumentRepository.java Source code

Java tutorial

Introduction

Here is the source code for org.silverpeas.attachment.repository.DocumentRepository.java

Source

/*
 * Copyright (C) 2000 - 2013 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection withWriter Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception.  You should have recieved a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "http://www.silverpeas.org/docs/core/legal/floss_exception.html"
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.silverpeas.attachment.repository;

import com.silverpeas.jcrutil.BasicDaoFactory;
import com.silverpeas.util.FileUtil;
import com.silverpeas.util.StringUtil;
import com.silverpeas.util.i18n.I18NHelper;
import com.stratelia.silverpeas.silvertrace.SilverTrace;
import com.stratelia.webactiv.util.DateUtil;
import com.stratelia.webactiv.util.FileRepositoryManager;
import com.stratelia.webactiv.util.WAPrimaryKey;
import org.apache.commons.io.FileUtils;
import org.silverpeas.attachment.model.DocumentType;
import org.silverpeas.attachment.model.HistorisedDocument;
import org.silverpeas.attachment.model.SimpleAttachment;
import org.silverpeas.attachment.model.SimpleDocument;
import org.silverpeas.attachment.model.SimpleDocumentPK;
import org.silverpeas.attachment.model.SimpleDocumentVersion;
import org.silverpeas.attachment.util.SimpleDocumentList;
import org.silverpeas.util.jcr.NodeIterable;
import org.silverpeas.util.jcr.PropertyIterable;

import javax.inject.Named;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;
import javax.jcr.query.qom.ChildNode;
import javax.jcr.query.qom.Comparison;
import javax.jcr.query.qom.DescendantNode;
import javax.jcr.query.qom.Ordering;
import javax.jcr.query.qom.QueryObjectModel;
import javax.jcr.query.qom.QueryObjectModelFactory;
import javax.jcr.query.qom.Selector;
import javax.jcr.version.Version;
import javax.jcr.version.VersionHistory;
import javax.jcr.version.VersionIterator;
import javax.jcr.version.VersionManager;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static com.silverpeas.jcrutil.JcrConstants.*;
import static javax.jcr.nodetype.NodeType.MIX_SIMPLE_VERSIONABLE;

/**
 *
 * @author ehugonnet
 */
@Named("documentRepository")
public class DocumentRepository {

    private static final String SIMPLE_DOCUMENT_ALIAS = "SimpleDocuments";
    final DocumentConverter converter = new DocumentConverter();

    public void prepareComponentAttachments(String instanceId, String folder) throws RepositoryException {
        Session session = BasicDaoFactory.getSystemSession();
        try {
            prepareComponentAttachments(session, instanceId, folder);
            session.save();
        } finally {
            BasicDaoFactory.logout(session);
        }
    }

    protected Node prepareComponentAttachments(Session session, String instanceId, String folder)
            throws RepositoryException {
        Node targetInstanceNode = converter.getFolder(session.getRootNode(), instanceId);
        return converter.getFolder(targetInstanceNode, folder);
    }

    /**
     * Create file attached to an object who is identified by "PK" SimpleDocument object contains an
     * attribute who identifie the link by a foreign key.
     *
     * @param session
     * @param document
     * @return
     * @throws RepositoryException
     */
    public SimpleDocumentPK createDocument(Session session, SimpleDocument document) throws RepositoryException {
        SimpleDocument last = findLast(session, document.getInstanceId(), document.getForeignId());
        if ((null != last) && (0 >= document.getOrder())) {
            document.setOrder(last.getOrder() + 1);
        }
        Node docsNode = prepareComponentAttachments(session, document.getInstanceId(), document.getFolder());
        Node documentNode = docsNode.addNode(document.computeNodeName(), SLV_SIMPLE_DOCUMENT);
        document.setUpdatedBy(document.getCreatedBy());
        document.setUpdated(document.getCreated());
        converter.fillNode(document, documentNode);
        if (document.isVersioned()) {
            documentNode.addMixin(MIX_SIMPLE_VERSIONABLE);
        }
        document.setId(documentNode.getIdentifier());
        document.setOldSilverpeasId(documentNode.getProperty(SLV_PROPERTY_OLD_ID).getLong());
        return document.getPk();
    }

    /**
     * Move the document to another attached object.
     *
     * @param session
     * @param document
     * @param destination
     * @return
     * @throws RepositoryException
     */
    public SimpleDocumentPK moveDocument(Session session, SimpleDocument document, WAPrimaryKey destination)
            throws RepositoryException {
        SimpleDocument targetDoc = new SimpleDocument();
        SimpleDocumentPK pk = new SimpleDocumentPK(null, destination.getInstanceId());
        pk.setOldSilverpeasId(document.getOldSilverpeasId());
        targetDoc.setPK(pk);
        targetDoc.setDocumentType(document.getDocumentType());
        targetDoc.setNodeName(document.getNodeName());
        prepareComponentAttachments(session, destination.getInstanceId(), document.getFolder());
        session.save();
        Node originDocumentNode = session.getNodeByIdentifier(document.getPk().getId());
        if (!originDocumentNode.getPath().equals(targetDoc.getFullJcrPath())) {
            session.getWorkspace().move(originDocumentNode.getPath(), targetDoc.getFullJcrPath());
        }
        VersionManager versionManager = session.getWorkspace().getVersionManager();
        Node targetDocumentNode = session.getNode(targetDoc.getFullJcrPath());
        boolean mustCheckInVersion = true;
        if (converter.isVersionedMaster(targetDocumentNode)) {
            if (!targetDocumentNode.isCheckedOut()) {
                versionManager.checkout(targetDocumentNode.getPath());
            } else {
                mustCheckInVersion = false;
            }
        }
        converter.addStringProperty(targetDocumentNode, SLV_PROPERTY_FOREIGN_KEY, destination.getId());
        converter.addStringProperty(targetDocumentNode, SLV_PROPERTY_INSTANCEID, destination.getInstanceId());
        if (converter.isVersionedMaster(targetDocumentNode) && targetDocumentNode.isCheckedOut()) {
            session.save();
            if (mustCheckInVersion) {
                versionManager.checkin(targetDocumentNode.getPath());
            }
        }
        pk.setId(targetDocumentNode.getIdentifier());
        return pk;
    }

    /**
     * Copy the document to another attached object.
     *
     * @param session
     * @param document
     * @param destination the foreingId holding reference to the copy.
     * @return
     * @throws RepositoryException
     */
    public SimpleDocumentPK copyDocument(Session session, SimpleDocument document, WAPrimaryKey destination)
            throws RepositoryException {
        prepareComponentAttachments(destination.getInstanceId(), document.getFolder());
        SimpleDocumentPK pk = new SimpleDocumentPK(null, destination.getInstanceId());
        SimpleDocument targetDoc;
        if (document.isVersioned() && document.getDocumentType() == DocumentType.attachment) {
            targetDoc = new HistorisedDocument();
        } else {
            targetDoc = new SimpleDocument();
        }
        targetDoc.setNodeName(null);
        targetDoc.setPK(pk);
        targetDoc.setDocumentType(document.getDocumentType());
        targetDoc.setForeignId(destination.getId());
        targetDoc.computeNodeName();
        Node originDocumentNode = session.getNodeByIdentifier(document.getPk().getId());
        session.getWorkspace().copy(originDocumentNode.getPath(), targetDoc.getFullJcrPath());
        Node copy = session.getNode(targetDoc.getFullJcrPath());
        copy.setProperty(SLV_PROPERTY_OLD_ID, targetDoc.getOldSilverpeasId());
        copy.setProperty(SLV_PROPERTY_FOREIGN_KEY, destination.getId());
        copy.setProperty(SLV_PROPERTY_INSTANCEID, destination.getInstanceId());
        // The reservation is not copied.
        targetDoc = converter.fillDocument(copy, null);
        targetDoc.release();
        converter.fillNode(targetDoc, copy);
        return targetDoc.getPk();
    }

    /**
     * Copy the document to another attached object.
     *
     * @param session
     * @param document
     * @param destination the foreingId holding reference to the copy.
     * @return
     * @throws RepositoryException
     */
    public SimpleDocumentPK copyDocument(Session session, HistorisedDocument document, WAPrimaryKey destination)
            throws RepositoryException, IOException {
        prepareComponentAttachments(destination.getInstanceId(), document.getFolder());
        SimpleDocumentPK pk = new SimpleDocumentPK(null, destination.getInstanceId());
        List<SimpleDocument> history = new ArrayList<SimpleDocument>(document.getHistory());
        Collections.reverse(history);
        history.add(document);
        SimpleDocument targetDoc = new HistorisedDocument(history.remove(0));
        targetDoc.setNodeName(null);
        targetDoc.setPK(pk);
        targetDoc.setDocumentType(document.getDocumentType());
        targetDoc.setForeignId(destination.getId());
        targetDoc.setUpdatedBy(null);
        targetDoc.computeNodeName();
        pk = createDocument(session, targetDoc);
        if (I18NHelper.isI18nActivated()) {
            // The first version can have several language contents.
            Set<String> checkedLanguages = new HashSet<String>();
            checkedLanguages.add(targetDoc.getLanguage());
            for (String language : I18NHelper.getAllSupportedLanguages()) {
                if (!checkedLanguages.contains(language)) {
                    HistorisedDocument temp = (HistorisedDocument) findDocumentById(session, document.getPk(),
                            language);
                    List<SimpleDocumentVersion> versions = temp.getHistory();
                    if (!versions.isEmpty()) {
                        SimpleDocumentVersion firstVersion = versions.get(versions.size() - 1);
                        if (!checkedLanguages.contains(firstVersion.getLanguage())) {
                            addContent(session, targetDoc.getPk(), firstVersion.getAttachment());
                        }
                    }
                    checkedLanguages.add(language);
                }
            }
        }
        unlock(session, targetDoc, false);
        VersionManager versionManager = session.getWorkspace().getVersionManager();
        String currentVersion = targetDoc.getVersion();
        for (SimpleDocument doc : history) {
            if (I18NHelper.isI18nActivated()) {
                // One language content is aimed by a version. So the first step here is to search the
                // language content updated.
                Set<String> checkedLanguages = new HashSet<String>();
                checkedLanguages.add(doc.getLanguage());
                for (String language : I18NHelper.getAllSupportedLanguages()) {
                    if (!checkedLanguages.contains(language)) {
                        SimpleDocument temp = findDocumentById(session, doc.getPk(), language);
                        if (temp != null && !checkedLanguages.contains(temp.getLanguage())
                                && temp.getUpdated().after(doc.getUpdated())) {
                            doc = temp;
                        }
                        checkedLanguages.add(language);
                    }
                }
            }
            HistorisedDocument targetHistorisedDoc = new HistorisedDocument(doc);
            targetHistorisedDoc.setPK(pk);
            targetHistorisedDoc.setForeignId(destination.getId());
            targetHistorisedDoc.setNodeName(targetDoc.getNodeName());
            // The reservation is not copied.
            targetHistorisedDoc.release();
            Node masterDocumentNode = session.getNodeByIdentifier(pk.getId());
            if (!currentVersion.equals(doc.getVersion())) {
                // In this case, a functional version is performed, so the common tools are used
                lock(session, targetDoc, doc.getUpdatedBy());
                converter.fillNode(targetHistorisedDoc, masterDocumentNode);
                unlock(session, targetHistorisedDoc, false);
                currentVersion = targetHistorisedDoc.getVersion();
            } else {
                // In this case, the version is a technical one (setOrder for example) and technical
                // tools are used
                versionManager.checkout(masterDocumentNode.getPath());
                converter.fillNode(targetHistorisedDoc, masterDocumentNode);
                session.save();
                versionManager.checkin(masterDocumentNode.getPath());
            }
        }
        return pk;
    }

    /**
     * Create file attached to an object who is identified by "PK" SimpleDocument object contains an
     * attribute who identifie the link by a foreign key.
     *
     * @param session
     * @param document
     * @throws RepositoryException
     * @throws IOException
     */
    public void updateDocument(Session session, SimpleDocument document) throws RepositoryException, IOException {
        Node documentNode = session.getNodeByIdentifier(document.getPk().getId());
        if (StringUtil.isDefined(document.getEditedBy())) {
            document.setUpdatedBy(document.getEditedBy());
        }
        document.setUpdated(new Date());
        converter.fillNode(document, documentNode);
    }

    /**
     * Save the optional slv:forbiddenDownloadForRoles simple document property (MIXIN).
     * This saving works with versionable documents without changing the major and minor version.
     * This property is transverse between all versions.
     *
     * @param session
     * @param document
     * @throws RepositoryException
     */
    public void saveForbiddenDownloadForRoles(Session session, SimpleDocument document) throws RepositoryException {
        Node documentNode = session.getNodeByIdentifier(document.getVersionMaster().getPk().getId());
        boolean checkedin = !documentNode.isCheckedOut();
        if (checkedin) {
            session.getWorkspace().getVersionManager().checkout(documentNode.getPath());
        }

        // Optional downloadable mixin
        converter.setForbiddenDownloadForRolesOptionalNodeProperty(document, documentNode);

        if (checkedin) {
            session.save();
            session.getWorkspace().getVersionManager().checkin(documentNode.getPath());
        }
    }

    /**
     * Add the document's clone id to the document even if it is locked.
     *
     * @param session the JCR session.
     * @param original the original document to be cloned.
     * @param clone the cone of the original document.
     * @throws RepositoryException
     */
    public void setClone(Session session, SimpleDocument original, SimpleDocument clone)
            throws RepositoryException {
        Node documentNode = session.getNodeByIdentifier(clone.getId());
        boolean checkedin = !documentNode.isCheckedOut();
        if (checkedin) {
            session.getWorkspace().getVersionManager().checkout(documentNode.getPath());
        }
        documentNode.setProperty(SLV_PROPERTY_CLONE, original.getId());
        if (checkedin) {
            session.save();
            session.getWorkspace().getVersionManager().checkin(documentNode.getPath());
        }
    }

    /**
     * Update the document order. This is a unique operation since the order propery is not
     * versionable.
     *
     * @param session
     * @param document
     * @throws RepositoryException
     */
    public void setOrder(Session session, SimpleDocument document) throws RepositoryException {
        Node documentNode = session.getNodeByIdentifier(document.getPk().getId());
        boolean checkedin = !documentNode.isCheckedOut();
        if (checkedin) {
            session.getWorkspace().getVersionManager().checkout(documentNode.getPath());
        }
        documentNode.setProperty(SLV_PROPERTY_ORDER, document.getOrder());
        if (checkedin) {
            session.save();
            session.getWorkspace().getVersionManager().checkin(documentNode.getPath());
        }
    }

    /**
     * Delete a file attached to an object who is identified by "PK" SimpleDocument object contains an
     * attribute who identifie the link by a foreign key.
     *
     * @param session
     * @param documentPk
     * @throws RepositoryException
     */
    public void deleteDocument(Session session, SimpleDocumentPK documentPk) throws RepositoryException {
        try {
            Node documentNode = session.getNodeByIdentifier(documentPk.getId());
            deleteContent(documentNode, documentPk.getInstanceId());
            deleteDocumentNode(documentNode);
        } catch (ItemNotFoundException infex) {
            SilverTrace.info("attachment", "DocumentRepository.deleteDocument()", "", infex);
        }
    }

    /**
     * Change the management of versions of the document if the document is checkouted. If the
     * document is currently with version management, then all history is removed and the document
     * becomes a simple document with no more version management. If the document has no version
     * management then a new public version is created and the document becomes a document with a
     * version history management.
     *
     * @param session
     * @param documentPk the id of the document.
     * @param comment
     * @throws RepositoryException
     * @throws IOException
     */
    public SimpleDocumentPK changeVersionState(Session session, SimpleDocumentPK documentPk, String comment)
            throws RepositoryException, IOException {
        try {
            Node documentNode = session.getNodeByIdentifier(documentPk.getId());
            boolean versionedNode = documentNode.getParent() instanceof Version
                    || converter.isVersionedMaster(documentNode);
            Node parent = documentNode.getParent();
            if (parent instanceof Version) {
                Version selectedVersion = (Version) parent;
                VersionManager versionManager = documentNode.getSession().getWorkspace().getVersionManager();
                versionManager.restore(selectedVersion, true);
                documentNode = session
                        .getNodeByIdentifier(selectedVersion.getContainingHistory().getVersionableIdentifier());
            }
            if (!documentNode.isCheckedOut()) {
                checkoutNode(documentNode, null);
            }
            if (StringUtil.isDefined(comment)) {
                documentNode.setProperty(SLV_PROPERTY_COMMENT, comment);
            }
            SimpleDocument origin = converter.fillDocument(documentNode, I18NHelper.defaultLanguage);
            if (versionedNode) {
                removeHistory(documentNode);
                documentNode.removeMixin(MIX_SIMPLE_VERSIONABLE);
                documentNode.setProperty(SLV_PROPERTY_VERSIONED, false);
                documentNode.setProperty(SLV_PROPERTY_MAJOR, 0);
                documentNode.setProperty(SLV_PROPERTY_MINOR, 0);
                SimpleDocument target = converter.fillDocument(documentNode, I18NHelper.defaultLanguage);
                moveMultilangContent(origin, target);
                File currentDocumentDir = new File(target.getDirectoryPath(I18NHelper.defaultLanguage))
                        .getParentFile();
                File[] contents = currentDocumentDir.getParentFile().listFiles();
                for (File versionDirectory : contents) {
                    if (!versionDirectory.equals(currentDocumentDir)) {
                        FileUtils.deleteDirectory(versionDirectory);
                    }
                }
            } else {
                documentNode.setProperty(SLV_PROPERTY_VERSIONED, true);
                documentNode.setProperty(SLV_PROPERTY_MAJOR, 1);
                documentNode.setProperty(SLV_PROPERTY_MINOR, 0);
                documentNode.addMixin(MIX_SIMPLE_VERSIONABLE);
                SimpleDocument target = converter.fillDocument(documentNode, I18NHelper.defaultLanguage);
                VersionManager versionManager = documentNode.getSession().getWorkspace().getVersionManager();
                documentNode.getSession().save();
                moveMultilangContent(origin, target);
                versionManager.checkin(documentNode.getPath());
            }
            return new SimpleDocumentPK(documentNode.getIdentifier(), documentPk);
        } catch (ItemNotFoundException infex) {
            SilverTrace.info("attachment", "DocumentRepository.deleteDocument()", "", infex);
            return documentPk;
        }
    }

    private void deleteDocumentNode(Node documentNode) throws RepositoryException {
        if (null != documentNode) {
            if (converter.isVersionedMaster(documentNode)) {
                removeHistory(documentNode);
            }
            documentNode.remove();
        }
    }

    /**
     *
     * @param session
     * @param documentPk
     * @param lang
     * @return
     * @throws RepositoryException
     */
    public SimpleDocument findDocumentById(Session session, SimpleDocumentPK documentPk, String lang)
            throws RepositoryException {
        SimpleDocument document = null;
        try {
            Node documentNode = session.getNodeByIdentifier(documentPk.getId());
            document = converter.convertNode(documentNode, lang);
        } catch (ItemNotFoundException infex) {
            SilverTrace.info("attachment", "DocumentRepository.findDocumentById()", "", infex);
        }
        return document;
    }

    /**
     *
     * @param session
     * @param instanceId
     * @param oldSilverpeasId
     * @param versioned
     * @param lang
     * @return
     * @throws RepositoryException
     */
    public SimpleDocument findDocumentByOldSilverpeasId(Session session, String instanceId, long oldSilverpeasId,
            boolean versioned, String lang) throws RepositoryException {
        QueryManager manager = session.getWorkspace().getQueryManager();
        QueryObjectModelFactory factory = manager.getQOMFactory();
        Selector source = factory.selector(SLV_SIMPLE_DOCUMENT, SIMPLE_DOCUMENT_ALIAS);
        DescendantNode descendantdNodeConstraint = factory.descendantNode(SIMPLE_DOCUMENT_ALIAS,
                session.getRootNode().getPath() + instanceId);
        Comparison oldSilverpeasIdComparison = factory.comparison(
                factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_OLD_ID),
                QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO,
                factory.literal(session.getValueFactory().createValue(oldSilverpeasId)));
        Comparison versionedComparison = factory.comparison(
                factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_VERSIONED),
                QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO,
                factory.literal(session.getValueFactory().createValue(versioned)));

        QueryObjectModel query = factory.createQuery(source,
                factory.and(descendantdNodeConstraint, factory.and(oldSilverpeasIdComparison, versionedComparison)),
                null, null);
        QueryResult result = query.execute();
        NodeIterator iter = result.getNodes();
        if (iter.hasNext()) {
            return converter.convertNode(iter.nextNode(), lang);
        }
        return null;
    }

    /**
     * The last document in an instance with the specified foreignId.
     *
     * @param session the current JCR session.
     * @param instanceId the component id containing the documents.
     * @param foreignId the id of the container owning the documents.
     * @return the last document in an instance with the specified foreignId.
     * @throws RepositoryException
     */
    public SimpleDocument findLast(Session session, String instanceId, String foreignId)
            throws RepositoryException {
        NodeIterator iter = selectDocumentsByForeignIdAndType(session, instanceId, foreignId,
                DocumentType.attachment);
        while (iter.hasNext()) {
            Node node = iter.nextNode();
            if (!iter.hasNext()) {
                return converter.convertNode(node, I18NHelper.defaultLanguage);
            }
        }
        return null;
    }

    /**
     * Search all the documents of type attachment in an instance with the specified foreignId.
     *
     * @param session the current JCR session.
     * @param instanceId the component id containing the documents.
     * @param foreignId the id of the container owning the documents.
     * @param language the language in which the documents are required.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    public SimpleDocumentList<SimpleDocument> listDocumentsByForeignId(Session session, String instanceId,
            String foreignId, String language) throws RepositoryException {
        NodeIterator iter = selectDocumentsByForeignIdAndType(session, instanceId, foreignId,
                DocumentType.attachment);
        return converter.convertNodeIterator(iter, language);
    }

    /**
     * Search all the documents of any type in an instance with the specified foreignId.
     *
     * @param session the current JCR session.
     * @param instanceId the component id containing the documents.
     * @param foreignId the id of the container owning the documents.
     * @param language the language in which the documents are required.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    public SimpleDocumentList<SimpleDocument> listAllDocumentsByForeignId(Session session, String instanceId,
            String foreignId, String language) throws RepositoryException {
        NodeIterator iter = selectDocumentsByForeignId(session, instanceId, foreignId);
        return converter.convertNodeIterator(iter, language);
    }

    /**
     * Search all the documents in an instance with the specified foreignId.
     *
     * @param session the current JCR session.
     * @param instanceId the component id containing the documents.
     * @param foreignId the id of the container owning the documents.
     * @param type thetype of required documents.
     * @param language the language in which the documents are required.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    public SimpleDocumentList<SimpleDocument> listDocumentsByForeignIdAndType(Session session, String instanceId,
            String foreignId, DocumentType type, String language) throws RepositoryException {
        NodeIterator iter = selectDocumentsByForeignIdAndType(session, instanceId, foreignId, type);
        return converter.convertNodeIterator(iter, language);
    }

    /**
     * Search all the documents in an instance with the specified type.
     *
     * @param session the current JCR session.
     * @param instanceId the component id containing the documents.
     * @param type thetype of required documents.
     * @param language the language in which the documents are required.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    public List<SimpleDocument> listDocumentsByComponentIdAndType(Session session, String instanceId,
            DocumentType type, String language) throws RepositoryException {
        NodeIterator iter = selectDocumentsByComponentIdAndType(session, instanceId, type);
        return converter.convertNodeIterator(iter, language);
    }

    /**
     * Search all the documents related to the component instance identified by the specified
     * identifier.
     *
     * @param session the current JCR session.
     * @param instanceId the component id containing the documents.
     * @param language the language in which the documents are required.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    public List<SimpleDocument> listAllDocumentsByComponentId(Session session, String instanceId, String language)
            throws RepositoryException {
        NodeIterator iter = selectAllDocumentsByComponentId(session, instanceId);
        return converter.convertNodeIterator(iter, language);
    }

    /**
     * Search all the documents in an instance with the specified owner.
     *
     * @param session the current JCR session.
     * @param instanceId the component id containing the documents.
     * @param owner the id of the user owning the document.
     * @param language the language in which the documents are required.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    public List<SimpleDocument> listComponentDocumentsByOwner(Session session, String instanceId, String owner,
            String language) throws RepositoryException {
        NodeIterator iter = selectDocumentsByOwnerIdAndComponentId(session, instanceId, owner);
        return converter.convertNodeIterator(iter, language);
    }

    public List<SimpleDocument> listDocumentsLockedByUser(Session session, String usedId, String language)
            throws RepositoryException {
        NodeIterator iter = selectAllDocumentsByOwnerId(session, usedId);
        return converter.convertNodeIterator(iter, language);
    }

    /**
     * Search all the documents in an instance with the specified foreignId.
     *
     * @param session the current JCR session.
     * @param instanceId the component id containing the documents.
     * @param foreignId the id of the container owning the documents.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    NodeIterator selectAllDocumentsByForeignId(Session session, String instanceId, String foreignId)
            throws RepositoryException {
        QueryManager manager = session.getWorkspace().getQueryManager();
        QueryObjectModelFactory factory = manager.getQOMFactory();
        Selector source = factory.selector(SLV_SIMPLE_DOCUMENT, SIMPLE_DOCUMENT_ALIAS);
        DescendantNode descendantdNodeConstraint = factory.descendantNode(SIMPLE_DOCUMENT_ALIAS,
                session.getRootNode().getPath() + instanceId);
        Comparison foreignIdComparison = factory.comparison(
                factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_FOREIGN_KEY),
                QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO,
                factory.literal(session.getValueFactory().createValue(foreignId)));
        Ordering order = factory.ascending(factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_ORDER));
        QueryObjectModel query = factory.createQuery(source,
                factory.and(descendantdNodeConstraint, foreignIdComparison), new Ordering[] { order }, null);
        QueryResult result = query.execute();
        return result.getNodes();
    }

    /**
     * Search all the documents of the specified type in an instance with the specified foreignId.
     *
     * @param session the current JCR session.
     * @param instanceId the component id containing the documents.
     * @param foreignId the id of the container owning the documents.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    NodeIterator selectDocumentsByForeignIdAndType(Session session, String instanceId, String foreignId,
            DocumentType type) throws RepositoryException {
        QueryManager manager = session.getWorkspace().getQueryManager();
        QueryObjectModelFactory factory = manager.getQOMFactory();
        Selector source = factory.selector(SLV_SIMPLE_DOCUMENT, SIMPLE_DOCUMENT_ALIAS);
        ChildNode childNodeConstraint = factory.childNode(SIMPLE_DOCUMENT_ALIAS,
                session.getRootNode().getPath() + instanceId + '/' + type.getFolderName());
        Comparison foreignIdComparison = factory.comparison(
                factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_FOREIGN_KEY),
                QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO,
                factory.literal(session.getValueFactory().createValue(foreignId)));
        Ordering order = factory.ascending(factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_ORDER));
        QueryObjectModel query = factory.createQuery(source, factory.and(childNodeConstraint, foreignIdComparison),
                new Ordering[] { order }, null);
        QueryResult result = query.execute();
        return result.getNodes();
    }

    /**
     * Search all the documents of tany type in an instance with the specified foreignId.
     *
     * @param session the current JCR session.
     * @param instanceId the component id containing the documents.
     * @param foreignId the id of the container owning the documents.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    NodeIterator selectDocumentsByForeignId(Session session, String instanceId, String foreignId)
            throws RepositoryException {
        QueryManager manager = session.getWorkspace().getQueryManager();
        QueryObjectModelFactory factory = manager.getQOMFactory();
        Selector source = factory.selector(SLV_SIMPLE_DOCUMENT, SIMPLE_DOCUMENT_ALIAS);
        DescendantNode descendantNodeConstraint = factory.descendantNode(SIMPLE_DOCUMENT_ALIAS,
                session.getRootNode().getPath() + instanceId + '/');
        Comparison foreignIdComparison = factory.comparison(
                factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_FOREIGN_KEY),
                QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO,
                factory.literal(session.getValueFactory().createValue(foreignId)));
        Ordering order = factory.ascending(factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_ORDER));
        QueryObjectModel query = factory.createQuery(source,
                factory.and(descendantNodeConstraint, foreignIdComparison), new Ordering[] { order }, null);
        QueryResult result = query.execute();
        return result.getNodes();
    }

    /**
     * Search all the documents of the specified type in an instance with the specified foreignId.
     *
     * @param session the current JCR session.
     * @param instanceId the component id containing the documents.
     * @param type the type of document.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    NodeIterator selectDocumentsByComponentIdAndType(Session session, String instanceId, DocumentType type)
            throws RepositoryException {
        QueryManager manager = session.getWorkspace().getQueryManager();
        QueryObjectModelFactory factory = manager.getQOMFactory();
        Selector source = factory.selector(SLV_SIMPLE_DOCUMENT, SIMPLE_DOCUMENT_ALIAS);
        ChildNode childNodeConstraint = factory.childNode(SIMPLE_DOCUMENT_ALIAS,
                session.getRootNode().getPath() + instanceId + '/' + type.getFolderName());
        Ordering order = factory.ascending(factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_ORDER));
        QueryObjectModel query = factory.createQuery(source, childNodeConstraint, new Ordering[] { order }, null);
        QueryResult result = query.execute();
        return result.getNodes();
    }

    /**
     * Search all the documents related to the component instance identified by the specified
     * identifier.
     * @param session the current JCR session.
     * @param instanceId the component id containing the documents.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    NodeIterator selectAllDocumentsByComponentId(Session session, String instanceId) throws RepositoryException {
        QueryManager manager = session.getWorkspace().getQueryManager();
        QueryObjectModelFactory factory = manager.getQOMFactory();
        Selector source = factory.selector(SLV_SIMPLE_DOCUMENT, SIMPLE_DOCUMENT_ALIAS);
        DescendantNode childNodeConstraint = factory.descendantNode(SIMPLE_DOCUMENT_ALIAS,
                session.getRootNode().getPath() + instanceId);
        Ordering order = factory.ascending(factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_ORDER));
        QueryObjectModel query = factory.createQuery(source, childNodeConstraint, new Ordering[] { order }, null);
        QueryResult result = query.execute();
        return result.getNodes();
    }

    /**
     * Search all the documents in an instance which are expiring at the specified date.
     *
     * @param session the current JCR session.
     * @param expiryDate the date when the document reservation should expire.
     * @param language the language in which the documents are required.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    public List<SimpleDocument> listExpiringDocuments(Session session, Date expiryDate, String language)
            throws RepositoryException {
        NodeIterator iter = selectExpiringDocuments(session, DateUtil.getBeginOfDay(expiryDate));
        return converter.convertNodeIterator(iter, language);
    }

    /**
     * Search all the documents in an instance which are locked at the alert date.
     *
     * @param session the current JCR session.
     * @param alertDate the date when the document reservation should send an alert.
     * @param language the language in which the documents are required.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    public List<SimpleDocument> listDocumentsRequiringWarning(Session session, Date alertDate, String language)
            throws RepositoryException {
        NodeIterator iter = selectWarningDocuments(session, DateUtil.getBeginOfDay(alertDate));
        return converter.convertNodeIterator(iter, language);
    }

    /**
     * Search all the documents in an instance expiring at the specified date.
     *
     * @param session the current JCR session.
     * @param expiryDate the date when the document reservation should expire.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    NodeIterator selectExpiringDocuments(Session session, Date expiryDate) throws RepositoryException {
        QueryManager manager = session.getWorkspace().getQueryManager();
        QueryObjectModelFactory factory = manager.getQOMFactory();
        Calendar expiry = Calendar.getInstance();
        expiry.setTime(DateUtil.getBeginOfDay(expiryDate));
        Selector source = factory.selector(SLV_SIMPLE_DOCUMENT, SIMPLE_DOCUMENT_ALIAS);
        Comparison foreignIdComparison = factory.comparison(
                factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_EXPIRY_DATE),
                QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO,
                factory.literal(session.getValueFactory().createValue(expiry)));
        Ordering order = factory.ascending(factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_ORDER));
        QueryObjectModel query = factory.createQuery(source, foreignIdComparison, new Ordering[] { order }, null);
        QueryResult result = query.execute();
        return result.getNodes();
    }

    /**
     * Search all the documents in an instance requiring to be unlocked at the specified date.
     *
     * @param session the current JCR session.
     * @param expiryDate the date when the document reservation should expire.
     * @param language the language in which the documents are required.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    public List<SimpleDocument> listDocumentsToUnlock(Session session, Date expiryDate, String language)
            throws RepositoryException {
        NodeIterator iter = selectDocumentsRequiringUnlocking(session, expiryDate);
        return converter.convertNodeIterator(iter, language);
    }

    /**
     * Search all the documents in an instance requiring to be unlocked at the specified date.
     *
     * @param session the current JCR session.
     * @param expiryDate the date when the document reservation should expire.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    NodeIterator selectDocumentsRequiringUnlocking(Session session, Date expiryDate) throws RepositoryException {
        QueryManager manager = session.getWorkspace().getQueryManager();
        QueryObjectModelFactory factory = manager.getQOMFactory();
        Calendar expiry = Calendar.getInstance();
        expiry.setTime(DateUtil.getBeginOfDay(expiryDate));
        Selector source = factory.selector(SLV_SIMPLE_DOCUMENT, SIMPLE_DOCUMENT_ALIAS);
        Comparison foreignIdComparison = factory.comparison(
                factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_EXPIRY_DATE),
                QueryObjectModelFactory.JCR_OPERATOR_LESS_THAN,
                factory.literal(session.getValueFactory().createValue(expiry)));
        Ordering order = factory.ascending(factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_ORDER));
        QueryObjectModel query = factory.createQuery(source, foreignIdComparison, new Ordering[] { order }, null);
        QueryResult result = query.execute();
        return result.getNodes();
    }

    /**
     * Search all the documents in an instance in a warning state at the specified date.
     *
     * @param session the current JCR session.
     * @param alertDate the date when a warning is required.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    NodeIterator selectWarningDocuments(Session session, Date alertDate) throws RepositoryException {
        QueryManager manager = session.getWorkspace().getQueryManager();
        QueryObjectModelFactory factory = manager.getQOMFactory();
        Calendar alert = Calendar.getInstance();
        alert.setTime(DateUtil.getBeginOfDay(alertDate));
        Selector source = factory.selector(SLV_SIMPLE_DOCUMENT, SIMPLE_DOCUMENT_ALIAS);
        Comparison foreignIdComparison = factory.comparison(
                factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_ALERT_DATE),
                QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO,
                factory.literal(session.getValueFactory().createValue(alert)));
        Ordering order = factory.ascending(factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_ORDER));
        QueryObjectModel query = factory.createQuery(source, foreignIdComparison, new Ordering[] { order }, null);
        QueryResult result = query.execute();
        return result.getNodes();
    }

    /**
     * Search all the documents in an instance with the specified owner.
     *
     * @param session the current JCR session.
     * @param instanceId the component id containing the documents.
     * @param owner the id of the user owning the documents.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    NodeIterator selectDocumentsByOwnerIdAndComponentId(Session session, String instanceId, String owner)
            throws RepositoryException {
        QueryManager manager = session.getWorkspace().getQueryManager();
        QueryObjectModelFactory factory = manager.getQOMFactory();
        Selector source = factory.selector(SLV_SIMPLE_DOCUMENT, SIMPLE_DOCUMENT_ALIAS);
        ChildNode childNodeConstraint = factory.childNode(SIMPLE_DOCUMENT_ALIAS,
                session.getRootNode().getPath() + instanceId + '/' + DocumentType.attachment.getFolderName());
        Comparison ownerComparison = factory.comparison(
                factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_OWNER),
                QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO,
                factory.literal(session.getValueFactory().createValue(owner)));
        Ordering order = factory.ascending(factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_ORDER));
        QueryObjectModel query = factory.createQuery(source, factory.and(childNodeConstraint, ownerComparison),
                new Ordering[] { order }, null);
        QueryResult result = query.execute();
        return result.getNodes();
    }

    /**
     * Search all the documents with the specified owner.
     *
     * @param session the current JCR session.
     * @param owner the id of the user owning the documents.
     * @return an ordered list of the documents.
     * @throws RepositoryException
     */
    NodeIterator selectAllDocumentsByOwnerId(Session session, String owner) throws RepositoryException {
        QueryManager manager = session.getWorkspace().getQueryManager();
        QueryObjectModelFactory factory = manager.getQOMFactory();
        Selector source = factory.selector(SLV_SIMPLE_DOCUMENT, SIMPLE_DOCUMENT_ALIAS);
        Comparison ownerComparison = factory.comparison(
                factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_OWNER),
                QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO,
                factory.literal(session.getValueFactory().createValue(owner)));
        Ordering order = factory.ascending(factory.propertyValue(SIMPLE_DOCUMENT_ALIAS, SLV_PROPERTY_ORDER));
        QueryObjectModel query = factory.createQuery(source, ownerComparison, new Ordering[] { order }, null);
        QueryResult result = query.execute();
        return result.getNodes();
    }

    /**
     * Add the content.
     *
     * @param session the current JCR session.
     * @param documentPk the document which content is to be added.
     * @param attachment the attachment metadata.
     * @throws RepositoryException
     */
    public void addContent(Session session, SimpleDocumentPK documentPk, SimpleAttachment attachment)
            throws RepositoryException {
        Node documentNode = session.getNodeByIdentifier(documentPk.getId());
        if (converter.isVersionedMaster(documentNode) && !documentNode.isCheckedOut()) {
            String owner = attachment.getUpdatedBy();
            if (!StringUtil.isDefined(owner)) {
                owner = attachment.getCreatedBy();
            }
            checkoutNode(documentNode, owner);
        }
        converter.addAttachment(documentNode, attachment);
    }

    /**
     * Get the content.
     *
     * @param session the current JCR session.
     * @param pk the document which content is to be added.
     * @param lang the content language.
     * @return the attachment binary content.
     * @throws RepositoryException
     * @throws IOException
     */
    public InputStream getContent(Session session, SimpleDocumentPK pk, String lang)
            throws RepositoryException, IOException {
        Node docNode = session.getNodeByIdentifier(pk.getId());
        String language = lang;
        if (!StringUtil.isDefined(language)) {
            language = I18NHelper.defaultLanguage;
        }
        SimpleDocument document = converter.fillDocument(docNode, language);
        return new BufferedInputStream(FileUtils.openInputStream(new File(document.getAttachmentPath())));
    }

    /**
     * Remove the content for the specified language.
     * If no other content exists, then the document node is deleted.
     *
     * @param session the current JCR session.
     * @param documentPk the document which content is to be removed.
     * @param language the language of the content which is to be removed.
     * @return false if the document has no child node after the content remove, true otherwise.
     * @throws RepositoryException
     */
    public boolean removeContent(Session session, SimpleDocumentPK documentPk, String language)
            throws RepositoryException {
        Node documentNode = session.getNodeByIdentifier(documentPk.getId());
        if (converter.isVersionedMaster(documentNode) && !documentNode.isCheckedOut()) {
            checkoutNode(documentNode, null);
        }
        converter.removeAttachment(documentNode, language);
        documentNode = session.getNodeByIdentifier(documentPk.getId());
        boolean existsOtherContents = documentNode.hasNodes();
        if (!existsOtherContents) {
            deleteDocumentNode(documentNode);
        }
        return existsOtherContents;
    }

    /**
     * Lock a document if it is versionned to create a new work in progress version.
     *
     * @param session
     * @param document
     * @param owner the user locking the node.
     * @return true if node has be checked out - false otherwise.
     * @throws RepositoryException
     */
    public boolean lock(Session session, SimpleDocument document, String owner) throws RepositoryException {
        if (document.isVersioned()) {
            Node documentNode = session.getNodeByIdentifier(document.getId());
            if (!documentNode.isCheckedOut()) {
                checkoutNode(documentNode, owner);
            }
            return true;
        }
        return false;
    }

    /**
     * Unlock a document if it is versionned to create a new version or to restore a previous one.
     * By using this method, the metadata of the content are always updated.
     *
     * @param session the current JCR open session to perform actions.
     * @param document the document data from which all needed identifiers are retrieved.
     * @param restore true to restore the previous version if any.
     * @return the result of {@link #unlock(Session, SimpleDocument, boolean, boolean)} execution.
     * @throws RepositoryException
     */
    public SimpleDocument unlock(Session session, SimpleDocument document, boolean restore)
            throws RepositoryException {
        return unlock(session, document, restore, false);
    }

    /**
     * Unlock a document if it is versionned from a context into which a language content has just
     * been deleted. This method does not update the metadata of the content in order to obtain an
     * efficient content deletion.
     *
     * @param session the current JCR open session to perform actions.
     * @param document the document data from which all needed identifiers are retrieved.
     * @return the result of {@link #unlock(Session, SimpleDocument, boolean, boolean)} execution.
     * @throws RepositoryException
     */
    public SimpleDocument unlockFromContentDeletion(Session session, SimpleDocument document)
            throws RepositoryException {
        return unlock(session, document, false, true);
    }

    /**
     * Unlock a document if it is versionned to create a new version or to restore a previous one.
     *
     * @param session the current JCR open session to perform actions.
     * @param document the document data from which all needed identifiers are retrieved.
     * @param restore true to restore the previous version if any.
     * @param skipContentMetadataUpdate false to update the metadata of the content {@link
     * SimpleDocument#getAttachment()}.
     * @return the document updated.
     * @throws RepositoryException
     */
    private SimpleDocument unlock(Session session, SimpleDocument document, boolean restore,
            boolean skipContentMetadataUpdate) throws RepositoryException {
        Node documentNode;
        try {
            documentNode = session.getNodeByIdentifier(document.getId());
        } catch (ItemNotFoundException ex) {
            //Node may have been deleted after removing all its content.
            return document;
        }
        if (document.isVersioned() && documentNode.isCheckedOut()) {
            if (restore) {
                VersionIterator iter = session.getWorkspace().getVersionManager()
                        .getVersionHistory(document.getFullJcrPath()).getAllVersions();
                Version lastVersion = null;
                while (iter.hasNext()) {
                    lastVersion = iter.nextVersion();
                }
                if (null != lastVersion) {
                    session.getWorkspace().getVersionManager().restore(lastVersion, true);
                    return converter.convertNode(lastVersion.getFrozenNode(), document.getLanguage());
                }
            }
            converter.fillNode(document, documentNode, skipContentMetadataUpdate);
            return checkinNode(documentNode, document.getLanguage(), document.isPublic());
        }
        if (!document.isVersioned()) {
            converter.fillNode(document, documentNode, skipContentMetadataUpdate);
            converter.releaseDocumentNode(documentNode, document.getLanguage());
            return converter.convertNode(documentNode, document.getLanguage());
        }
        document.release();
        return document;
    }

    /**
     * Check the document out.
     *
     * @param node the node to checkout.
     * @param owner the user checkouting the node.
     * @throws RepositoryException
     */
    void checkoutNode(Node node, String owner) throws RepositoryException {
        node.getSession().getWorkspace().getVersionManager().checkout(node.getPath());
        converter.addStringProperty(node, SLV_PROPERTY_OWNER, owner);
    }

    /**
     * Check the document in.
     *
     * @param documentNode the node to checkin.
     * @param isMajor true if the new version is a major one - false otherwise.
     * @return the document for this new version.
     * @throws RepositoryException
     */
    SimpleDocument checkinNode(Node documentNode, String lang, boolean isMajor) throws RepositoryException {
        VersionManager versionManager = documentNode.getSession().getWorkspace().getVersionManager();
        String versionLabel = converter.updateVersion(documentNode, lang, isMajor);
        documentNode.getSession().save();
        Version lastVersion = versionManager.checkin(documentNode.getPath());
        lastVersion.getContainingHistory().addVersionLabel(lastVersion.getName(), versionLabel, false);
        SimpleDocument doc = converter.convertNode(documentNode, lang);
        return doc;
    }

    /**
     * Add the version feature to an existing document. If the document has already the version
     * feature, nothing is done.
     *
     * @param session
     * @param documentPk
     * @throws RepositoryException
     */
    public void setVersionnable(Session session, SimpleDocumentPK documentPk) throws RepositoryException {
        Node documentNode = session.getNodeByIdentifier(documentPk.getId());
        if (!converter.isVersionedMaster(documentNode)) {
            documentNode.addMixin(MIX_SIMPLE_VERSIONABLE);
            documentNode.setProperty(SLV_PROPERTY_VERSIONED, true);
        }
    }

    /**
     * Remove the version feature to an existing document. If the document doesn't have already the
     * version feature, nothing is done.
     *
     * @param session
     * @param documentPk
     * @throws RepositoryException
     */
    public void removeVersionnable(Session session, SimpleDocumentPK documentPk) throws RepositoryException {
        Node documentNode = session.getNodeByIdentifier(documentPk.getId());
        if (converter.isVersionedMaster(documentNode)) {
            removeHistory(documentNode);
            VersionHistory history = documentNode.getSession().getWorkspace().getVersionManager()
                    .getVersionHistory(documentNode.getPath());
            history.remove();

            documentNode.removeMixin(MIX_SIMPLE_VERSIONABLE);
        }
        documentNode.setProperty(SLV_PROPERTY_VERSIONED, false);
    }

    void removeHistory(Node documentNode) throws RepositoryException {
        VersionHistory history = documentNode.getSession().getWorkspace().getVersionManager()
                .getVersionHistory(documentNode.getPath());
        Version root = history.getRootVersion();
        VersionIterator versions = history.getAllVersions();
        while (versions.hasNext()) {
            Version version = versions.nextVersion();
            if (!version.isSame(root)) {
                history.removeVersion(version.getName());
            }
        }
    }

    public void fillNodeName(Session session, SimpleDocument document) throws RepositoryException {
        Node documentNode = session.getNodeByIdentifier(document.getId());
        if (!StringUtil.isDefined(document.getNodeName())) {
            document.setNodeName(documentNode.getName());
        }
    }

    public long storeContent(SimpleDocument document, InputStream in, boolean update)
            throws RepositoryException, IOException {
        File file = new File(document.getAttachmentPath());
        SilverTrace.debug("attachment", "DocumentRepository",
                "Storing file for document in " + document.getAttachmentPath());
        if (update) {
            File parentFile = file.getParentFile();
            if (parentFile.isDirectory() && parentFile.list().length > 0) {
                FileUtils.deleteQuietly(parentFile);
                FileUtils.forceMkdir(parentFile);
            }
        }
        FileUtils.copyInputStreamToFile(in, file);
        return file.length();
    }

    public long storeContent(SimpleDocument document, InputStream in) throws RepositoryException, IOException {
        return storeContent(document, in, false);
    }

    public void duplicateContent(SimpleDocument origin, SimpleDocument document)
            throws IOException, RepositoryException {
        String originDir = origin.getDirectoryPath(null);
        String targetDir = document.getDirectoryPath(null);
        targetDir = targetDir.replace('/', File.separatorChar);
        File target = new File(targetDir).getParentFile();
        File source = new File(originDir).getParentFile();
        if (!source.exists() || !source.isDirectory() || source.listFiles() == null) {
            return;
        }
        if (!target.exists()) {
            target.mkdir();
        }
        for (File langDir : source.listFiles()) {
            File targetLangDir = new File(target, langDir.getName());
            if (!targetLangDir.exists()) {
                FileUtils.copyDirectory(langDir, targetLangDir);
            }
        }
    }

    public void deleteContent(Node documentNode, String instanceId) throws RepositoryException {
        String directory = FileRepositoryManager.getAbsolutePath(instanceId) + documentNode.getName();
        directory = directory.replace('/', File.separatorChar);
        File documentDirectory = new File(directory);
        if (documentDirectory.exists() && documentDirectory.isDirectory()) {
            FileUtils.deleteQuietly(documentDirectory);
        }
    }

    public void copyMultilangContent(SimpleDocument origin, SimpleDocument copy) throws IOException {
        String originDir = origin.getDirectoryPath(null);
        String targetDir = copy.getDirectoryPath(null);
        targetDir = targetDir.replace('/', File.separatorChar);
        File target = new File(targetDir).getParentFile();
        File source = new File(originDir).getParentFile();
        if (!source.exists() || !source.isDirectory() || source.listFiles() == null) {
            return;
        }
        if (target.exists()) {
            FileUtils.cleanDirectory(target);
        }
        FileUtils.copyDirectory(source, target);
    }

    public void copyFullContent(SimpleDocument origin, SimpleDocument copy) throws IOException {
        String originDir = origin.getDirectoryPath(null);
        String targetDir = copy.getDirectoryPath(null);
        targetDir = targetDir.replace('/', File.separatorChar);
        File target = new File(targetDir).getParentFile().getParentFile();
        File source = new File(originDir).getParentFile().getParentFile();
        if (!source.exists() || !source.isDirectory() || source.listFiles() == null) {
            return;
        }
        if (target.exists()) {
            FileUtils.cleanDirectory(target);
        }
        FileUtils.copyDirectory(source, target);
    }

    public void moveMultilangContent(SimpleDocument origin, SimpleDocument copy) throws IOException {
        String originDir = origin.getDirectoryPath(null);
        File source = new File(originDir).getParentFile();
        String targetDir = copy.getDirectoryPath(null);
        targetDir = targetDir.replace('/', File.separatorChar);
        File target = new File(targetDir).getParentFile();
        if (!source.exists() || !source.isDirectory() || source.listFiles() == null) {
            return;
        }
        if (!target.getParentFile().getName().equals(source.getParentFile().getName())) {
            source = source.getParentFile();
            target = target.getParentFile();
        }
        if (!source.equals(target)) {
            FileUtils.moveDirectory(source, target);
            FileUtil.deleteEmptyDir(source.getParentFile());
        }
    }

    public void moveFullContent(SimpleDocument origin, SimpleDocument copy) throws IOException {
        String originDir = origin.getDirectoryPath(null);
        File source = new File(originDir).getParentFile().getParentFile();
        String targetDir = copy.getDirectoryPath(null);
        targetDir = targetDir.replace('/', File.separatorChar);
        File target = new File(targetDir).getParentFile().getParentFile();
        if (!source.exists() || !source.isDirectory() || source.listFiles() == null) {
            return;
        }
        if (!source.equals(target)) {
            FileUtils.moveDirectory(source, target);
            FileUtil.deleteEmptyDir(source.getParentFile());
        }
    }

    public void mergeAttachment(Session session, SimpleDocument attachment, SimpleDocument clone)
            throws RepositoryException {
        Node originalNode = session.getNodeByIdentifier(attachment.getId());
        Set<String> existingAttachements = new HashSet<String>(I18NHelper.getNumberOfLanguages());
        for (Node child : new NodeIterable(originalNode.getNodes())) {
            existingAttachements.add(child.getName());
        }
        Node cloneNode = session.getNodeByIdentifier(clone.getId());
        for (Node child : new NodeIterable(cloneNode.getNodes())) {
            String childNodeName = child.getName();
            if (existingAttachements.contains(childNodeName) && originalNode.hasNode(childNodeName)) {
                copyNode(session, child, originalNode.getNode(childNodeName));
                existingAttachements.remove(childNodeName);
            } else {
                session.move(child.getPath(), originalNode.getPath() + '/' + childNodeName);
            }
        }
        for (String deletedNode : existingAttachements) {
            if (originalNode.hasNode(deletedNode)) {
                originalNode.getNode(deletedNode).remove();
            }
        }
        converter.addStringProperty(originalNode, SLV_PROPERTY_CLONE, null);
    }

    private void copyNode(Session session, Node source, Node target) throws RepositoryException {
        for (Node child : new NodeIterable(target.getNodes())) {
            if (!child.getDefinition().isProtected()) {
                child.remove();
            }
        }
        for (Node child : new NodeIterable(source.getNodes())) {
            session.move(child.getPath(), target.getPath() + '/' + child.getName());
        }
        for (Property property : new PropertyIterable(target.getProperties())) {
            if (!property.getDefinition().isProtected()) {
                property.remove();
            }
        }
        for (Property property : new PropertyIterable(source.getProperties())) {
            if (!property.getDefinition().isProtected()) {
                target.setProperty(property.getName(), property.getValue());
            }
        }
    }
}