org.obm.push.backend.FolderSnapshotService.java Source code

Java tutorial

Introduction

Here is the source code for org.obm.push.backend.FolderSnapshotService.java

Source

/* ***** BEGIN LICENSE BLOCK *****
 * 
 * Copyright (C) 2015 Linagora
 *
 * This program is free software: you can redistribute it and/or 
 * modify it under the terms of the GNU Affero General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version, provided you comply 
 * with the Additional Terms applicable for OBM connector by Linagora 
 * pursuant to Section 7 of the GNU Affero General Public License, 
 * subsections (b), (c), and (e), pursuant to which you must notably (i) retain 
 * the Message sent thanks to OBM, Free Communication by Linagora? 
 * signature notice appended to any and all outbound messages 
 * (notably e-mail and meeting requests), (ii) retain all hypertext links between 
 * OBM and obm.org, as well as between Linagora and linagora.com, and (iii) refrain 
 * from infringing Linagora intellectual property rights over its trademarks 
 * and commercial brands. Other Additional Terms apply, 
 * see <http://www.linagora.com/licenses/> for more details. 
 *
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
 * for more details. 
 *
 * You should have received a copy of the GNU Affero General Public License 
 * and its applicable Additional Terms for OBM along with this program. If not, 
 * see <http://www.gnu.org/licenses/> for the GNU Affero General Public License version 3 
 * and <http://www.linagora.com/licenses/> for the Additional Terms applicable to 
 * OBM connectors. 
 * 
 * ***** END LICENSE BLOCK ***** */
package org.obm.push.backend;

import java.util.Map;
import java.util.Set;

import org.obm.push.bean.UserDataRequest;
import org.obm.push.bean.change.hierarchy.BackendFolder;
import org.obm.push.bean.change.hierarchy.BackendFolder.BackendId;
import org.obm.push.bean.change.hierarchy.BackendFolders;
import org.obm.push.bean.change.hierarchy.CollectionChange;
import org.obm.push.bean.change.hierarchy.CollectionDeletion;
import org.obm.push.bean.change.hierarchy.Folder;
import org.obm.push.bean.change.hierarchy.FolderSnapshot;
import org.obm.push.bean.change.hierarchy.HierarchyCollectionChanges;
import org.obm.push.exception.DaoException;
import org.obm.push.exception.activesync.InvalidFolderSyncKeyException;
import org.obm.push.protocol.bean.CollectionId;
import org.obm.push.service.FolderSnapshotDao;
import org.obm.push.service.FolderSnapshotDao.FolderSnapshotNotFoundException;
import org.obm.push.state.FolderSyncKey;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Singleton;

@Singleton
public class FolderSnapshotService {

    protected final FolderSnapshotDao folderSnapshotDao;

    @Inject
    protected FolderSnapshotService(FolderSnapshotDao folderSnapshotDao) {
        this.folderSnapshotDao = folderSnapshotDao;
    }

    public FolderSnapshot findFolderSnapshot(UserDataRequest udr, FolderSyncKey folderSyncKey) {
        try {
            if (folderSyncKey.isInitialFolderSync()) {
                return FolderSnapshot.empty();
            }
            return folderSnapshotDao.get(udr.getUser(), udr.getDevice(), folderSyncKey);
        } catch (FolderSnapshotNotFoundException e) {
            throw new InvalidFolderSyncKeyException(folderSyncKey, e);
        }
    }

    public FolderSnapshot snapshot(UserDataRequest udr, FolderSyncKey outgoingSyncKey, FolderSnapshot knownSnapshot,
            BackendFolders currentFolders) {

        int nextId = knownSnapshot.getNextId();
        Map<BackendId, Folder> knownFolders = knownSnapshot.getFoldersByBackendId();

        ImmutableSet.Builder<Folder> allFolders = ImmutableSet.builder();
        for (BackendFolder currentFolder : currentFolders) {
            if (isKnownFolder(knownFolders, currentFolder)) {
                CollectionId collectionId = knownFolders.get(currentFolder.getBackendId()).getCollectionId();
                allFolders.add(Folder.from(currentFolder, collectionId));
            } else {
                allFolders.add(Folder.from(currentFolder, CollectionId.of(nextId++)));
            }
        }

        FolderSnapshot snapshot = FolderSnapshot.nextId(nextId).folders(allFolders.build());
        folderSnapshotDao.create(udr.getUser(), udr.getDevice(), outgoingSyncKey, snapshot);
        return snapshot;
    }

