org.xwiki.wikistream.confluence.xml.internal.ConfluenceXMLPackage.java Source code

Java tutorial

Introduction

Here is the source code for org.xwiki.wikistream.confluence.xml.internal.ConfluenceXMLPackage.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This 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 software 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.wikistream.confluence.xml.internal;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;

import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.input.CloseShieldInputStream;
import org.apache.commons.lang3.math.NumberUtils;
import org.xwiki.model.EntityType;
import org.xwiki.model.reference.EntityReference;
import org.xwiki.wikistream.WikiStreamException;
import org.xwiki.wikistream.input.InputSource;
import org.xwiki.wikistream.input.InputStreamInputSource;
import org.xwiki.xml.stax.StAXUtils;

public class ConfluenceXMLPackage {
    public static final String KEY_SPACE_NAME = "name";

    public static final String KEY_SPACE_DESCRIPTION = "description";

    public static final String KEY_PAGE_HOMEPAGE = "homepage";

    public static final String KEY_PAGE_PARENT = "parent";

    public static final String KEY_PAGE_SPACE = "space";

    public static final String KEY_PAGE_TITLE = "title";

    public static final String KEY_PAGE_CONTENTS = "bodyContents";

    public static final String KEY_PAGE_CREATION_AUTHOR = "creatorName";

    public static final String KEY_PAGE_CREATION_DATE = "creationDate";

    public static final String KEY_PAGE_REVISION = "version";

    public static final String KEY_PAGE_REVISION_AUTHOR = "lastModifierName";

    public static final String KEY_PAGE_REVISION_DATE = "lastModificationDate";

    public static final String KEY_PAGE_REVISION_COMMENT = "versionComment";

    public static final String KEY_PAGE_REVISIONS = "historicalVersions";

    public static final String KEY_PAGE_CONTENT_STATUS = "contentStatus";

    public static final String KEY_PAGE_BODY = "body";

    public static final String KEY_PAGE_BODY_TYPE = "bodyType";

    public static final String KEY_ATTACHMENT_NAME = "fileName";

    public static final String KEY_ATTACHMENT_CONTENT_TYPE = "contentType";

    public static final String KEY_ATTACHMENT_CONTENT = "content";

    public static final String KEY_ATTACHMENT_CREATION_AUTHOR = "creatorName";

    public static final String KEY_ATTACHMENT_CREATION_DATE = "creationDate";

    public static final String KEY_ATTACHMENT_REVISION_AUTHOR = "lastModifierName";

    public static final String KEY_ATTACHMENT_REVISION_DATE = "lastModificationDate";

    public static final String KEY_ATTACHMENT_CONTENT_SIZE = "fileSize";

    public static final String KEY_ATTACHMENT_REVISION_COMMENT = "comment";

    public static final String KEY_ATTACHMENT_REVISION = "attachmentVersion";

    public static final String KEY_ATTACHMENT_ORIGINAL_REVISION = "originalVersion";

    public static final String KEY_ATTACHMENT_DTO = "imageDetailsDTO";

    public static final String KEY_GROUP_NAME = "name";

    public static final String KEY_GROUP_ACTIVE = "active";

    public static final String KEY_GROUP_LOCAL = "local";

    public static final String KEY_GROUP_CREATION_DATE = "createdDate";

    public static final String KEY_GROUP_REVISION_DATE = "updatedDate";

    public static final String KEY_GROUP_DESCRIPTION = "description";

    public static final String KEY_GROUP_MEMBERUSERS = "memberusers";

    public static final String KEY_GROUP_MEMBERGROUPS = "membergroups";

    public static final String KEY_USER_NAME = "name";

    public static final String KEY_USER_ACTIVE = "active";

    public static final String KEY_USER_CREATION_DATE = "createdDate";

    public static final String KEY_USER_REVISION_DATE = "updatedDate";

    public static final String KEY_USER_FIRSTNAME = "firstName";

    public static final String KEY_USER_LASTNAME = "lastName";

    public static final String KEY_USER_DISPLAYNAME = "displayName";

    public static final String KEY_USER_EMAIL = "emailAddress";

    public static final String KEY_USER_PASSWORD = "credential";

    /**
     * 2012-03-07 17:16:48.158
     */
    public static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    /**
     * pattern to find the end of "intentionally damaged" CDATA end sections.
     * Confluence does this to nest CDATA sections inside CDATA sections.
     * Interestingly it does not care if there is a > after the ]].
     */
    private static final Pattern FIND_BROKEN_CDATA_PATTERN = Pattern.compile("]] ");

