org.opencms.db.generic.CmsHistoryDriver.java Source code

Java tutorial

Introduction

Here is the source code for org.opencms.db.generic.CmsHistoryDriver.java

Source

/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software GmbH, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.db.generic;

import org.opencms.configuration.CmsConfigurationManager;
import org.opencms.configuration.CmsParameterConfiguration;
import org.opencms.db.CmsDbConsistencyException;
import org.opencms.db.CmsDbContext;
import org.opencms.db.CmsDbEntryNotFoundException;
import org.opencms.db.CmsDbSqlException;
import org.opencms.db.CmsDriverManager;
import org.opencms.db.CmsResourceState;
import org.opencms.db.I_CmsDriver;
import org.opencms.db.I_CmsHistoryDriver;
import org.opencms.db.I_CmsVfsDriver;
import org.opencms.file.CmsDataAccessException;
import org.opencms.file.CmsFile;
import org.opencms.file.CmsFolder;
import org.opencms.file.CmsProject;
import org.opencms.file.CmsProperty;
import org.opencms.file.CmsPropertyDefinition;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsUser;
import org.opencms.file.CmsVfsResourceNotFoundException;
import org.opencms.file.history.CmsHistoryFile;
import org.opencms.file.history.CmsHistoryFolder;
import org.opencms.file.history.CmsHistoryPrincipal;
import org.opencms.file.history.CmsHistoryProject;
import org.opencms.file.history.I_CmsHistoryResource;
import org.opencms.main.CmsLog;
import org.opencms.security.CmsOrganizationalUnit;
import org.opencms.security.I_CmsPrincipal;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;

/**
 * Generic (ANSI-SQL) database server implementation of the history driver methods.<p>
 * 
 * @since 6.9.1
 */
public class CmsHistoryDriver implements I_CmsDriver, I_CmsHistoryDriver {

    /** The log object for this class. */
    private static final Log LOG = CmsLog.getLog(org.opencms.db.generic.CmsHistoryDriver.class);

    /** The driver manager instance. */
    protected CmsDriverManager m_driverManager;