    public CollectionId createSnapshotAddingFolder(UserDataRequest udr, FolderSyncKey outgoingSyncKey,
            FolderSnapshot knownSnapshot, BackendFolder backendFolder) throws Exception {
        int nextId = knownSnapshot.getNextId();

        CollectionId collectionId = CollectionId.of(nextId++);

        Set<Folder> knownFolders = knownSnapshot.getFolders();

        Set<Folder> newFolders = Sets.union(knownFolders,
                ImmutableSet.of(Folder.from(backendFolder, collectionId)));

        FolderSnapshot snapshot = FolderSnapshot.nextId(nextId).folders(newFolders);
        folderSnapshotDao.create(udr.getUser(), udr.getDevice(), outgoingSyncKey, snapshot);

        return collectionId;
    }

    private boolean isKnownFolder(Map<BackendId, Folder> knownFolders, BackendFolder currentFolder) {
        return knownFolders.containsKey(currentFolder.getBackendId());
    }

    public HierarchyCollectionChanges buildDiff(final FolderSnapshot knownSnapshot,
            final FolderSnapshot currentSnapshot) throws DaoException {
        final Map<BackendId, Folder> knownFolders = knownSnapshot.getFoldersByBackendId();
        final Map<BackendId, Folder> currentFolders = currentSnapshot.getFoldersByBackendId();

        final Set<BackendId> adds = Sets.difference(currentFolders.keySet(), knownFolders.keySet());
        final Set<BackendId> dels = Sets.difference(knownFolders.keySet(), currentFolders.keySet());

        return HierarchyCollectionChanges.builder()
                .deletions(FluentIterable.from(dels).transform(folderToCollectionDeletion(knownFolders)).toSet())
                .additions(FluentIterable.from(adds).transform(folderToCollectionCreation(currentFolders)).toSet())
                .changes(FluentIterable.from(currentSnapshot.getFolders()).filter(new Predicate<Folder>() {

                    @Override
                    public boolean apply(Folder folder) {
                        return !adds.contains(folder.getBackendId()) && !dels.contains(folder.getBackendId())
                                && !knownSnapshot.getFolders().contains(folder);
                    }
                }).transform(folderToCollectionChange(currentFolders)).toSet()).build();
    }

    @VisibleForTesting
    Function<BackendId, CollectionDeletion> folderToCollectionDeletion(final Map<BackendId, Folder> knownFolders) {
        return new Function<BackendId, CollectionDeletion>() {

            @Override
            public CollectionDeletion apply(BackendId id) {
                return CollectionDeletion.builder().collectionId(knownFolders.get(id).getCollectionId()).build();
            }
        };
    }

    @VisibleForTesting
    Function<BackendId, CollectionChange> folderToCollectionCreation(
            final Map<BackendId, Folder> currentFoldersMap) {
        return new Function<BackendId, CollectionChange>() {

            @Override
            public CollectionChange apply(BackendId id) {
                Folder folder = currentFoldersMap.get(id);
                return CollectionChange.builder().isNew(true).displayName(folder.getDisplayName())
                        .folderType(folder.getFolderType()).collectionId(folder.getCollectionId())
                        .parentCollectionId(getParentCollectionId(currentFoldersMap, folder)).build();
            }
        };
    }

    @VisibleForTesting
    Function<Folder, CollectionChange> folderToCollectionChange(final Map<BackendId, Folder> currentFolders) {
        return new Function<Folder, CollectionChange>() {

            @Override
            public CollectionChange apply(Folder folder) {
                return CollectionChange.builder().isNew(false).displayName(folder.getDisplayName())
                        .folderType(folder.getFolderType()).collectionId(folder.getCollectionId())
                        .parentCollectionId(getParentCollectionId(currentFolders, folder)).build();
            }
        };
    }

    private CollectionId getParentCollectionId(Map<BackendId, Folder> currentFoldersMap, Folder folder) {
        Optional<BackendId> parentBackendId = folder.getParentBackendIdOpt();
        if (!parentBackendId.isPresent()) {
            return CollectionId.ROOT;
        }
        Folder parentFolder = currentFoldersMap.get(parentBackendId.get());
        return parentFolder != null ? parentFolder.getCollectionId() : CollectionId.ROOT;
    }

}