    /**
     * replacement to repair the CDATA 
     */
    private static final String REPAIRED_CDATA_END = "]]";

    private File directory;

    private File entities;

    private File descriptor;

    private File tree;

    private Map<Integer, List<Integer>> pages = new HashMap<Integer, List<Integer>>();

    public ConfluenceXMLPackage(InputSource source) throws IOException, WikiStreamException, XMLStreamException,
            FactoryConfigurationError, NumberFormatException, ConfigurationException {
        InputStream stream;

        if (source instanceof InputStreamInputSource) {
            stream = ((InputStreamInputSource) source).getInputStream();
        } else {
            throw new WikiStreamException(
                    String.format("Unsupported input source of type [%s]", source.getClass().getName()));
        }

        try {
            // Get temporary folder
            this.directory = File.createTempFile("confluencexml", "");
            this.directory.delete();
            this.directory.mkdir();
            this.directory.deleteOnExit();

            // Extract the zip
            ZipArchiveInputStream zais = new ZipArchiveInputStream(stream);
            for (ZipArchiveEntry zipEntry = zais.getNextZipEntry(); zipEntry != null; zipEntry = zais
                    .getNextZipEntry()) {
                if (!zipEntry.isDirectory()) {
                    String path = zipEntry.getName();
                    File file = new File(this.directory, path);

                    if (path.equals("entities.xml")) {
                        this.entities = file;
                    } else if (path.equals("exportDescriptor.properties")) {
                        this.descriptor = file;
                    }

                    FileUtils.copyInputStreamToFile(new CloseShieldInputStream(zais), file);
                }
            }
        } finally {
            source.close();
        }

        // Initialize

        createTree();
    }

    private PropertiesConfiguration newProperties() {
        PropertiesConfiguration properties = new PropertiesConfiguration();

        properties.setDelimiterParsingDisabled(true);

        return properties;
    }

    public Date getDate(PropertiesConfiguration properties, String key) throws ParseException {
        String str = properties.getString(key);

        return str != null ? DATE_FORMAT.parse(str) : null;
    }

    public List<Integer> getIntegertList(PropertiesConfiguration properties, String key) {
        return getIntegertList(properties, key, null);
    }

    public List<Integer> getIntegertList(PropertiesConfiguration properties, String key, List<Integer> def) {
        List<Object> list = properties.getList(key, null);

        if (list == null) {
            return def;
        }

        if (list.isEmpty()) {
            return Collections.emptyList();
        }

        if (list.get(0) instanceof Integer) {
            return (List) list;
        }

        List<Integer> integerList = new ArrayList<Integer>(list.size());
        for (Object element : list) {
            integerList.add(Integer.valueOf(element.toString()));
        }

        return integerList;
    }

    public EntityReference getReferenceFromId(PropertiesConfiguration currentProperties, String key)
            throws ConfigurationException {
        if (currentProperties.containsKey(key)) {
            int pageId = currentProperties.getInt(key);

            PropertiesConfiguration pageProperties = getPageProperties(pageId);

            int spaceId = pageProperties.getInt(KEY_PAGE_SPACE);
            int currentSpaceId = currentProperties.getInt(KEY_PAGE_SPACE);

            EntityReference spaceReference;
            if (spaceId != currentSpaceId) {
                spaceReference = new EntityReference(getSpaceName(currentSpaceId), EntityType.SPACE);
            } else {
                spaceReference = null;
            }

            return new EntityReference(pageProperties.getString(KEY_PAGE_TITLE), EntityType.DOCUMENT,
                    spaceReference);
        }

        return null;
    }

    public String getSpaceName(int spaceId) throws ConfigurationException {
        PropertiesConfiguration spaceProperties = getSpaceProperties(spaceId);

        return spaceProperties.getString(KEY_SPACE_NAME);
    }

    public Map<Integer, List<Integer>> getPages() {
        return this.pages;
    }

    private void createTree() throws XMLStreamException, FactoryConfigurationError, NumberFormatException,
            IOException, ConfigurationException, WikiStreamException {
        this.tree = new File(this.directory, "tree");
        this.tree.mkdir();

        InputStream stream = new FileInputStream(getEntities());

        XMLStreamReader xmlReader = XMLInputFactory.newInstance().createXMLStreamReader(stream);

        xmlReader.nextTag();

        for (xmlReader.nextTag(); xmlReader.isStartElement(); xmlReader.nextTag()) {
            String elementName = xmlReader.getLocalName();

            if (elementName.equals("object")) {
                readObject(xmlReader);
            } else {
                StAXUtils.skipElement(xmlReader);
            }
        }
    }

