/* * Syncany, www.syncany.org * Copyright (C) 2011-2015 Philipp C. Heckel <philipp.heckel@gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.syncany.operations.down; import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; import org.syncany.database.DatabaseVersionHeader; import org.syncany.plugins.transfer.StorageException; import org.syncany.plugins.transfer.files.DatabaseRemoteFile; /** * Helper class to help map a database version header to its corresponding * downloaded remote database. This class is used by the {@link DownOperation} * to read the new/unknown remote databases. * * @author Philipp C. Heckel <philipp.heckel@gmail.com> */ public class DatabaseFileList { private SortedMap<File, DatabaseRemoteFile> newRemoteDatabases; private Map<String, File> shortFilenameToFileMap; public DatabaseFileList(SortedMap<File, DatabaseRemoteFile> newRemoteDatabases) { this.newRemoteDatabases = newRemoteDatabases; this.shortFilenameToFileMap = initLookupMap(); } private Map<String, File> initLookupMap() { // Make map 'short filename' -> 'full filename' Map<String, File> shortFilenameToFileMap = new HashMap<String, File>(); for (File remoteDatabase : newRemoteDatabases.keySet()) { shortFilenameToFileMap.put(remoteDatabase.getName(), remoteDatabase); } return shortFilenameToFileMap; } /** * Returns the database file for a given database version header, or <tt>null</tt> * if for this database version header no file has been downloaded. * * <p>Unlike {@link #getNextDatabaseVersionFile(DatabaseVersionHeader, Map) getNextDatabaseVersionFile()}, * this method does <b>not</b> try to find a database file by counting up the local version. It returns * null if the exact version has not been found! * * <p><b>Example:</b> given database version header is A/(A3,B2)/T=.. * <pre> * - Does db-A-0003 exist? No, return null. * </pre> */ public File getExactDatabaseVersionFile(DatabaseVersionHeader databaseVersionHeader) throws StorageException { String clientName = databaseVersionHeader.getClient(); long clientFileClock = databaseVersionHeader.getVectorClock().getClock(clientName); DatabaseRemoteFile potentialDatabaseRemoteFileForRange = new DatabaseRemoteFile(clientName, clientFileClock); return shortFilenameToFileMap.get(potentialDatabaseRemoteFileForRange.getName()); } /** * Returns a database file for a given database version header, or throws an error if * no file has been found. * * <p><b>Note:</b> Unlike {@link #getExactDatabaseVersionFile(DatabaseVersionHeader, Map) getExactDatabaseVersionFile()}, * this method tries to find a database file by counting up the local version, i.e. if the exact version cannot be found, * it increases the local client version by one until a matching version is found. * * <p><b>Example:</b> given database version header is A/(A3,B2)/T=.. * <pre> * - Does db-A-0003 exist? No, continue. * - Does db-A-0004 exist? No, continue. * - Does db-A-0005 exist. Yes, return db-A-0005. * </pre> */ public File getNextDatabaseVersionFile(DatabaseVersionHeader databaseVersionHeader) throws StorageException { String clientName = databaseVersionHeader.getClient(); long clientFileClock = databaseVersionHeader.getVectorClock().getClock(clientName); DatabaseRemoteFile potentialDatabaseRemoteFileForRange = null; File databaseFileForRange = null; int maxRounds = 100000; // TODO [medium] This is ugly and potentially dangerous. Can this lead to incorrect results? boolean isLoadableDatabaseFile = false; while (!isLoadableDatabaseFile && maxRounds > 0) { potentialDatabaseRemoteFileForRange = new DatabaseRemoteFile(clientName, clientFileClock); databaseFileForRange = shortFilenameToFileMap.get(potentialDatabaseRemoteFileForRange.getName()); isLoadableDatabaseFile = databaseFileForRange != null; maxRounds--; clientFileClock++; } if (!isLoadableDatabaseFile) { throw new StorageException("Cannot find suitable database remote file to load range."); } return databaseFileForRange; } }