// ************************************************************************** // * This file is part of the FreeFileSync project. It is distributed under * // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** #include "detect_renaming.h" #include #include #include using namespace FreeFileSync; /*detect renamed files: Example: X -> |_| Create right |_| -> Y Delete right is detected as: Rename Y to X on right Algorithm: ---------- DB-file left ---filename, Metadata(=:MD)---> DB-file right /|\ | | fileID, MD fileID, MD | | \|/ X Y */ class FindDBAssoc { /* load and associate db-files by filename and metadata(size, date) fileID, MD |-> fileID */ public: struct AssocKey { AssocKey(const Utility::FileID& fileId, const wxLongLong& lastWriteTimeRaw, const wxULongLong& fileSize); bool operator<(const AssocKey& other) const; Utility::FileID fileId_; wxLongLong lastWriteTimeRaw_; wxULongLong fileSize_; }; FindDBAssoc(const FreeFileSync::BaseDirMapping& baseMapping, std::map& assocDBLeftToRight); private: void recurse(const DirContainer& leftSide, const DirContainer& rightSide); std::map& assocDBLeftToRight_; //--> }; inline FindDBAssoc::AssocKey::AssocKey(const Utility::FileID& fileId, const wxLongLong& lastWriteTimeRaw, const wxULongLong& fileSize) : fileId_(fileId), lastWriteTimeRaw_(lastWriteTimeRaw), fileSize_(fileSize) {} inline bool FindDBAssoc::AssocKey::operator<(const AssocKey& other) const { if (fileId_ != other.fileId_) return fileId_ < other.fileId_; if (lastWriteTimeRaw_ != other.lastWriteTimeRaw_) return lastWriteTimeRaw_ < other.lastWriteTimeRaw_; return fileSize_ < other.fileSize_; } FindDBAssoc::FindDBAssoc(const FreeFileSync::BaseDirMapping& baseMapping, std::map& assocDBLeftToRight) : assocDBLeftToRight_(assocDBLeftToRight) { try { std::pair dbInfo = FreeFileSync::loadFromDisk(baseMapping); //throw (FileError) recurse(dbInfo.first->baseDirContainer, dbInfo.second->baseDirContainer); } catch (...) {} //swallow... } void FindDBAssoc::recurse(const DirContainer& leftSide, const DirContainer& rightSide) { for (DirContainer::SubFileList::const_iterator i = leftSide.getSubFiles().begin(); i != leftSide.getSubFiles().end(); ++i) { const FileDescriptor& fileDescrI = i->second.getData(); if (!fileDescrI.fileIdentifier.isNull()) //fileIdentifier may be NULL { const DirContainer::SubFileList::const_iterator j = rightSide.getSubFiles().find(i->first); //find files that exist on left and right if (j != rightSide.getSubFiles().end()) { const FileDescriptor& fileDescrJ = j->second.getData(); if (!fileDescrJ.fileIdentifier.isNull()) //fileIdentifier may be NULL { if ( fileDescrI.lastWriteTimeRaw == fileDescrJ.lastWriteTimeRaw && fileDescrI.fileSize == fileDescrJ.fileSize) { assocDBLeftToRight_[AssocKey(fileDescrI.fileIdentifier, fileDescrI.lastWriteTimeRaw, fileDescrI.fileSize)] = fileDescrJ.fileIdentifier; } } } } } //----------------------------------------------------------------------------------------------- for (DirContainer::SubDirList::const_iterator i = leftSide.getSubDirs().begin(); i != leftSide.getSubDirs().end(); ++i) { const DirContainer::SubDirList::const_iterator j = rightSide.getSubDirs().find(i->first); //directories that exist on both sides if (j != rightSide.getSubDirs().end()) { recurse(i->second, j->second); //recurse into subdirectories } } } class FindRenameCandidates { public: FindRenameCandidates(FreeFileSync::BaseDirMapping& baseMapping) { FindDBAssoc(baseMapping, assocDBLeftToRight); if (!assocDBLeftToRight.empty()) recurse(baseMapping); } void getRenameCandidates(std::vector >& renameOnLeft, std::vector >& renameOnRight); private: void recurse(HierarchyObject& hierObj) { //files std::for_each(hierObj.subFiles.begin(), hierObj.subFiles.end(), boost::bind(&FindRenameCandidates::processFile, this, _1)); //directories std::for_each(hierObj.subDirs.begin(), hierObj.subDirs.end(), boost::bind(&FindRenameCandidates::recurse, this, _1));//recursion } void processFile(FileMapping& fileObj) { switch (fileObj.getSyncOperation()) //evaluate comparison result and sync direction { case SO_CREATE_NEW_LEFT: if (!fileObj.getFileID().isNull()) //fileIdentifier may be NULL createLeft[FindDBAssoc::AssocKey(fileObj.getFileID(), fileObj.getLastWriteTime(), fileObj.getFileSize())] = &fileObj; break; case SO_CREATE_NEW_RIGHT: if (!fileObj.getFileID().isNull()) //fileIdentifier may be NULL createRight.push_back(&fileObj); break; case SO_DELETE_LEFT: if (!fileObj.getFileID().isNull()) //fileIdentifier may be NULL deleteLeft.push_back(&fileObj); break; case SO_DELETE_RIGHT: if (!fileObj.getFileID().isNull()) //fileIdentifier may be NULL deleteRight[FindDBAssoc::AssocKey(fileObj.getFileID(), fileObj.getLastWriteTime(), fileObj.getFileSize())] = &fileObj; break; case SO_OVERWRITE_RIGHT: case SO_OVERWRITE_LEFT: case SO_DO_NOTHING: case SO_UNRESOLVED_CONFLICT: break; } } std::vector createRight; //pointer always bound! std::vector deleteLeft; // // | // \|/ std::map assocDBLeftToRight; // | // \|/ std::map deleteRight; //pointer always bound! std::map createLeft; // }; void FindRenameCandidates::getRenameCandidates( std::vector >& renameOnLeft, std::vector >& renameOnRight) { for (std::vector::const_iterator crRightIter = createRight.begin(); crRightIter != createRight.end(); ++crRightIter) { const FindDBAssoc::AssocKey assocDbKey((*crRightIter)->getFileID(), (*crRightIter)->getLastWriteTime(), (*crRightIter)->getFileSize()); const std::map::const_iterator assocDBIter = assocDBLeftToRight.find(assocDbKey); if (assocDBIter != assocDBLeftToRight.end()) { std::map::const_iterator delRightIter = deleteRight.find(FindDBAssoc::AssocKey(assocDBIter->second, //FileID of right side assocDbKey.lastWriteTimeRaw_, assocDbKey.fileSize_)); if (delRightIter != deleteRight.end()) { renameOnRight.push_back(std::make_pair(*crRightIter, delRightIter->second)); } } } //------------------------------------------------------------------------------------------------ for (std::vector::const_iterator delLeftIter = deleteLeft.begin(); delLeftIter != deleteLeft.end(); ++delLeftIter) { const FindDBAssoc::AssocKey assocDbKey((*delLeftIter)->getFileID(), (*delLeftIter)->getLastWriteTime(), (*delLeftIter)->getFileSize()); const std::map::const_iterator assocDBIter = assocDBLeftToRight.find(assocDbKey); if (assocDBIter != assocDBLeftToRight.end()) { std::map::const_iterator createLeftIter = createLeft.find(FindDBAssoc::AssocKey(assocDBIter->second, //FileID of right side assocDbKey.lastWriteTimeRaw_, assocDbKey.fileSize_)); if (createLeftIter != createLeft.end()) { renameOnLeft.push_back(std::make_pair(createLeftIter->second, *delLeftIter)); } } } } void FreeFileSync::getRenameCandidates(FreeFileSync::BaseDirMapping& baseMapping, //in std::vector >& renameOnLeft, //out std::vector >& renameOnRight) //out throw()! { FindRenameCandidates(baseMapping).getRenameCandidates(renameOnLeft, renameOnRight); }