    private void readObject(XMLStreamReader xmlReader) throws XMLStreamException, NumberFormatException,
            IOException, ConfigurationException, WikiStreamException {
        String type = xmlReader.getAttributeValue(null, "class");

        if (type != null) {
            if (type.equals("Page")) {
                readPageObject(xmlReader);
            } else if (type.equals("Space")) {
                readSpaceObject(xmlReader);
            } else if (type.equals("InternalUser")) {
                readUserObject(xmlReader);
            } else if (type.equals("InternalGroup")) {
                readGroupObject(xmlReader);
            } else if (type.equals("HibernateMembership")) {
                readMembershipObject(xmlReader);
            } else if (type.equals("BodyContent")) {
                readBodyContentObject(xmlReader);
            } else if (type.equals("SpaceDescription")) {
                readSpaceDescriptionObject(xmlReader);
            } else if (type.equals("SpacePermission")) {
                readSpacePermissionObject(xmlReader);
            } else if (type.equals("Attachment")) {
                readAttachmentObject(xmlReader);
            } else if (type.equals("ReferralLink")) {
                // TODO: any idea how to convert that ?
                StAXUtils.skipElement(xmlReader);
            } else if (type.equals("Label")) {
                // TODO: any idea how to convert that ?
                StAXUtils.skipElement(xmlReader);
            } else {
                StAXUtils.skipElement(xmlReader);
            }
        }
    }

    private int readObjectProperties(XMLStreamReader xmlReader, PropertiesConfiguration properties)
            throws XMLStreamException, WikiStreamException, ConfigurationException, IOException {
        int id = -1;

        for (xmlReader.nextTag(); xmlReader.isStartElement(); xmlReader.nextTag()) {
            String elementName = xmlReader.getLocalName();

            if (elementName.equals("id")) {
                id = Integer.valueOf(xmlReader.getElementText());

                properties.setProperty("id", id);
            } else if (elementName.equals("property")) {
                String propertyName = xmlReader.getAttributeValue(null, "name");

                properties.setProperty(propertyName, readProperty(xmlReader));
            } else if (elementName.equals("collection")) {
                String propertyName = xmlReader.getAttributeValue(null, "name");

                properties.setProperty(propertyName, readProperty(xmlReader));
            } else {
                StAXUtils.skipElement(xmlReader);
            }
        }

        return id;
    }

    private void readAttachmentObject(XMLStreamReader xmlReader)
            throws XMLStreamException, WikiStreamException, ConfigurationException, IOException {
        PropertiesConfiguration properties = newProperties();

        int attachmentId = readObjectProperties(xmlReader, properties);

        if (properties.containsKey(KEY_ATTACHMENT_CONTENT)) {
            int pageId = properties.getInt(KEY_ATTACHMENT_CONTENT);

            // Save attachment
            saveAttachmentProperties(properties, pageId, attachmentId);
        }
    }

    private void readSpaceObject(XMLStreamReader xmlReader)
            throws XMLStreamException, WikiStreamException, ConfigurationException, IOException {
        PropertiesConfiguration properties = newProperties();

        int spaceId = readObjectProperties(xmlReader, properties);

        // Save page
        saveSpaceProperties(properties, spaceId);

        // Register space
        List<Integer> spacePages = this.pages.get(spaceId);
        if (spacePages == null) {
            spacePages = new LinkedList<Integer>();
            this.pages.put(spaceId, spacePages);
        }
    }

    private void readSpaceDescriptionObject(XMLStreamReader xmlReader)
            throws XMLStreamException, WikiStreamException, ConfigurationException, IOException {
        PropertiesConfiguration properties = newProperties();

        int descriptionId = readObjectProperties(xmlReader, properties);

        properties.setProperty(KEY_PAGE_HOMEPAGE, true);

        // Save page
        savePageProperties(properties, descriptionId);
    }

    private void readSpacePermissionObject(XMLStreamReader xmlReader)
            throws XMLStreamException, ConfigurationException, IOException, WikiStreamException {
        PropertiesConfiguration properties = newProperties();

        int permissionId = readObjectProperties(xmlReader, properties);

        if (properties.containsKey("space")) {
            int spaceId = properties.getInt("space");

            // Save attachment
            saveSpacePermissionsProperties(properties, spaceId, permissionId);
        }
    }