    /** The SQL manager instance. */
    protected CmsSqlManager m_sqlManager;

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#createPropertyDefinition(org.opencms.db.CmsDbContext, java.lang.String, org.opencms.file.CmsPropertyDefinition.CmsPropertyType)
     */
    public CmsPropertyDefinition createPropertyDefinition(CmsDbContext dbc, String name,
            CmsPropertyDefinition.CmsPropertyType type) throws CmsDataAccessException {

        Connection conn = null;
        PreparedStatement stmt = null;

        try {
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTYDEF_CREATE_HISTORY");
            stmt.setString(1, new CmsUUID().toString());
            stmt.setString(2, name);
            stmt.setInt(3, type.getMode());
            stmt.executeUpdate();
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);

        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, null);
        }

        return readPropertyDefinition(dbc, name);
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#deleteEntries(CmsDbContext, I_CmsHistoryResource, int, long)
     */
    public int deleteEntries(CmsDbContext dbc, I_CmsHistoryResource resource, int versionsToKeep, long time)
            throws CmsDataAccessException {

        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet res = null;

        try {
            conn = m_sqlManager.getConnection(dbc);

            int maxVersion = -1;
            // get the maximal version number for this resource
            stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_MAXVER");
            stmt.setString(1, resource.getStructureId().toString());
            res = stmt.executeQuery();
            if (res.next()) {
                maxVersion = res.getInt(1);
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            } else {
                // make sure the connection is closed
                m_sqlManager.closeAll(dbc, conn, stmt, res);
                // nothing to delete
                internalCleanup(dbc, resource);
                return 0;
            }
            m_sqlManager.closeAll(dbc, conn, stmt, res);

            if (time >= 0) {
                int maxVersionByTime = -1;
                conn = m_sqlManager.getConnection(dbc);
                // get the maximal version to keep for this resource based on the time parameter
                stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_MAXVER_BYTIME");
                stmt.setString(1, resource.getStructureId().toString());
                stmt.setLong(2, time);
                res = stmt.executeQuery();
                if (res.next()) {
                    maxVersionByTime = res.getInt(1);
                    while (res.next()) {
                        // do nothing only move through all rows because of mssql odbc driver
                    }
                }
                m_sqlManager.closeAll(dbc, conn, stmt, res);
                if (maxVersionByTime > 0) {
                    if (versionsToKeep < 0) {
                        versionsToKeep = (maxVersion - maxVersionByTime);
                    } else {
                        versionsToKeep = Math.min(versionsToKeep, (maxVersion - maxVersionByTime));
                    }
                }
            }

            if ((maxVersion - versionsToKeep) <= 0) {
                // nothing to delete
                internalCleanup(dbc, resource);
                return 0;
            }

            // get the minimal structure publish tag to keep for this sibling
            int minStrPublishTagToKeep = -1;
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_READ_MAXTAG_FOR_VERSION");
            stmt.setString(1, resource.getStructureId().toString());
            stmt.setInt(2, (1 + maxVersion) - versionsToKeep);
            res = stmt.executeQuery();
            if (res.next()) {
                minStrPublishTagToKeep = res.getInt(1);
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            } else {
                // make sure the statement and the result is closed
                m_sqlManager.closeAll(dbc, conn, stmt, res);
                // nothing to delete
                internalCleanup(dbc, resource);
                return 0;
            }
            m_sqlManager.closeAll(dbc, conn, stmt, res);
            if (minStrPublishTagToKeep < 1) {
                // nothing to delete
                internalCleanup(dbc, resource);
                return 0;
            }
            minStrPublishTagToKeep++;

            // delete the properties
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTIES_HISTORY_DELETE");
            stmt.setString(1, resource.getStructureId().toString());
            stmt.setInt(2, minStrPublishTagToKeep);
            stmt.executeUpdate();
            m_sqlManager.closeAll(dbc, null, stmt, null);

            // delete the structure entries
            stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_DELETE");
            stmt.setString(1, resource.getStructureId().toString());
            stmt.setInt(2, minStrPublishTagToKeep);
            int structureVersions = stmt.executeUpdate();
            m_sqlManager.closeAll(dbc, null, stmt, null);

            // get the minimal resource publish tag to keep, 
            // all entries with publish tag less than this will be deleted
            int minResPublishTagToKeep = -1;
            stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_READ_MIN_USED_TAG");
            stmt.setString(1, resource.getResourceId().toString());
            res = stmt.executeQuery();
            if (res.next()) {
                minResPublishTagToKeep = res.getInt(1);
                if (res.wasNull()) {
                    // the database will return a row with a single NULL column if there are no rows at all for the given
                    // resource id. This means that we want to clean up all resource history and content history entries 
                    // for this resource id, and we achieve this by comparing their publish tag with the maximum integer.
                    minResPublishTagToKeep = Integer.MAX_VALUE;
                }
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            } else {
                // make sure the statement and the result is closed
                m_sqlManager.closeAll(dbc, conn, stmt, res);
                // nothing to delete
                internalCleanup(dbc, resource);
                return structureVersions;
            }
            m_sqlManager.closeAll(dbc, conn, stmt, res);

            // delete the resource entries
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_DELETE");
            stmt.setString(1, resource.getResourceId().toString());
            stmt.setInt(2, minResPublishTagToKeep);
            int resourceVersions = stmt.executeUpdate();
            m_sqlManager.closeAll(dbc, null, stmt, null);

            // delete the content entries
            stmt = m_sqlManager.getPreparedStatement(conn, "C_CONTENT_HISTORY_DELETE");
            stmt.setString(1, resource.getResourceId().toString());
            stmt.setInt(2, minResPublishTagToKeep);
            stmt.executeUpdate();

            // make sure the statement and the result is closed
            m_sqlManager.closeAll(dbc, conn, stmt, res);
            internalCleanup(dbc, resource);
            return Math.max(structureVersions, resourceVersions);
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#deletePropertyDefinition(org.opencms.db.CmsDbContext, org.opencms.file.CmsPropertyDefinition)
     */
    public void deletePropertyDefinition(CmsDbContext dbc, CmsPropertyDefinition metadef)
            throws CmsDataAccessException {

        Connection conn = null;
        PreparedStatement stmt = null;

        try {
            if ((internalCountProperties(dbc, metadef, CmsProject.ONLINE_PROJECT_ID) != 0)
                    || (internalCountProperties(dbc, metadef, CmsUUID.getOpenCmsUUID()) != 0)) { // HACK: to get an offline project

                throw new CmsDbConsistencyException(
                        Messages.get().container(Messages.ERR_ERROR_DELETING_PROPERTYDEF_1, metadef.getName()));
            }

            // delete the historical property definition
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTYDEF_DELETE_HISTORY");
            stmt.setString(1, metadef.getId().toString());
            stmt.executeUpdate();
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, null);
        }
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#destroy()
     */
    public void destroy() throws Throwable {

        m_sqlManager = null;
        m_driverManager = null;

        if (CmsLog.INIT.isInfoEnabled()) {
            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SHUTDOWN_DRIVER_1, getClass().getName()));
        }
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#getAllDeletedEntries(org.opencms.db.CmsDbContext)
     */
    public List<I_CmsHistoryResource> getAllDeletedEntries(CmsDbContext dbc) throws CmsDataAccessException {

        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet res = null;

        Map<CmsUUID, Integer> tmpEntrieis = new HashMap<CmsUUID, Integer>();
        List<I_CmsHistoryResource> entries = new ArrayList<I_CmsHistoryResource>();
        try {
            conn = m_sqlManager.getConnection(dbc);
            // get all not-deleted historical entries that may come in question
            stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_READ_DELETED");
            res = stmt.executeQuery();
            while (res.next()) {
                CmsUUID structureId = new CmsUUID(res.getString(1));
                int version = res.getInt(2);
                tmpEntrieis.put(structureId, Integer.valueOf(version));
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }
        for (Map.Entry<CmsUUID, Integer> entry : tmpEntrieis.entrySet()) {
            entries.add(readResource(dbc, entry.getKey(), entry.getValue().intValue()));
        }
        return entries;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#getAllNotDeletedEntries(org.opencms.db.CmsDbContext)
     */
    public List<I_CmsHistoryResource> getAllNotDeletedEntries(CmsDbContext dbc) throws CmsDataAccessException {

        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet res = null;

        Map<CmsUUID, Integer> tmpEntrieis = new HashMap<CmsUUID, Integer>();

        List<I_CmsHistoryResource> entries = new ArrayList<I_CmsHistoryResource>();
        try {
            conn = m_sqlManager.getConnection(dbc);

            // get all not-deleted historical entries that may come in question
            stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_READ_NOTDELETED");
            res = stmt.executeQuery();
            while (res.next()) {
                CmsUUID structureId = new CmsUUID(res.getString(1));
                int version = res.getInt(2);
                tmpEntrieis.put(structureId, Integer.valueOf(version));
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }

        for (Map.Entry<CmsUUID, Integer> entry : tmpEntrieis.entrySet()) {
            entries.add(readResource(dbc, entry.getKey(), entry.getValue().intValue()));
        }
        return entries;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#getSqlManager()
     */
    public CmsSqlManager getSqlManager() {

        return m_sqlManager;
    }

    /**
     * @see org.opencms.db.I_CmsDriver#init(org.opencms.db.CmsDbContext, org.opencms.configuration.CmsConfigurationManager, java.util.List, org.opencms.db.CmsDriverManager)
     */
    public void init(CmsDbContext dbc, CmsConfigurationManager configurationManager, List successiveDrivers,
            CmsDriverManager driverManager) {

        CmsParameterConfiguration configuration = configurationManager.getConfiguration();

        String poolUrl;
        if (configuration.get("db.history.pool") != null) {
            poolUrl = configuration.get("db.history.pool").toString();
        } else {
            // TODO: deprecated, remove as soon as possible
            poolUrl = configuration.get("db.backup.pool").toString();
        }

        String classname;
        if (configuration.get("db.history.sqlmanager") != null) {
            classname = configuration.get("db.history.sqlmanager").toString();
        } else {
            // TODO: deprecated, remove as soon as possible
            classname = configuration.get("db.backup.sqlmanager").toString();
        }

        m_sqlManager = initSqlManager(classname);
        m_sqlManager.init(I_CmsHistoryDriver.DRIVER_TYPE_ID, poolUrl);

        m_driverManager = driverManager;

        if (CmsLog.INIT.isInfoEnabled()) {
            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_ASSIGNED_POOL_1, poolUrl));
        }

        if ((successiveDrivers != null) && !successiveDrivers.isEmpty()) {
            if (LOG.isWarnEnabled()) {
                LOG.warn(Messages.get().getBundle().key(Messages.LOG_SUCCESSIVE_DRIVERS_UNSUPPORTED_1,
                        getClass().getName()));
            }
        }
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#initSqlManager(String)
     */
    public org.opencms.db.generic.CmsSqlManager initSqlManager(String classname) {

        return CmsSqlManager.getInstance(classname);
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readAllAvailableVersions(CmsDbContext, CmsUUID)
     */
    public List readAllAvailableVersions(CmsDbContext dbc, CmsUUID structureId) throws CmsDataAccessException {

        ResultSet res = null;
        List result = new ArrayList();
        PreparedStatement stmt = null;
        Connection conn = null;

        try {
            conn = m_sqlManager.getConnection(dbc);

            // get all direct versions (where the structure entry has been written)
            // sorted from the NEWEST to the OLDEST version (publish tag descendant)
            List historyResources = new ArrayList();
            stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_ALL_VERSIONS");
            stmt.setString(1, structureId.toString());
            res = stmt.executeQuery();
            while (res.next()) {
                historyResources.add(internalCreateResource(res));
            }
            m_sqlManager.closeAll(dbc, null, stmt, res);

            if (!historyResources.isEmpty()) {
                // look for newer versions
                // this is the NEWEST version, with the HIGHEST publish tag
                I_CmsHistoryResource histRes = (I_CmsHistoryResource) historyResources.get(0);

                // look for later resource entries
                stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_NEW_VERSIONS");
                stmt.setString(1, histRes.getResourceId().toString());
                stmt.setInt(2, histRes.getPublishTag());
                res = stmt.executeQuery();

                I_CmsHistoryResource lastHistRes = histRes;
                // these are sorted from the oldest to the newest version (publish tag ascendent)
                while (res.next()) {
                    int resVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_VERSION"));
                    if (resVersion == lastHistRes.getResourceVersion()) {
                        // skip not interesting versions
                        continue;
                    }
                    I_CmsHistoryResource newHistRes = internalMergeResource(histRes, res, 0);
                    // add interesting versions, in the right order
                    result.add(0, newHistRes);
                    lastHistRes = newHistRes;
                }
                m_sqlManager.closeAll(dbc, null, stmt, res);
            }
            // iterate from the NEWEST to the OLDEST versions (publish tag descendant)
            for (int i = 0; i < historyResources.size(); i++) {
                I_CmsHistoryResource histRes = (I_CmsHistoryResource) historyResources.get(i);
                result.add(histRes);
                if (i < (historyResources.size() - 1)) {
                    // this is one older direct version than histRes (histRes.getPublishTag() > histRes2.getPublishTag())
                    I_CmsHistoryResource histRes2 = (I_CmsHistoryResource) historyResources.get(i + 1);

                    // look for resource changes in between of the direct versions in ascendent order                    
                    stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_BTW_VERSIONS");
                    stmt.setString(1, histRes.getResourceId().toString());
                    stmt.setInt(2, histRes2.getPublishTag()); // lower limit
                    stmt.setInt(3, histRes.getPublishTag()); // upper limit
                    res = stmt.executeQuery();

                    int pos = result.size();
                    I_CmsHistoryResource lastHistRes = histRes2;
                    while (res.next()) {
                        int resVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_VERSION"));
                        if (resVersion == lastHistRes.getResourceVersion()) {
                            // skip not interesting versions
                            continue;
                        }
                        I_CmsHistoryResource newHistRes = internalMergeResource(histRes2, res, 0);
                        // add interesting versions, in the right order
                        result.add(pos, newHistRes);
                        lastHistRes = newHistRes;
                    }
                    m_sqlManager.closeAll(dbc, null, stmt, res);
                }
            }
            if (!result.isEmpty()) {
                // get the oldest version
                I_CmsHistoryResource histRes = (I_CmsHistoryResource) result.get(result.size() - 1);

                if (histRes.getVersion() > 1) {
                    // look for older resource versions, in descendant order
                    stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_OLD_VERSIONS");
                    stmt.setString(1, histRes.getResourceId().toString());
                    stmt.setInt(2, histRes.getPublishTag());
                    res = stmt.executeQuery();

                    int offset = (histRes.getStructureVersion() > 0 ? 1 : 0);

                    I_CmsHistoryResource lastHistRes = histRes;
                    while (res.next()) {
                        I_CmsHistoryResource newHistRes = internalMergeResource(histRes, res, offset);
                        if (newHistRes.getResourceVersion() != lastHistRes.getResourceVersion()) {
                            // only add interesting versions
                            if (offset == 1) {
                                if (histRes != lastHistRes) {
                                    result.add(lastHistRes);
                                }
                            } else {
                                result.add(newHistRes);
                            }
                        }
                        lastHistRes = newHistRes;
                    }
                    // add the last one if there is one
                    if ((offset == 1) && (lastHistRes != histRes)) {
                        result.add(lastHistRes);
                    }
                    m_sqlManager.closeAll(dbc, null, stmt, res);
                }
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }

        return result;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readContent(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID, int)
     */
    public byte[] readContent(CmsDbContext dbc, CmsUUID resourceId, int publishTag) throws CmsDataAccessException {

        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet res = null;
        byte[] content = null;

        try {
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_READ_CONTENT");
            stmt.setString(1, resourceId.toString());
            stmt.setInt(2, publishTag);
            stmt.setInt(3, publishTag);
            res = stmt.executeQuery();

            if (res.next()) {
                content = m_sqlManager.getBytes(res, m_sqlManager.readQuery("C_RESOURCES_FILE_CONTENT"));
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }
        return content;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readDeletedResources(CmsDbContext, CmsUUID, CmsUUID)
     */
    public List<I_CmsHistoryResource> readDeletedResources(CmsDbContext dbc, CmsUUID structureId, CmsUUID userId)
            throws CmsDataAccessException {

        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet res = null;
        List<I_CmsHistoryResource> result = new ArrayList<I_CmsHistoryResource>();
        List<I_CmsHistoryResource> tmpHistRes = new ArrayList<I_CmsHistoryResource>();

        try {
            conn = m_sqlManager.getConnection(dbc);
            if (userId == null) {
                stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_DELETED");
            } else {
                stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_DELETED_RESTRICTED");
            }
            stmt.setString(1, structureId.toString());
            if (userId != null) {
                stmt.setString(2, userId.toString());
            }
            res = stmt.executeQuery();
            while (res.next()) {
                // store the result into a temporary list
                tmpHistRes.add(internalCreateResource(res));
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }
        I_CmsVfsDriver vfsDriver = m_driverManager.getVfsDriver(dbc);
        for (I_CmsHistoryResource histRes : tmpHistRes) {
            if (vfsDriver.validateStructureIdExists(dbc, dbc.currentProject().getUuid(),
                    histRes.getStructureId())) {
                // only add resources that are really deleted
                continue;
            }
            result.add(histRes);
        }

        if (!result.isEmpty() || (dbc.getRequestContext() == null)
                || (dbc.getRequestContext().getAttribute("ATTR_RESOURCE_NAME") == null)) {
            return result;
        }
        try {
            conn = m_sqlManager.getConnection(dbc);
            if (userId == null) {
                stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_DELETED_NAME");
            } else {
                stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_DELETED_NAME_RESTRICTED");
            }
            String path = dbc.getRequestContext().getAttribute("ATTR_RESOURCE_NAME").toString();
            stmt.setString(1, path + '%');
            stmt.setString(2, path);
            if (userId != null) {
                stmt.setString(3, userId.toString());
            }
            res = stmt.executeQuery();
            // clear the temporary list
            tmpHistRes.clear();
            while (res.next()) {
                // store the result into a temporary list
                tmpHistRes.add(internalCreateResource(res));
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }
        for (I_CmsHistoryResource histRes : tmpHistRes) {
            if (vfsDriver.validateStructureIdExists(dbc, dbc.currentProject().getUuid(),
                    histRes.getStructureId())) {
                // only add resources that are really deleted
                continue;
            }
            result.add(histRes);
        }
        return result;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readFile(CmsDbContext, CmsUUID, int)
     * 
     * @deprecated use {@link #readResource(CmsDbContext, CmsUUID, int)} instead
     *             but notice that the <code>publishTag != version</code>
     */
    public I_CmsHistoryResource readFile(CmsDbContext dbc, CmsUUID structureId, int tagId)
            throws CmsDataAccessException {

        I_CmsHistoryResource file = null;
        PreparedStatement stmt = null;
        ResultSet res = null;
        Connection conn = null;

        try {
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_FILES_READ_HISTORY_BYID");
            stmt.setString(1, structureId.toString());
            stmt.setInt(2, tagId);
            res = stmt.executeQuery();
            if (res.next()) {
                file = internalCreateResource(res);
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            } else {
                throw new CmsVfsResourceNotFoundException(
                        Messages.get().container(Messages.ERR_HISTORY_FILE_NOT_FOUND_1, structureId));
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }

        ((CmsFile) file).setContents(readContent(dbc, file.getResourceId(), file.getPublishTag()));
        return file;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readLastVersion(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID)
     */
    public int readLastVersion(CmsDbContext dbc, CmsUUID structureId) throws CmsDataAccessException {

        PreparedStatement stmt = null;
        Connection conn = null;
        ResultSet res = null;
        int lastVersion = 0;

        try {
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_MAXVER");
            stmt.setString(1, structureId.toString());
            res = stmt.executeQuery();

            if (res.next()) {
                lastVersion = res.getInt(1);
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            } else {
                lastVersion = 0;
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }

        return lastVersion;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readMaxPublishTag(CmsDbContext, CmsUUID)
     */
    public int readMaxPublishTag(CmsDbContext dbc, CmsUUID resourceId) throws CmsDataAccessException {

        PreparedStatement stmt = null;
        Connection conn = null;
        ResultSet res = null;
        int result = 0;

        try {
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_READ_MAX_PUBLISH_TAG");
            stmt.setString(1, resourceId.toString());
            res = stmt.executeQuery();

            if (res.next()) {
                result = res.getInt(1);
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }

        return result;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readNextPublishTag(org.opencms.db.CmsDbContext)
     */
    public int readNextPublishTag(CmsDbContext dbc) {

        PreparedStatement stmt = null;
        Connection conn = null;
        ResultSet res = null;
        int projectPublishTag = 1;
        int resourcePublishTag = 1;

        try {
            // get the max publish tag from project history 
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTS_HISTORY_MAXTAG");
            res = stmt.executeQuery();

            if (res.next()) {
                projectPublishTag = res.getInt(1) + 1;
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            }
        } catch (SQLException exc) {
            LOG.error(Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
                    exc);
        } finally {
            m_sqlManager.closeAll(dbc, null, stmt, res);
        }

        try {
            // get the max publish tag from resource history 
            stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_MAXTAG");
            res = stmt.executeQuery();

            if (res.next()) {
                resourcePublishTag = res.getInt(1) + 1;
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            }
        } catch (SQLException exc) {
            LOG.error(Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
                    exc);
        } finally {
            m_sqlManager.closeAll(dbc, null, stmt, res);
        }

        // keep the biggest 
        if (resourcePublishTag > projectPublishTag) {
            projectPublishTag = resourcePublishTag;
        }

        try {
            // get the max publish tag from contents 
            stmt = m_sqlManager.getPreparedStatement(conn, "C_CONTENT_PUBLISH_MAXTAG");
            res = stmt.executeQuery();

            if (res.next()) {
                resourcePublishTag = res.getInt(1) + 1;
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            }
        } catch (SQLException exc) {
            LOG.error(Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
                    exc);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }

        // return the biggest 
        if (resourcePublishTag > projectPublishTag) {
            projectPublishTag = resourcePublishTag;
        }

        return projectPublishTag;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readPrincipal(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID)
     */
    public CmsHistoryPrincipal readPrincipal(CmsDbContext dbc, CmsUUID principalId) throws CmsDataAccessException {

        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet res = null;
        CmsHistoryPrincipal historyPrincipal = null;

        try {
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_PRINCIPAL_READ");
            stmt.setString(1, principalId.toString());
            res = stmt.executeQuery();
            if (res.next()) {
                String userName = res.getString(m_sqlManager.readQuery("C_PRINCIPALS_HISTORY_NAME"));
                String ou = CmsOrganizationalUnit
                        .removeLeadingSeparator(res.getString(m_sqlManager.readQuery("C_PRINCIPALS_HISTORY_OU")));
                historyPrincipal = new CmsHistoryPrincipal(principalId, ou + userName,
                        res.getString(m_sqlManager.readQuery("C_PRINCIPALS_HISTORY_DESCRIPTION")),
                        res.getString(m_sqlManager.readQuery("C_PRINCIPALS_HISTORY_EMAIL")),
                        res.getString(m_sqlManager.readQuery("C_PRINCIPALS_HISTORY_TYPE")),
                        new CmsUUID(res.getString(m_sqlManager.readQuery("C_PRINCIPALS_HISTORY_USERDELETED"))),
                        res.getLong(m_sqlManager.readQuery("C_PRINCIPALS_HISTORY_DATEDELETED")));
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            } else {
                throw new CmsDbEntryNotFoundException(
                        Messages.get().container(Messages.ERR_HISTORY_PRINCIPAL_NOT_FOUND_1, principalId));
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }
        return historyPrincipal;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readProject(org.opencms.db.CmsDbContext, CmsUUID)
     */
    public CmsHistoryProject readProject(CmsDbContext dbc, CmsUUID projectId) throws CmsDataAccessException {

        PreparedStatement stmt = null;
        CmsHistoryProject project = null;
        ResultSet res = null;
        Connection conn = null;

        int tmpTag;

        try {
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTS_HISTORY_READ_BYID");

            stmt.setString(1, projectId.toString());
            res = stmt.executeQuery();

            if (res.next()) {
                tmpTag = res.getInt(m_sqlManager.readQuery("C_PROJECTS_PUBLISH_TAG_0"));
                project = internalCreateProject(res, null);
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            } else {
                throw new CmsDbEntryNotFoundException(
                        Messages.get().container(Messages.ERR_NO_HISTORY_PROJECT_WITH_ID_1, projectId));
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }

        List<String> projectresources = readProjectResources(dbc, tmpTag);
        project.setProjectResources(projectresources);

        return project;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readProject(org.opencms.db.CmsDbContext, int)
     */
    public CmsHistoryProject readProject(CmsDbContext dbc, int publishTag) throws CmsDataAccessException {

        PreparedStatement stmt = null;
        CmsHistoryProject project = null;
        ResultSet res = null;
        Connection conn = null;
        try {
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTS_HISTORY_READ");

            stmt.setInt(1, publishTag);
            res = stmt.executeQuery();

            if (res.next()) {
                project = internalCreateProject(res, null);
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            } else {
                throw new CmsDbEntryNotFoundException(Messages.get()
                        .container(Messages.ERR_NO_HISTORY_PROJECT_WITH_TAG_ID_1, new Integer(publishTag)));
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }

        List<String> projectresources = readProjectResources(dbc, publishTag);
        project.setProjectResources(projectresources);

        return project;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readProjectResources(org.opencms.db.CmsDbContext, int)
     */
    public List<String> readProjectResources(CmsDbContext dbc, int publishTag) throws CmsDataAccessException {

        PreparedStatement stmt = null;
        Connection conn = null;
        ResultSet res = null;
        List<String> projectResources = new ArrayList<String>();

        try {
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTRESOURCES_HISTORY_READ");
            stmt.setInt(1, publishTag);
            res = stmt.executeQuery();
            while (res.next()) {
                projectResources.add(res.getString("RESOURCE_PATH"));
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }

        return projectResources;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readProjects(org.opencms.db.CmsDbContext)
     */
    public List<CmsHistoryProject> readProjects(CmsDbContext dbc) throws CmsDataAccessException {

        List<CmsHistoryProject> projects = new ArrayList<CmsHistoryProject>();
        ResultSet res = null;
        PreparedStatement stmt = null;
        Connection conn = null;

        Map<Integer, CmsHistoryProject> tmpProjects = new HashMap<Integer, CmsHistoryProject>();

        try {
            // create the statement
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTS_HISTORY_READ_ALL");
            res = stmt.executeQuery();

            // this is not really efficient
            // but it is overriden in all db specific implementations, including mysql
            int i = 0;
            int max = 300;

            while (res.next() && (i < max)) {
                tmpProjects.put(Integer.valueOf(res.getInt("PUBLISH_TAG")), internalCreateProject(res, null));
                i++;
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }

        for (Map.Entry<Integer, CmsHistoryProject> entry : tmpProjects.entrySet()) {
            List<String> resources = readProjectResources(dbc, entry.getKey().intValue());
            entry.getValue().setProjectResources(resources);
            projects.add(entry.getValue());
        }

        return projects;
    }

    /** 
     * @see org.opencms.db.I_CmsHistoryDriver#readProperties(org.opencms.db.CmsDbContext, org.opencms.file.history.I_CmsHistoryResource)
     */
    public List readProperties(CmsDbContext dbc, I_CmsHistoryResource resource) throws CmsDataAccessException {

        ResultSet res = null;
        PreparedStatement stmt = null;
        Connection conn = null;

        Map propertyMap = new HashMap();

        try {
            conn = m_sqlManager.getConnection(dbc);

            // get the latest properties for this sibling
            int pubTag = -1;
            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTIES_HISTORY_READ_PUBTAG");
            stmt.setString(1, resource.getStructureId().toString());
            stmt.setInt(2, resource.getPublishTag());
            res = stmt.executeQuery();
            if (res.next()) {
                pubTag = res.getInt(1);
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            }
            m_sqlManager.closeAll(dbc, null, stmt, res);

            if (pubTag > 0) {
                // add the siblings props
                stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTIES_HISTORY_READALL_STR");
                stmt.setString(1, resource.getStructureId().toString());
                stmt.setInt(2, pubTag);
                res = stmt.executeQuery();

                while (res.next()) {
                    String propertyKey = res.getString(1);
                    String propertyValue = res.getString(2);
                    int mappingType = res.getInt(3);

                    internalAddToPropMap(propertyMap, resource, propertyKey, propertyValue, mappingType);
                }
                m_sqlManager.closeAll(dbc, null, stmt, res);
            }

            if (pubTag != resource.getPublishTag()) {
                // check if there were newer shared properties modifications
                stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTIES_HISTORY_READALL_RES");
                stmt.setString(1, resource.getStructureId().toString());
                stmt.setInt(2, resource.getPublishTag());
                res = stmt.executeQuery();

                while (res.next()) {
                    String propertyKey = res.getString(1);
                    String propertyValue = res.getString(2);
                    int mappingType = res.getInt(3);

                    internalAddToPropMap(propertyMap, resource, propertyKey, propertyValue, mappingType);
                }
                m_sqlManager.closeAll(dbc, null, stmt, res);
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }

        return new ArrayList(propertyMap.values());
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readPropertyDefinition(org.opencms.db.CmsDbContext, java.lang.String)
     */
    public CmsPropertyDefinition readPropertyDefinition(CmsDbContext dbc, String name)
            throws CmsDataAccessException {

        CmsPropertyDefinition propDef = null;
        ResultSet res = null;
        PreparedStatement stmt = null;
        Connection conn = null;

        try {
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTYDEF_READ_HISTORY");
            stmt.setString(1, name);
            res = stmt.executeQuery();

            if (res.next()) {
                propDef = new CmsPropertyDefinition(
                        new CmsUUID(res.getString(m_sqlManager.readQuery("C_PROPERTYDEF_ID"))),
                        res.getString(m_sqlManager.readQuery("C_PROPERTYDEF_NAME")),
                        CmsPropertyDefinition.CmsPropertyType
                                .valueOf(res.getInt(m_sqlManager.readQuery("C_PROPERTYDEF_TYPE"))));
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            } else {
                throw new CmsDbEntryNotFoundException(
                        Messages.get().container(Messages.ERR_NO_PROPERTYDEF_WITH_NAME_1, name));
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }

        return propDef;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readPublishTag(org.opencms.db.CmsDbContext, long)
     */
    public int readPublishTag(CmsDbContext dbc, long maxdate) throws CmsDataAccessException {

        ResultSet res = null;
        PreparedStatement stmt = null;
        Connection conn = null;
        int maxVersion = 0;

        try {
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTS_HISTORY_READ_TAG_FOR_DATE");
            stmt.setLong(1, maxdate);
            res = stmt.executeQuery();
            if (res.next()) {
                maxVersion = res.getInt(1);
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }
        return maxVersion;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readResource(CmsDbContext, CmsUUID, int)
     */
    public I_CmsHistoryResource readResource(CmsDbContext dbc, CmsUUID structureId, int version)
            throws CmsDataAccessException {

        I_CmsHistoryResource resource = null;
        PreparedStatement stmt = null;
        ResultSet res = null;
        Connection conn = null;

        try {
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_VERSION");
            stmt.setString(1, structureId.toString());
            stmt.setInt(2, version);
            res = stmt.executeQuery();
            if (res.next()) {
                resource = internalCreateResource(res);
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            } else {
                throw new CmsVfsResourceNotFoundException(
                        Messages.get().container(Messages.ERR_HISTORY_FILE_NOT_FOUND_1, structureId));
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }
        return resource;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#setDriverManager(org.opencms.db.CmsDriverManager)
     */
    public void setDriverManager(CmsDriverManager driverManager) {

        m_driverManager = driverManager;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#setSqlManager(org.opencms.db.CmsSqlManager)
     */
    public void setSqlManager(org.opencms.db.CmsSqlManager sqlManager) {

        m_sqlManager = (CmsSqlManager) sqlManager;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#writePrincipal(CmsDbContext, org.opencms.security.I_CmsPrincipal)
     */
    public void writePrincipal(CmsDbContext dbc, I_CmsPrincipal principal) throws CmsDataAccessException {

        Connection conn = null;
        PreparedStatement stmt = null;

        try {
            // check if the principal was already saved
            readPrincipal(dbc, principal.getId());
            return;
        } catch (CmsDbEntryNotFoundException e) {
            // ok
        }
        try {
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_PRINCIPAL_CREATE");
            stmt.setString(1, principal.getId().toString());
            stmt.setString(2, principal.getSimpleName());
            String desc = principal.getDescription();
            desc = CmsStringUtil.isEmptyOrWhitespaceOnly(desc) ? "-" : desc;
            stmt.setString(3, desc);
            stmt.setString(4, CmsOrganizationalUnit.SEPARATOR + principal.getOuFqn());
            if (principal instanceof CmsUser) {
                String email = ((CmsUser) principal).getEmail();
                email = CmsStringUtil.isEmptyOrWhitespaceOnly(email) ? "-" : email;
                stmt.setString(5, email);
                stmt.setString(6, I_CmsPrincipal.PRINCIPAL_USER);
            } else {
                stmt.setString(5, "-");
                stmt.setString(6, I_CmsPrincipal.PRINCIPAL_GROUP);
            }
            stmt.setString(7, dbc.currentUser().getId().toString());
            stmt.setLong(8, System.currentTimeMillis());

            stmt.executeUpdate();
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, null);
        }
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#writeProject(org.opencms.db.CmsDbContext, int, long)
     */
    public void writeProject(CmsDbContext dbc, int publishTag, long publishDate) throws CmsDataAccessException {

        CmsProject currentProject = dbc.currentProject();
        CmsUser currentUser = dbc.currentUser();

        List projectresources = m_driverManager.getProjectDriver(dbc).readProjectResources(dbc, currentProject);

        // write historical project to the database
        Connection conn = null;
        PreparedStatement stmt = null;
        try {
            conn = m_sqlManager.getConnection(dbc);

            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTS_HISTORY_CREATE");
            // first write the project
            stmt.setInt(1, publishTag);
            stmt.setString(2, currentProject.getUuid().toString());
            stmt.setString(3, currentProject.getSimpleName());
            stmt.setLong(4, publishDate);
            stmt.setString(5, currentUser.getId().toString());
            stmt.setString(6, currentProject.getOwnerId().toString());
            stmt.setString(7, currentProject.getGroupId().toString());
            stmt.setString(8, currentProject.getManagerGroupId().toString());
            stmt.setString(9, currentProject.getDescription());
            stmt.setLong(10, currentProject.getDateCreated());
            stmt.setInt(11, currentProject.getType().getMode());
            stmt.setString(12, CmsOrganizationalUnit.SEPARATOR + currentProject.getOuFqn());
            stmt.executeUpdate();

            m_sqlManager.closeAll(dbc, null, stmt, null);

            // now write the projectresources
            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTRESOURCES_HISTORY_CREATE");
            Iterator i = projectresources.iterator();
            while (i.hasNext()) {
                stmt.setInt(1, publishTag);
                stmt.setString(2, currentProject.getUuid().toString());
                stmt.setString(3, (String) i.next());
                stmt.executeUpdate();
                stmt.clearParameters();
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, null);
        }
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#writeProperties(org.opencms.db.CmsDbContext, org.opencms.file.CmsResource, java.util.List, int)
     */
    public void writeProperties(CmsDbContext dbc, CmsResource resource, List<CmsProperty> properties,
            int publishTag) throws CmsDataAccessException {

        Connection conn = null;
        PreparedStatement stmt = null;

        Map<CmsProperty, CmsPropertyDefinition> propDefs = new HashMap<CmsProperty, CmsPropertyDefinition>();

        try {
            for (CmsProperty property : properties) {
                CmsPropertyDefinition propDef = null;
                try {
                    propDef = readPropertyDefinition(dbc, property.getName());
                } catch (CmsDbEntryNotFoundException e) {
                    // create if missing
                    propDef = createPropertyDefinition(dbc, property.getName(), CmsPropertyDefinition.TYPE_NORMAL);
                }
                propDefs.put(property, propDef);
            }
        } catch (CmsDataAccessException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, null);
        }

        try {
            conn = m_sqlManager.getConnection(dbc);
            for (Map.Entry<CmsProperty, CmsPropertyDefinition> entry : propDefs.entrySet()) {

                for (int i = 0; i < 2; i++) {
                    int mappingType;
                    String value;
                    CmsUUID id;
                    if (i == 0) {
                        // write the structure value on the first cycle
                        value = entry.getKey().getStructureValue();
                        mappingType = CmsProperty.STRUCTURE_RECORD_MAPPING;
                        id = resource.getStructureId();
                        if (CmsStringUtil.isEmpty(value)) {
                            continue;
                        }
                    } else {
                        // write the resource value on the second cycle
                        value = entry.getKey().getResourceValue();
                        mappingType = CmsProperty.RESOURCE_RECORD_MAPPING;
                        id = resource.getResourceId();
                        if (CmsStringUtil.isEmpty(value)) {
                            break;
                        }
                    }

                    stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTIES_HISTORY_CREATE");

                    stmt.setString(1, resource.getStructureId().toString());
                    stmt.setString(2, entry.getValue().getId().toString());
                    stmt.setString(3, id.toString());
                    stmt.setInt(4, mappingType);
                    stmt.setString(5, m_sqlManager.validateEmpty(value));
                    stmt.setInt(6, publishTag);

                    stmt.executeUpdate();
                    m_sqlManager.closeAll(dbc, null, stmt, null);
                }
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, null);
        }
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#writeResource(org.opencms.db.CmsDbContext, org.opencms.file.CmsResource, java.util.List, int)
     */
    public void writeResource(CmsDbContext dbc, CmsResource resource, List<CmsProperty> properties, int publishTag)
            throws CmsDataAccessException {

        Connection conn = null;
        PreparedStatement stmt = null;

        try {
            boolean valResource = internalValidateResource(dbc, resource, publishTag);
            int sibCount = resource.getSiblingCount();

            // if deleted
            if (resource.getState().isDeleted()) {
                // if it is a file
                if (resource instanceof CmsFile) {
                    if (!valResource) {
                        if (sibCount < 2) {
                            // copy from offline content to content tables
                            // so that the history contains the last state of the file
                            m_driverManager.getVfsDriver(dbc).createOnlineContent(dbc, resource.getResourceId(),
                                    ((CmsFile) resource).getContents(), publishTag, false, true);
                        } else {
                            @SuppressWarnings("unchecked")
                            Set<CmsUUID> changedAndDeleted = (Set<CmsUUID>) dbc
                                    .getAttribute(CmsDriverManager.KEY_CHANGED_AND_DELETED);
                            if ((changedAndDeleted == null)
                                    || !changedAndDeleted.contains(resource.getResourceId())) {
                                // put the content definitively in the history if no sibling is left
                                // (unless another sibling with status "changed" or "new" is published)
                                m_driverManager.getVfsDriver(dbc).createOnlineContent(dbc, resource.getResourceId(),
                                        ((CmsFile) resource).getContents(), publishTag, true, false);
                            }
                        }
                    }
                }

                // update version numbers
                m_driverManager.getVfsDriver(dbc).publishVersions(dbc, resource, !valResource);
            }

            // read the version numbers
            Map<String, Integer> versions = m_driverManager.getVfsDriver(dbc).readVersions(dbc,
                    CmsProject.ONLINE_PROJECT_ID, resource.getResourceId(), resource.getStructureId());
            int structureVersion = (versions.get("structure")).intValue();
            int resourceVersion = (versions.get("resource")).intValue();

            CmsUUID parentId = CmsUUID.getNullUUID();
            CmsFolder parent = m_driverManager.getVfsDriver(dbc).readParentFolder(dbc, CmsProject.ONLINE_PROJECT_ID,
                    resource.getStructureId());
            if (parent != null) {
                parentId = parent.getStructureId();
            }

            conn = m_sqlManager.getConnection(dbc);
            if (!valResource) {
                // write the resource
                stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_WRITE");
                stmt.setString(1, resource.getResourceId().toString());
                stmt.setInt(2, resource.getTypeId());
                stmt.setInt(3, resource.getFlags());
                stmt.setLong(4, resource.getDateCreated());
                stmt.setString(5, resource.getUserCreated().toString());
                stmt.setLong(6, resource.getDateLastModified());
                stmt.setString(7, resource.getUserLastModified().toString());
                stmt.setInt(8, resource.getState().getState());
                stmt.setInt(9, resource.getLength());
                stmt.setLong(10, resource.getDateContent());
                stmt.setString(11, dbc.currentProject().getUuid().toString());
                stmt.setInt(12, resource.getSiblingCount());
                stmt.setInt(13, resourceVersion);
                stmt.setInt(14, publishTag);
                stmt.executeUpdate();
                m_sqlManager.closeAll(dbc, null, stmt, null);
            }
            // write the structure
            stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_WRITE");
            stmt.setString(1, resource.getStructureId().toString());
            stmt.setString(2, resource.getResourceId().toString());
            stmt.setString(3, resource.getRootPath());
            stmt.setInt(4, resource.getState().getState());
            stmt.setLong(5, resource.getDateReleased());
            stmt.setLong(6, resource.getDateExpired());
            stmt.setInt(7, structureVersion);
            stmt.setString(8, parentId.toString());
            stmt.setInt(9, publishTag);
            stmt.setInt(10, resource.getVersion());
            stmt.executeUpdate();
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, null);
        }

        writeProperties(dbc, resource, properties, publishTag);
    }

    /**
     * Updates the property map for the given resource with the given property data.<p>
     * 
     * @param propertyMap the map to update
     * @param resource the resource the properties belong to
     * @param propertyKey the property key
     * @param propertyValue the property value
     * @param mappingType the mapping type
     * 
     * @throws CmsDbConsistencyException if the mapping type is wrong
     */
    protected void internalAddToPropMap(Map propertyMap, I_CmsHistoryResource resource, String propertyKey,
            String propertyValue, int mappingType) throws CmsDbConsistencyException {

        CmsProperty property = (CmsProperty) propertyMap.get(propertyKey);
        if (property != null) {
            // there exists already a property for this key in the result
            switch (mappingType) {
            case CmsProperty.STRUCTURE_RECORD_MAPPING:
                // this property value is mapped to a structure record
                property.setStructureValue(propertyValue);
                break;
            case CmsProperty.RESOURCE_RECORD_MAPPING:
                // this property value is mapped to a resource record
                property.setResourceValue(propertyValue);
                break;
            default:
                throw new CmsDbConsistencyException(
                        Messages.get().container(Messages.ERR_UNKNOWN_PROPERTY_VALUE_MAPPING_3,
                                resource.getRootPath(), new Integer(mappingType), propertyKey));
            }
        } else {
            // there doesn't exist a property for this key yet
            property = new CmsProperty();
            property.setName(propertyKey);

            switch (mappingType) {
            case CmsProperty.STRUCTURE_RECORD_MAPPING:
                // this property value is mapped to a structure record
                property.setStructureValue(propertyValue);
                property.setResourceValue(null);
                break;
            case CmsProperty.RESOURCE_RECORD_MAPPING:
                // this property value is mapped to a resource record
                property.setStructureValue(null);
                property.setResourceValue(propertyValue);
                break;
            default:
                throw new CmsDbConsistencyException(
                        Messages.get().container(Messages.ERR_UNKNOWN_PROPERTY_VALUE_MAPPING_3,
                                resource.getRootPath(), new Integer(mappingType), propertyKey));
            }
            propertyMap.put(propertyKey, property);
        }
    }

    /**
     * Deletes all historical entries of subresources of a folder without any historical netry left.<p>
     * 
     * @param dbc the current database context
     * @param resource the resource to check
     * 
     * @throws CmsDataAccessException if something goes wrong
     */
    protected void internalCleanup(CmsDbContext dbc, I_CmsHistoryResource resource) throws CmsDataAccessException {

        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet res = null;
        Map<CmsUUID, Integer> tmpSubResources = new HashMap<CmsUUID, Integer>();

        // if is folder and if no versions left
        boolean isFolderAndNoVersionLeft = resource.getRootPath().endsWith("/")
                && (readLastVersion(dbc, resource.getStructureId()) == 0);

        // if the resource is a folder
        if (isFolderAndNoVersionLeft) {
            try {
                conn = m_sqlManager.getConnection(dbc);
                // get all direct subresources                    
                stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_READ_SUBRESOURCES");
                stmt.setString(1, resource.getStructureId().toString());
                res = stmt.executeQuery();
                while (res.next()) {
                    CmsUUID structureId = new CmsUUID(res.getString(1));
                    int version = res.getInt(2);
                    tmpSubResources.put(structureId, Integer.valueOf(version));
                }
            } catch (SQLException e) {
                throw new CmsDbSqlException(
                        Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
                        e);
            } finally {
                m_sqlManager.closeAll(dbc, conn, stmt, res);
            }
        }
        // delete all subresource versions
        for (Map.Entry<CmsUUID, Integer> entry : tmpSubResources.entrySet()) {
            I_CmsHistoryResource histResource = readResource(dbc, entry.getKey(), entry.getValue().intValue());
            deleteEntries(dbc, histResource, 0, -1);
        }
    }

    /**
     * Returns the amount of properties for a propertydefinition.<p>
     * 
     * @param dbc the current database context
     * @param metadef the propertydefinition to test
     * @param projectId the ID of the current project
     * 
     * @return the amount of properties for a propertydefinition
     * @throws CmsDataAccessException if something goes wrong
     */
    protected int internalCountProperties(CmsDbContext dbc, CmsPropertyDefinition metadef, CmsUUID projectId)
            throws CmsDataAccessException {

        ResultSet res = null;
        PreparedStatement stmt = null;
        Connection conn = null;

        int returnValue;
        try {
            // create statement
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_PROPERTIES_READALL_COUNT");
            stmt.setString(1, metadef.getId().toString());
            res = stmt.executeQuery();

            if (res.next()) {
                returnValue = res.getInt(1);
                while (res.next()) {
                    // do nothing only move through all rows because of mssql odbc driver
                }
            } else {
                throw new CmsDbConsistencyException(
                        Messages.get().container(Messages.ERR_NO_PROPERTIES_FOR_PROPERTYDEF_1, metadef.getName()));
            }
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }
        return returnValue;
    }

    /**
     * Creates a historical project from the given result set and resources.<p>
     * @param res the resource set
     * @param resources the historical resources
     * 
     * @return the historical project
     *  
     * @throws SQLException if something goes wrong
     */
    protected CmsHistoryProject internalCreateProject(ResultSet res, List resources) throws SQLException {

        String ou = CmsOrganizationalUnit
                .removeLeadingSeparator(res.getString(m_sqlManager.readQuery("C_PROJECTS_PROJECT_OU_0")));
        CmsUUID publishedById = new CmsUUID(res.getString(m_sqlManager.readQuery("C_PROJECT_PUBLISHED_BY_0")));
        CmsUUID userId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_PROJECTS_USER_ID_0")));
        return new CmsHistoryProject(res.getInt(m_sqlManager.readQuery("C_PROJECTS_PUBLISH_TAG_0")),
                new CmsUUID(res.getString(m_sqlManager.readQuery("C_PROJECTS_PROJECT_ID_0"))),
                ou + res.getString(m_sqlManager.readQuery("C_PROJECTS_PROJECT_NAME_0")),
                res.getString(m_sqlManager.readQuery("C_PROJECTS_PROJECT_DESCRIPTION_0")), userId,
                new CmsUUID(res.getString(m_sqlManager.readQuery("C_PROJECTS_GROUP_ID_0"))),
                new CmsUUID(res.getString(m_sqlManager.readQuery("C_PROJECTS_MANAGERGROUP_ID_0"))),
                res.getLong(m_sqlManager.readQuery("C_PROJECTS_DATE_CREATED_0")),
                CmsProject.CmsProjectType.valueOf(res.getInt(m_sqlManager.readQuery("C_PROJECTS_PROJECT_TYPE_0"))),
                res.getLong(m_sqlManager.readQuery("C_PROJECT_PUBLISHDATE_0")), publishedById, resources);
    }

    /**
     * Creates a valid {@link I_CmsHistoryResource} instance from a JDBC ResultSet.<p>
     * 
     * @param res the JDBC result set
     * 
     * @return the new historical resource instance
     * 
     * @throws SQLException if a requested attribute was not found in the result set
     */
    protected I_CmsHistoryResource internalCreateResource(ResultSet res) throws SQLException {

        int resourceVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_VERSION"));
        int structureVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_VERSION"));
        int tagId = res.getInt(m_sqlManager.readQuery("C_RESOURCES_PUBLISH_TAG"));
        CmsUUID structureId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_ID")));
        CmsUUID resourceId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_ID")));
        String resourcePath = res.getString(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_PATH"));
        int resourceType = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_TYPE"));
        int resourceFlags = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_FLAGS"));
        CmsUUID projectLastModified = new CmsUUID(
                res.getString(m_sqlManager.readQuery("C_RESOURCES_PROJECT_LASTMODIFIED")));
        int state = Math.max(res.getInt(m_sqlManager.readQuery("C_RESOURCES_STATE")),
                res.getInt(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_STATE")));
        long dateCreated = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CREATED"));
        long dateLastModified = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_LASTMODIFIED"));
        long dateReleased = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_RELEASED"));
        long dateExpired = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_EXPIRED"));
        int resourceSize = res.getInt(m_sqlManager.readQuery("C_RESOURCES_SIZE"));
        CmsUUID userLastModified = new CmsUUID(
                res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_LASTMODIFIED")));
        CmsUUID userCreated = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_CREATED")));
        CmsUUID parentId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_HISTORY_PARENTID")));
        long dateContent = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CONTENT"));

        boolean isFolder = resourcePath.endsWith("/");
        if (isFolder) {
            return new CmsHistoryFolder(tagId, structureId, resourceId, resourcePath, resourceType, resourceFlags,
                    projectLastModified, CmsResourceState.valueOf(state), dateCreated, userCreated,
                    dateLastModified, userLastModified, dateReleased, dateExpired,
                    resourceVersion + structureVersion, parentId, resourceVersion, structureVersion);
        } else {
            return new CmsHistoryFile(tagId, structureId, resourceId, resourcePath, resourceType, resourceFlags,
                    projectLastModified, CmsResourceState.valueOf(state), dateCreated, userCreated,
                    dateLastModified, userLastModified, dateReleased, dateExpired, resourceSize, dateContent,
                    resourceVersion + structureVersion, parentId, null, resourceVersion, structureVersion);
        }
    }

    /**
     * Merges an historical entry for a sibling, based on the structure data from the given historical resource
     * and result set for the resource entry.<p>
     * 
     * @param histRes the original historical entry
     * @param res the result set of the resource entry
     * @param versionOffset the offset for the structure version
     * 
     * @return a merged historical entry for the sibling
     * 
     * @throws SQLException if something goes wrong
     */
    protected I_CmsHistoryResource internalMergeResource(I_CmsHistoryResource histRes, ResultSet res,
            int versionOffset) throws SQLException {

        int resourceVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_VERSION"));
        int structureVersion = histRes.getStructureVersion() - versionOffset;
        int tagId = res.getInt(m_sqlManager.readQuery("C_RESOURCES_PUBLISH_TAG"));
        CmsUUID structureId = histRes.getStructureId();
        CmsUUID resourceId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_ID")));
        int resourceType = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_TYPE"));
        int resourceFlags = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_FLAGS"));
        CmsUUID projectLastModified = new CmsUUID(
                res.getString(m_sqlManager.readQuery("C_RESOURCES_PROJECT_LASTMODIFIED")));
        int state = histRes.getState().getState(); // may be we have to compute something here?
        long dateCreated = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CREATED"));
        long dateLastModified = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_LASTMODIFIED"));
        long dateReleased = histRes.getDateReleased();
        long dateExpired = histRes.getDateExpired();
        int resourceSize = res.getInt(m_sqlManager.readQuery("C_RESOURCES_SIZE"));
        CmsUUID userLastModified = new CmsUUID(
                res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_LASTMODIFIED")));
        CmsUUID userCreated = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_CREATED")));
        // here we could use the path/parent id for the sibling where the modification really occurred
        String resourcePath = histRes.getRootPath();
        CmsUUID parentId = histRes.getParentId();
        long dateContent = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CONTENT"));

        if (histRes.isFolder()) {
            return new CmsHistoryFolder(tagId, structureId, resourceId, resourcePath, resourceType, resourceFlags,
                    projectLastModified, CmsResourceState.valueOf(state), dateCreated, userCreated,
                    dateLastModified, userLastModified, dateReleased, dateExpired,
                    resourceVersion + structureVersion, parentId, resourceVersion, structureVersion);
        } else {
            return new CmsHistoryFile(tagId, structureId, resourceId, resourcePath, resourceType, resourceFlags,
                    projectLastModified, CmsResourceState.valueOf(state), dateCreated, userCreated,
                    dateLastModified, userLastModified, dateReleased, dateExpired, resourceSize, dateContent,
                    resourceVersion + structureVersion, parentId, null, resourceVersion, structureVersion);
        }
    }

    /**
     * Tests if a history resource does exist.<p>
     * 
     * @param dbc the current database context
     * @param resource the resource to test
     * @param publishTag the publish tag of the resource to test
     * 
     * @return <code>true</code> if the resource already exists, <code>false</code> otherwise
     * 
     * @throws CmsDataAccessException if something goes wrong
     */
    protected boolean internalValidateResource(CmsDbContext dbc, CmsResource resource, int publishTag)
            throws CmsDataAccessException {

        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet res = null;
        boolean exists = false;

        try {
            conn = m_sqlManager.getConnection(dbc);
            stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_EXISTS_RESOURCE");
            stmt.setString(1, resource.getResourceId().toString());
            stmt.setInt(2, publishTag);
            res = stmt.executeQuery();

            exists = res.next();
        } catch (SQLException e) {
            throw new CmsDbSqlException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), e);
        } finally {
            m_sqlManager.closeAll(dbc, conn, stmt, res);
        }
        return exists;
    }

}