    private void readBodyContentObject(XMLStreamReader xmlReader)
            throws XMLStreamException, ConfigurationException, WikiStreamException, IOException {
        PropertiesConfiguration properties = newProperties();
        properties.setDelimiterParsingDisabled(true);

        readObjectProperties(xmlReader, properties);

        if (properties.containsKey("content")) {
            int pageId = properties.getInt("content");

            savePageProperties(properties, pageId);
        }
    }

    private void readPageObject(XMLStreamReader xmlReader) throws IOException, NumberFormatException,
            XMLStreamException, ConfigurationException, WikiStreamException {
        PropertiesConfiguration properties = newProperties();

        int pageId = readObjectProperties(xmlReader, properties);

        // Save page
        savePageProperties(properties, pageId);

        // Register only current pages (they will take care of handling there history)
        Integer originalVersion = (Integer) properties.getProperty("originalVersion");
        if (originalVersion == null) {
            Integer spaceId = (Integer) properties.getInteger("space", null);
            List<Integer> spacePages = this.pages.get(spaceId);
            if (spacePages == null) {
                spacePages = new LinkedList<Integer>();
                this.pages.put(spaceId, spacePages);
            }
            spacePages.add(pageId);
        }
    }

    private void readUserObject(XMLStreamReader xmlReader) throws IOException, NumberFormatException,
            XMLStreamException, ConfigurationException, WikiStreamException {
        PropertiesConfiguration properties = newProperties();

        int pageId = readObjectProperties(xmlReader, properties);

        // Save page
        saveUserProperties(properties, pageId);
    }

    private void readGroupObject(XMLStreamReader xmlReader) throws IOException, NumberFormatException,
            XMLStreamException, ConfigurationException, WikiStreamException {
        PropertiesConfiguration properties = newProperties();

        int pageId = readObjectProperties(xmlReader, properties);

        // Save page
        saveGroupProperties(properties, pageId);
    }

    private void readMembershipObject(XMLStreamReader xmlReader)
            throws ConfigurationException, XMLStreamException, WikiStreamException, IOException {
        PropertiesConfiguration properties = newProperties();

        readObjectProperties(xmlReader, properties);

        Integer parentGroup = properties.getInteger("parentGroup", null);

        if (parentGroup != null) {
            PropertiesConfiguration groupProperties = getGroupProperties(parentGroup);

            Integer userMember = properties.getInteger("userMember", null);

            if (userMember != null) {
                List<Integer> users = new ArrayList<Integer>(
                        getIntegertList(groupProperties, KEY_GROUP_MEMBERUSERS, Collections.<Integer>emptyList()));
                users.add(userMember);
                groupProperties.setProperty(KEY_GROUP_MEMBERUSERS, users);
            }

            Integer groupMember = properties.getInteger("groupMember", null);

            if (groupMember != null) {
                List<Integer> groups = new ArrayList<Integer>(
                        getIntegertList(groupProperties, KEY_GROUP_MEMBERGROUPS, Collections.<Integer>emptyList()));
                groups.add(groupMember);
                groupProperties.setProperty(KEY_GROUP_MEMBERGROUPS, groups);
            }

            saveGroupProperties(groupProperties, parentGroup);
        }
    }

    private Object readProperty(XMLStreamReader xmlReader) throws XMLStreamException, WikiStreamException {
        String propertyClass = xmlReader.getAttributeValue(null, "class");

        if (propertyClass == null) {
            return fixCData(xmlReader.getElementText());
        } else if (propertyClass.equals("java.util.List") || propertyClass.equals("java.util.Collection")) {
            return readListProperty(xmlReader);
        } else if (propertyClass.equals("java.util.Set")) {
            return readSetProperty(xmlReader);
        } else if (propertyClass.equals("Page") || propertyClass.equals("Space")
                || propertyClass.equals("BodyContent") || propertyClass.equals("Attachment")
                || propertyClass.equals("SpaceDescription") || propertyClass.equals("Labelling")
                || propertyClass.equals("SpacePermission") || propertyClass.equals("InternalGroup")
                || propertyClass.equals("InternalUser") || propertyClass.equals("Comment")) {
            return readIdProperty(xmlReader);
        } else {
            StAXUtils.skipElement(xmlReader);
        }

        return null;
    }

    /**
     * to protect content with cdata section inside of cdata elements confluence adds a single space after two consecutive curly braces.
     * we need to undo this patch as otherwise the content parser will complain about invalid content.
     * strictly speaking this needs only to be done for string valued properties
     */
    private String fixCData(String elementText) {
        if (elementText == null) {
            return elementText;
        }
        return FIND_BROKEN_CDATA_PATTERN.matcher(elementText).replaceAll(REPAIRED_CDATA_END);
    }

    private Integer readIdProperty(XMLStreamReader xmlReader) throws WikiStreamException, XMLStreamException {
        xmlReader.nextTag();

        if (!xmlReader.getLocalName().equals("id")) {
            throw new WikiStreamException(
                    String.format("Was expecting id element but found [%s]", xmlReader.getLocalName()));
        }

        Integer value = Integer.valueOf(xmlReader.getElementText());

        xmlReader.nextTag();

        return value;
    }

    private List<Object> readListProperty(XMLStreamReader xmlReader)
            throws XMLStreamException, WikiStreamException {
        List<Object> list = new ArrayList<Object>();

        for (xmlReader.nextTag(); xmlReader.isStartElement(); xmlReader.nextTag()) {
            list.add(readProperty(xmlReader));
        }

        return list;
    }

    private Set<Object> readSetProperty(XMLStreamReader xmlReader) throws XMLStreamException, WikiStreamException {
        Set<Object> set = new LinkedHashSet<Object>();

        for (xmlReader.nextTag(); xmlReader.isStartElement(); xmlReader.nextTag()) {
            set.add(readProperty(xmlReader));
        }

        return set;
    }

    private File getSpacesFolder() {
        return new File(this.tree, "spaces");
    }

    private File getSpaceFolder(int spaceId) {
        return new File(getSpacesFolder(), String.valueOf(spaceId));
    }

    private File getPagesFolder() {
        return new File(this.tree, "pages");
    }

    private File getUsersFolder() {
        return new File(this.tree, "users");
    }

    private File getGroupsFolder() {
        return new File(this.tree, "groups");
    }

    private File getPageFolder(int pageId) {
        return new File(getPagesFolder(), String.valueOf(pageId));
    }

    private File getUserFolder(int userId) {
        return new File(getUsersFolder(), String.valueOf(userId));
    }

    private File getGroupFolder(int groupId) {
        return new File(getGroupsFolder(), String.valueOf(groupId));
    }

    private File getPagePropertiesFile(int pageId) {
        File folder = getPageFolder(pageId);

        return new File(folder, "properties.properties");
    }

    private File getUserPropertiesFile(int userId) {
        File folder = getUserFolder(userId);

        return new File(folder, "properties.properties");
    }

    private File getGroupPropertiesFile(int groupId) {
        File folder = getGroupFolder(groupId);

        return new File(folder, "properties.properties");
    }

    public Collection<Integer> getAttachments(int pageId) {
        File folder = getAttachmentsFolder(pageId);

        Collection<Integer> attachments;
        if (folder.exists()) {
            String[] attachmentFolders = folder.list();

            attachments = new TreeSet<Integer>();
            for (String attachmentIdString : attachmentFolders) {
                if (NumberUtils.isNumber(attachmentIdString)) {
                    attachments.add(Integer.valueOf(attachmentIdString));
                }
            }
        } else {
            attachments = Collections.emptyList();
        }

        return attachments;
    }

    private File getAttachmentsFolder(int pageId) {
        return new File(getPageFolder(pageId), "attachments");
    }

    private File getSpacePermissionsFolder(int spaceId) {
        return new File(getSpaceFolder(spaceId), "permissions");
    }

    private File getAttachmentFolder(int pageId, int attachmentId) {
        return new File(getAttachmentsFolder(pageId), String.valueOf(attachmentId));
    }

    private File getSpacePermissionFolder(int spaceId, int permissionId) {
        return new File(getSpacePermissionsFolder(spaceId), String.valueOf(permissionId));
    }

    private File getAttachmentPropertiesFile(int pageId, int attachmentId) {
        File folder = getAttachmentFolder(pageId, attachmentId);

        return new File(folder, "properties.properties");
    }

    private File getSpacePermissionPropertiesFile(int spaceId, int permissionId) {
        File folder = getSpacePermissionFolder(spaceId, permissionId);

        return new File(folder, "properties.properties");
    }

    private File getSpacePropertiesFile(int spaceId) {
        File folder = getSpaceFolder(spaceId);

        return new File(folder, "properties.properties");
    }

    public PropertiesConfiguration getPageProperties(int pageId) throws ConfigurationException {
        File file = getPagePropertiesFile(pageId);

        return new PropertiesConfiguration(file);
    }

    public PropertiesConfiguration getUserProperties(int userId) throws ConfigurationException {
        File file = getUserPropertiesFile(userId);

        return new PropertiesConfiguration(file);
    }

    public Collection<Integer> getUsers() {
        File folder = getUsersFolder();

        Collection<Integer> users;
        if (folder.exists()) {
            String[] userFolders = folder.list();

            users = new TreeSet<Integer>();
            for (String userIdString : userFolders) {
                if (NumberUtils.isNumber(userIdString)) {
                    users.add(Integer.valueOf(userIdString));
                }
            }
        } else {
            users = Collections.emptyList();
        }

        return users;
    }

    public Collection<Integer> getGroups() {
        File folder = getGroupsFolder();

        Collection<Integer> groups;
        if (folder.exists()) {
            String[] groupFolders = folder.list();

            groups = new TreeSet<Integer>();
            for (String groupIdString : groupFolders) {
                if (NumberUtils.isNumber(groupIdString)) {
                    groups.add(Integer.valueOf(groupIdString));
                }
            }
        } else {
            groups = Collections.emptyList();
        }

        return groups;
    }

    public PropertiesConfiguration getGroupProperties(int groupId) throws ConfigurationException {
        File file = getGroupPropertiesFile(groupId);

        return new PropertiesConfiguration(file);
    }

    public PropertiesConfiguration getAttachmentProperties(int pageId, int attachmentId)
            throws ConfigurationException {
        File file = getAttachmentPropertiesFile(pageId, attachmentId);

        return new PropertiesConfiguration(file);
    }

    public PropertiesConfiguration getSpacePermissionProperties(int spaceId, int permissionId)
            throws ConfigurationException {
        File file = getSpacePermissionPropertiesFile(spaceId, permissionId);

        return new PropertiesConfiguration(file);
    }

    public PropertiesConfiguration getSpaceProperties(int spaceId) throws ConfigurationException {
        File file = getSpacePropertiesFile(spaceId);

        return new PropertiesConfiguration(file);
    }

    private void savePageProperties(PropertiesConfiguration properties, int pageId)
            throws IOException, ConfigurationException {
        PropertiesConfiguration fileProperties = getPageProperties(pageId);

        fileProperties.copy(properties);

        fileProperties.save();
    }

    private void saveUserProperties(PropertiesConfiguration properties, int userId)
            throws IOException, ConfigurationException {
        PropertiesConfiguration fileProperties = getUserProperties(userId);

        fileProperties.copy(properties);

        fileProperties.save();
    }

    private void saveGroupProperties(PropertiesConfiguration properties, int groupId)
            throws IOException, ConfigurationException {
        PropertiesConfiguration fileProperties = getGroupProperties(groupId);

        fileProperties.copy(properties);

        fileProperties.save();
    }

    private void saveAttachmentProperties(PropertiesConfiguration properties, int pageId, int attachmentId)
            throws IOException, ConfigurationException {
        PropertiesConfiguration fileProperties = getAttachmentProperties(pageId, attachmentId);

        fileProperties.copy(properties);

        fileProperties.save();
    }

    private void saveSpacePermissionsProperties(PropertiesConfiguration properties, int spaceId, int permissionId)
            throws IOException, ConfigurationException {
        PropertiesConfiguration fileProperties = getSpacePermissionProperties(spaceId, permissionId);

        fileProperties.copy(properties);

        fileProperties.save();
    }

    private void saveSpaceProperties(PropertiesConfiguration properties, int spaceId)
            throws IOException, ConfigurationException {
        PropertiesConfiguration fileProperties = getSpaceProperties(spaceId);

        fileProperties.copy(properties);

        fileProperties.save();
    }

    public File getEntities() {
        return this.entities;
    }

    public File getDescriptor() {
        return this.descriptor;
    }

    public File getAttachmentFile(int pageId, int attachmentId, int version) {
        File attachmentsFolder = new File(this.directory, "attachments");
        File attachmentsPageFolder = new File(attachmentsFolder, String.valueOf(pageId));
        File attachmentFolder = new File(attachmentsPageFolder, String.valueOf(attachmentId));

        return new File(attachmentFolder, String.valueOf(version));
    }

    public void close() throws IOException {
        if (this.tree != null) {
            FileUtils.deleteDirectory(this.tree);
        }
    }
}