private void ConflictHandler(FileCompareObject fco, int fileIndex) { string src = Path.Combine(fco.GetSmartParentPath(fileIndex), fco.Name); string conflictFolder = Path.Combine(fco.GetSmartParentPath(fileIndex), _syncConfig.ConflictDir); if (!Directory.Exists(conflictFolder)) Directory.CreateDirectory(conflictFolder); string currTime = String.Format("{0:MMddHHmmss}", DateTime.Now) + "_"; string dest = Path.Combine(conflictFolder, currTime + fco.Name); try { CommonMethods.CopyFile(src, dest); // Copy conflicted file from to the conflict folder CommonMethods.DeleteFile(src); // Delete conflicted file } catch (CopyFileException) { ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error copying file from " + src + " to " + dest)); } catch (DeleteFileException) { ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error deleting file " + src)); } }
private void PopulateHash(FileCompareObject file, int index) { if (file.Exists[index]) { // If the metadata exists, and the creation time of the file is equals to that of the metadata, and the last write time is also the same, // as well as the length, we assume that the file is unchanged, and simply use the hash info from the metadata instead of re-hashing. This // saves a lot of time. if (file.MetaExists[index] && file.CreationTimeUtc[index] == file.MetaCreationTimeUtc[index] && file.LastWriteTimeUtc[index] == file.MetaLastWriteTimeUtc[index] && file.Length[index] == file.MetaLength[index]) { file.Hash[index] = file.MetaHash[index]; } else { try { // Calculate the hash of the file file.Hash[index] = CommonMethods.CalculateMD5Hash(Path.Combine(file.GetSmartParentPath(index), file.Name)); } catch (HashFileException) { // If there is an error hashing, we set the FinalState to error, and make it invalid so it is excluded from all comparison and // syncing. ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error hashing " + Path.Combine(file.GetSmartParentPath(index), file.Name + "."))); file.FinalState[index] = FinalState.Error; file.Invalid = true; } } } }
/// <summary> /// Visit implementaton for <see cref="FileCompareObject"/>. /// </summary> /// <param name="file">The <see cref="FileCompareObject"/> to process.</param> /// <param name="numOfPaths">The total number of folders to sync.</param> public void Visit(FileCompareObject file, int numOfPaths) { _nodesCount++; _syncProgress.Message = file.Name; if (file.Invalid) { _syncProgress.Fail(); return; } int maxPriorityPos = file.SourcePosition; // Get the position of the source file. if (file.Priority[maxPriorityPos] > 0) { switch (file.ChangeType[maxPriorityPos]) { case MetaChangeType.Delete: DeleteFile(file, numOfPaths, maxPriorityPos); break; case MetaChangeType.New: case MetaChangeType.Update: case MetaChangeType.NoChange: CopyFile(file, numOfPaths, maxPriorityPos); break; case MetaChangeType.Rename: MoveFile(file, numOfPaths, maxPriorityPos); break; } } _syncProgress.Complete(); }
/// <summary> /// Visit implementation for <see cref="FileCompareObject"/>. /// </summary> /// <param name="file">The <see cref="FileCompareObject"/> to process.</param> /// <param name="numOfPaths"></param> public void Visit(FileCompareObject file, int numOfPaths) { if (file.ConflictPositions == null || file.ConflictPositions.Count == 0) return; foreach (int i in file.ConflictPositions) ConflictHandler(file, i); }
/// <summary> /// Visit implementation for <see cref="FileCompareObject"/>. /// </summary> /// <param name="file">The <see cref="FileCompareObject"/> to process.</param> /// <param name="numOfPaths">The total number of folders to sync.</param> public void Visit(FileCompareObject file, int numOfPaths) { for (int i = 0; i < numOfPaths; i++) { PopulateHash(file, i); ProcessFileMetaData(file, i); } }
/// <summary> /// Visit implementation for <see cref="FileCompareObject"/>. /// </summary> /// <param name="file">The <see cref="FileCompareObject"/> to process.</param> /// <param name="numOfPaths">The total number of folders to keep in sync.</param> public void Visit(FileCompareObject file, int numOfPaths) { if (file.Invalid) return; DetectFileRename(file, numOfPaths); CompareFiles(file, numOfPaths); _totalNodes++; }
/// <summary> /// Visits each file node and return them and it will update the values either through the meta data /// or the last known state document /// </summary> /// <param name="file"></param> /// <param name="numOfPaths"></param> public void Visit(FileCompareObject file, int numOfPaths) { XmlDocument xmlDoc = new XmlDocument(); for (int i = 0; i < numOfPaths; i++) { string path = Path.Combine(file.GetSmartParentPath(i), CommonXMLConstants.MetadataPath); if (!File.Exists(path)) continue; CommonMethods.LoadXML(ref xmlDoc, path); file = PopulateFileWithMetaData(xmlDoc, file, i); } }
/// <summary> /// It calls ProcessMetaChangeType method and if either needLastKnownState or the method is true, /// it will call GenerateFileLastKnownState method to write a last known state document /// </summary> /// <param name="file"></param> /// <param name="numOfPaths"></param> public void Visit(FileCompareObject file, int numOfPaths) { if (file.Invalid) return; bool needLastKnownState = false; for (int i = 0; i < numOfPaths; i++) { _progress.Message = Path.Combine(file.GetSmartParentPath(i), file.Name); needLastKnownState = ProcessMetaChangeType(file, i) || needLastKnownState; } if (needLastKnownState) GenerateFileLastKnownState(file, numOfPaths); _progress.Complete(); }
private void DeleteFile(FileCompareObject fco, int numOfPaths, int srcFilePos) { bool changed = false; // Loop through all the files under this node. for (int i = 0; i < numOfPaths; i++) { if (i != srcFilePos && fco.Priority[i] != fco.Priority[srcFilePos]) { string destFile = Path.Combine(fco.GetSmartParentPath(i), fco.Name); try { if (_syncConfig.ArchiveLimit > 0) { CommonMethods.ArchiveFile(destFile, _syncConfig.ArchiveName, _syncConfig.ArchiveLimit); ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ARCHIVED, "File archived " + destFile)); } } catch (ArchiveFileException) { fco.FinalState[i] = FinalState.Error; ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error archiving file " + destFile)); } try { if (_syncConfig.Recycle) CommonMethods.DeleteFileToRecycleBin(destFile); else CommonMethods.DeleteFile(destFile); fco.Exists[i] = false; // Update the state of the node after it has been deleted. fco.FinalState[i] = FinalState.Deleted; //Set the FinalState to deleted. changed = true; // Since at least one file has changed, set changed to true. if (_syncConfig.Recycle) ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_DELETED, "File deleted to recycle bin " + destFile)); else ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_DELETED, "File deleted " + destFile)); } catch (DeleteFileException) { fco.FinalState[i] = FinalState.Error; ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error deleting file " + destFile)); } } else // If the priority is equal, it will also mean that the file at position i has been deleted. { fco.FinalState[i] = fco.MetaExists[i] ? FinalState.Deleted : FinalState.Unchanged; // If meta exists, set the change type to deleted. Otherwise set it as unchanged. changed = true; } } fco.FinalState[srcFilePos] = changed ? FinalState.Deleted : FinalState.Unchanged; // If at least one file has been deleted (changed), set the finalstate of the source file to deleted. Otherwise set it as unchanged. }
private void CopyFile(FileCompareObject fco, int numOfPaths, int srcFilePos) { string src = Path.Combine(fco.GetSmartParentPath(srcFilePos), fco.Name); // Loop through all the files under this node. for (int i = 0; i < numOfPaths; i++) { if (i != srcFilePos && fco.Parent.FinalState[i] != FinalState.Deleted) { // Only process if the priority is not equal (implies lower priority actually). if (fco.Priority[i] != fco.Priority[srcFilePos]) { string destFile = Path.Combine(fco.GetSmartParentPath(i), fco.Name); bool fileExists = fco.Exists[i]; try { if (fileExists) { if (_syncConfig.ArchiveLimit > 0) { CommonMethods.ArchiveFile(destFile, _syncConfig.ArchiveName, _syncConfig.ArchiveLimit); ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ARCHIVED, "File archived " + destFile)); } if (_syncConfig.Recycle) { CommonMethods.DeleteFileToRecycleBin(destFile); ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_DELETED, "File deleted to recycle bin " + destFile)); } } } catch (ArchiveFileException) { ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error archiving file " + destFile)); } catch (DeleteFileException) { ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error delete file to recycle bin " + destFile)); } try { CommonMethods.CopyFile(src, destFile); fco.CreationTimeUtc[i] = File.GetCreationTimeUtc(destFile).Ticks; // Creation time will be different since it's a new file, thus we get it from the actual file. fco.LastWriteTimeUtc[i] = File.GetLastWriteTimeUtc(destFile).Ticks; // Last modified/write time may be different, thus we get it from the actual file. // Duplicate source file information to the destination fco.Exists[i] = true; fco.FinalState[i] = fileExists ? FinalState.Updated : FinalState.Created; fco.Hash[i] = fco.Hash[srcFilePos]; fco.Length[i] = fco.Length[srcFilePos]; if (fileExists) ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_MODIFIED, "File updated from " + src + " to " + destFile)); else ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_CREATED, "File copied from " + src + " to " + destFile)); } catch (CopyFileException) { fco.FinalState[i] = FinalState.Error; if (fileExists) ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error updating file from " + src + " to " + destFile)); else ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error copying file from " + src + " to " + destFile)); } } else { // Set FinalState to Unchanged if the hash in the metadata is the same as the actual file. // Otherwise, it is possible that it is the exact same file as the source file, but was not saved to // meta previously, thus we set the FinalState to created. fco.FinalState[i] = (fco.MetaHash[i] == fco.Hash[i]) ? FinalState.Unchanged : FinalState.Created; } } } // Set the FinalState to Unchanged if it already exists in metadata, and the hash in the metadata is the same as that of the actual file. // Otherwise set it to Created. fco.FinalState[srcFilePos] = (fco.MetaExists[srcFilePos] && fco.MetaHash[srcFilePos] == fco.Hash[srcFilePos]) ? FinalState.Unchanged : FinalState.Created; }
// Do nothing for file public void Visit(FileCompareObject file, int numOfPaths) { }
/// <summary> /// Processes and adds files to the tree. /// </summary> /// <param name="folder"><see cref="FolderCompareObject"/> to process and add files to.</param> /// <param name="numOfPaths">The total number of sync folders.</param> /// <param name="f">The <see cref="DirectoryInfo"/> to process and get files from.</param> /// <param name="index">The index indicating which sync folder it belongs to.</param> private void ProcessFiles(FolderCompareObject folder, int numOfPaths, DirectoryInfo f, int index) { try { FileInfo[] fileInfos = f.GetFiles(); foreach (FileInfo info in fileInfos) { if (_filterChain.ApplyFilter(_filter, info.FullName)) { if (_progress != null) { _progress.Message = info.FullName; _progress.Update(); } BaseCompareObject o = folder.GetChild(info.Name); // Gets a child with the same name. FileCompareObject fco = null; bool conflict = false; if (o == null) // If o is null, create a new file compare object fco = new FileCompareObject(info.Name, numOfPaths, folder); else { try { fco = (FileCompareObject)o; // Case o to a FileCompareObject is o is not null } catch (InvalidCastException) // If invalid cast, it means there is a FolderCompareObject with the exact same name. { _typeConflicts.Add(info.FullName); // Add to to conflicts conflict = true; ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write( new LogData(LogEventType.FSCHANGE_CONFLICT, "Conflicted file detected " + info.FullName)); } } if (!conflict) { fco.CreationTimeUtc[index] = info.CreationTimeUtc.Ticks; fco.LastWriteTimeUtc[index] = info.LastWriteTimeUtc.Ticks; fco.Length[index] = info.Length; fco.Exists[index] = true; if (o == null) folder.AddChild(fco); // Add the newly created FileCompareObject to this current folder } } } } catch (UnauthorizedAccessException e) { ServiceLocator.GetLogger(ServiceLocator.DEBUG_LOG).Write(e); ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.UNKNOWN, "Error retrieving contents of folder due to unauthorized access.")); } catch (DirectoryNotFoundException e) { ServiceLocator.GetLogger(ServiceLocator.DEBUG_LOG).Write(e); ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.UNKNOWN, "Error retrieving contents of folder due to directory not being found.")); } }
// Sets the parent of a file to dirty if necessary private static void SetFileParentDirty(FileCompareObject file, int numOfPaths) { int numExists = 0; int posExists = -1; SetFileSystemObjectDirty(file, numOfPaths, ref numExists, ref posExists); if (numExists == 1) { if (file.ChangeType[posExists] == MetaChangeType.New || file.ChangeType[posExists] == MetaChangeType.Update) file.Parent.Dirty = true; } }
// Loads the xml document , and populate the file's data from the meta data private void PopulateFromMetaData(FileCompareObject file, XmlNode node, int counter) { XmlNodeList childNodeList = node.ChildNodes; for (int i = 0; i < childNodeList.Count; i++) { XmlNode childNode = childNodeList[i]; switch (childNode.Name) { case CommonXMLConstants.NodeSize: file.MetaLength[counter] = long.Parse(childNode.InnerText); break; case CommonXMLConstants.NodeHash: file.MetaHash[counter] = childNode.InnerText; break; case CommonXMLConstants.NodeLastModifiedUtc: file.MetaLastWriteTimeUtc[counter] = long.Parse(childNode.InnerText); break; case CommonXMLConstants.NodeLastCreatedUtc: file.MetaCreationTimeUtc[counter] = long.Parse(childNode.InnerText); break; case CommonXMLConstants.NodeLastUpdatedUtc: file.MetaUpdated[counter] = long.Parse(childNode.InnerText); break; } } file.MetaExists[counter] = true; }
//Populates the file from meta data. If it does not exist, try to look it up from the last known state //instead private FileCompareObject PopulateFileWithMetaData(XmlDocument xmlDoc, FileCompareObject file, int counter) { XmlNode node = xmlDoc.SelectSingleNode(CommonXMLConstants.XPathExpr + CommonXMLConstants.XPathFile + "[name=" + CommonMethods.ParseXPathString(file.Name) + "]"); if (node != null) { PopulateFromMetaData(file, node, counter); } else { PopulateFromLastKnownState(file, counter); } return file; }
// For any XMLCompareObjects in the list , create them as a node and append them to the folder node private void AddFileToChild(List<XMLCompareObject> xmlFileList, FolderCompareObject folder, int counter, int length) { for (int i = 0; i < xmlFileList.Count; i++) { BaseCompareObject o = folder.GetChild(xmlFileList[i].Name); FileCompareObject fco; if (o == null) fco = new FileCompareObject(xmlFileList[i].Name, length, folder); else fco = (FileCompareObject)o; fco.MetaCreationTimeUtc[counter] = xmlFileList[i].CreatedTimeUtc; fco.MetaHash[counter] = xmlFileList[i].Hash; fco.MetaLastWriteTimeUtc[counter] = xmlFileList[i].LastModifiedTimeUtc; fco.MetaLength[counter] = xmlFileList[i].Size; fco.MetaUpdated[counter] = xmlFileList[i].LastUpdatedTimeUtc; fco.MetaExists[counter] = true; if (o == null) folder.AddChild(fco); } }
// Based on the file and counter , it will load the xml document and delete the file node private void DeleteFileObject(FileCompareObject file, int counter) { XmlDocument xmlDoc = new XmlDocument(); string parentPath = file.GetSmartParentPath(counter); string xmlPath = Path.Combine(parentPath, CommonXMLConstants.MetadataPath); if (File.Exists(xmlPath)) { CommonMethods.LoadXML(ref xmlDoc, xmlPath); XmlNode node = xmlDoc.SelectSingleNode(CommonXMLConstants.XPathExpr + CommonXMLConstants.XPathFile + "[name=" + CommonMethods.ParseXPathString(file.Name) + "]"); if (node == null) return; node.ParentNode.RemoveChild(node); CommonMethods.SaveXML(ref xmlDoc, xmlPath); } }
// Based on the file and counter , it will load the xml document and rename the file iin the meta data private void RenameFileObject(FileCompareObject file, int counter) { XmlDocument xmlDoc = new XmlDocument(); string xmlPath = Path.Combine(file.GetSmartParentPath(counter), CommonXMLConstants.MetadataPath); CommonMethods.CreateFileIfNotExist(file.GetSmartParentPath(counter)); CommonMethods.LoadXML(ref xmlDoc, xmlPath); XmlNode node = xmlDoc.SelectSingleNode(CommonXMLConstants.XPathExpr + CommonXMLConstants.XPathFile + "[name=" + CommonMethods.ParseXPathString(file.Name) + "]"); if (node == null) { CreateFileObject(file, counter, true); return; } XmlNodeList childNodeList = node.ChildNodes; for (int i = 0; i < childNodeList.Count; i++) { XmlNode nodes = childNodeList[i]; switch (nodes.Name) { case CommonXMLConstants.NodeName: nodes.InnerText = file.NewName; break; case CommonXMLConstants.NodeLastUpdatedUtc: nodes.InnerText = _dateTime.ToString(); break; } } CommonMethods.SaveXML(ref xmlDoc, xmlPath); }
// Based on the file and the counter , load the xml document and create a new file node private void CreateFileObject(FileCompareObject file, int counter, bool useNewName) { XmlDocument xmlDoc = new XmlDocument(); string xmlPath = Path.Combine(file.GetSmartParentPath(counter), CommonXMLConstants.MetadataPath); CommonMethods.CreateFileIfNotExist(file.GetSmartParentPath(counter)); CommonMethods.LoadXML(ref xmlDoc, xmlPath); CommonMethods.DoFileCleanUp(xmlDoc, useNewName ? file.NewName : file.Name); XmlText hashText = xmlDoc.CreateTextNode(file.Hash[counter]); XmlText nameText = xmlDoc.CreateTextNode(useNewName ? file.NewName : file.Name); XmlText sizeText = xmlDoc.CreateTextNode(file.Length[counter].ToString()); XmlText lastModifiedUtcText = xmlDoc.CreateTextNode(file.LastWriteTimeUtc[counter].ToString()); XmlText lastCreatedUtcText = xmlDoc.CreateTextNode(file.CreationTimeUtc[counter].ToString()); XmlText lastUpdatedUtcText = xmlDoc.CreateTextNode(_dateTime.ToString()); XmlElement fileElement = xmlDoc.CreateElement(CommonXMLConstants.NodeFile); XmlElement hashElement = xmlDoc.CreateElement(CommonXMLConstants.NodeHash); XmlElement nameElement = xmlDoc.CreateElement(CommonXMLConstants.NodeName); XmlElement sizeElement = xmlDoc.CreateElement(CommonXMLConstants.NodeSize); XmlElement lastModifiedElement = xmlDoc.CreateElement(CommonXMLConstants.NodeLastModifiedUtc); XmlElement lastCreatedElement = xmlDoc.CreateElement(CommonXMLConstants.NodeLastCreatedUtc); XmlElement lastUpdatedElement = xmlDoc.CreateElement(CommonXMLConstants.NodeLastUpdatedUtc); hashElement.AppendChild(hashText); nameElement.AppendChild(nameText); sizeElement.AppendChild(sizeText); lastModifiedElement.AppendChild(lastModifiedUtcText); lastCreatedElement.AppendChild(lastCreatedUtcText); lastUpdatedElement.AppendChild(lastUpdatedUtcText); fileElement.AppendChild(nameElement); fileElement.AppendChild(sizeElement); fileElement.AppendChild(hashElement); fileElement.AppendChild(lastModifiedElement); fileElement.AppendChild(lastCreatedElement); fileElement.AppendChild(lastUpdatedElement); XmlNode node = xmlDoc.SelectSingleNode(CommonXMLConstants.XPathExpr); if (node == null) return; node.AppendChild(fileElement); CommonMethods.SaveXML(ref xmlDoc, xmlPath); DeleteFileLastKnownState(file, counter); }
// Checks the File's FinalState and process them accordingly private bool ProcessMetaChangeType(FileCompareObject file, int counter) { FinalState? changeType = file.FinalState[counter]; bool needLastKnownState = false; switch (changeType) { case FinalState.Created: CreateFileObject(file, counter, false); break; case FinalState.Updated: UpdateFileObject(file, counter); break; case FinalState.Deleted: DeleteFileObject(file, counter); needLastKnownState = true; break; case FinalState.Renamed: RenameFileObject(file, counter); needLastKnownState = true; break; case FinalState.CreatedRenamed: CreateFileObject(file, counter, true); DeleteFileObject(file, counter); needLastKnownState = true; break; } return needLastKnownState; }
/// <summary> /// For every FileCompareObject, check which path has the max priority and then decide /// what sync operation should be approriate for each FileCompareObject. Once the path of the max /// priority has been set, for every other path which is not the max priority, populate the datagrid /// with by adding a DataRow for each operation that needs to be done. /// </summary> /// <param name="fco">FileCompareObject to be visited</param> /// <param name="numOfPaths">Total number of paths present in the particular FileCompareObject</param> public void Visit(FileCompareObject fco, int numOfPaths) { if (fco.Invalid) return; // find max priortiy position int maxPriorityPos = fco.SourcePosition; if (fco.Priority[maxPriorityPos] > 0) { for (int i = 0; i < numOfPaths; i++) { if (i != maxPriorityPos && fco.Priority[i] != fco.Priority[maxPriorityPos]) { string operation = string.Empty, source = string.Empty, dest = string.Empty, tooltip = string.Empty; DataRow row = SyncData.NewRow(); switch (fco.ChangeType[maxPriorityPos]) { // if the MetaChangeType is of New, Update, Change case MetaChangeType.New: case MetaChangeType.Update: case MetaChangeType.NoChange: source = Path.Combine(fco.GetSmartParentPath(maxPriorityPos), fco.Name); dest = Path.Combine(fco.GetSmartParentPath(i), fco.Name); operation = fco.Exists[i] ? UpdateArrow : CopyArrow; tooltip = fco.Exists[i] ? FileUpdateToolTip : FileCopyToolTip; break; // if the MetaChangeType is of Delete type case MetaChangeType.Delete: source = Path.Combine(fco.GetSmartParentPath(maxPriorityPos), fco.Name); dest = Path.Combine(fco.GetSmartParentPath(i), fco.Name); operation = DeleteArrow; tooltip = FileDeleteToolTip; break; // if the MetaChangeType is of Rename type case MetaChangeType.Rename: string oldName = Path.Combine(fco.GetSmartParentPath(i), fco.Name); source = File.Exists(oldName) ? oldName : Path.Combine(fco.GetSmartParentPath(maxPriorityPos), fco.NewName); dest = Path.Combine(fco.GetSmartParentPath(i), fco.NewName); operation = File.Exists(oldName) ? RenameArrow : CopyArrow; tooltip = File.Exists(oldName) ? FileRenameToolTip : FileCopyToolTip; break; } // set all properties of the DataRow which has been decided by the switch case above row[Source] = source; row[Operation] = operation; row[Dest] = dest; row[Tooltip] = tooltip; row[SourceIcon] = FileIcon; row[DestIcon] = FileIcon; // Set Date/Time/Length of FCO. DateTime sourceDateTime = new DateTime(fco.LastWriteTimeUtc[maxPriorityPos]); row[SourceLastModifiedDate] = sourceDateTime.ToShortDateString(); row[SourceLastModifiedTime] = sourceDateTime.ToShortTimeString(); row[SourceSize] = fco.Length[maxPriorityPos]; // Check if a particular file path exists if (fco.Exists[i]) { DateTime destDateTime = new DateTime(fco.LastWriteTimeUtc[i]); row[DestLastModifiedDate] = destDateTime.ToShortDateString(); row[DestLastModifiedTime] = destDateTime.ToShortTimeString(); row[DestSize] = fco.Length[i]; } else { row[DestLastModifiedDate] = "-"; row[DestSize] = "-"; } SyncData.Rows.Add(row); } } } }
private void MoveFile(FileCompareObject fco, int numOfPaths, int srcFilePos) { bool changed = false; for (int i = 0; i < numOfPaths; i++) { if (i != srcFilePos && fco.Priority[i] != fco.Priority[srcFilePos]) { string oldName = Path.Combine(fco.GetSmartParentPath(i), fco.Name); string newName = Path.Combine(fco.GetSmartParentPath(i), fco.NewName); string srcName = Path.Combine(fco.GetSmartParentPath(srcFilePos), fco.NewName); try { if (File.Exists(oldName)) // If the old file exists, simply rename it. { CommonMethods.MoveFile(oldName, newName); fco.FinalState[i] = FinalState.Renamed; ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_RENAMED, "File renamed from " + oldName + " to " + newName)); } else // If the old file does not exists, copy the source file over instead. { CommonMethods.CopyFile(srcName, newName); FileCompareObject srcFile = fco.Parent.GetChild(fco.NewName) as FileCompareObject; fco.CreationTimeUtc[i] = File.GetCreationTimeUtc(newName).Ticks; fco.LastWriteTimeUtc[i] = File.GetLastAccessTimeUtc(newName).Ticks; fco.Exists[i] = true; fco.Hash[i] = srcFile.Hash[srcFilePos]; fco.Length[i] = srcFile.Length[srcFilePos]; fco.FinalState[i] = FinalState.CreatedRenamed; ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_CREATED, "File copied from " + srcName + " to " + newName)); } changed = true; // Set changed to true since the change has propagated to at least one file } catch (MoveFileException) { fco.FinalState[i] = FinalState.Error; ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error renaming file from " + oldName + " to " + newName)); } catch (CopyFileException) { fco.FinalState[i] = FinalState.Error; ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error copying file from " + srcName + " to " + newName)); } } else { fco.FinalState[i] = FinalState.Renamed; // Set the FinalState to renamed if the file has the same priority, implying it has the same name as the new file name changed = true; } } fco.FinalState[srcFilePos] = changed ? FinalState.Renamed : FinalState.Unchanged; // Set the FinalState to renamed if at least one file has changed. Otherwise, set it as unchanged. }
// Helper method to handle file deletes. private static bool FileDeleteHelper(FileCompareObject file, int numOfPaths) { List<int> deleteIndexes = new List<int>(); // Used to store the indexes of deleted files bool stop = false; for (int i = 0; i < numOfPaths; i++) { if (stop) break; if (file.ChangeType[i] == MetaChangeType.Delete) { deleteIndexes.Add(i); for (int j = 0; j < numOfPaths; j++) { if (file.Exists[j]) // If file exists in one of the indexes { if (file.MetaUpdated[j] > file.MetaUpdated[i]) // If the meta updated of j is more than i, void the delete { deleteIndexes.Clear(); // Clear all delete indexes stop = true; break; } } } } else if (file.ChangeType[i] != MetaChangeType.NoChange && file.ChangeType[i] != null) { deleteIndexes.Clear(); // Clear the delete indexes as long as there's a rename/update/new detected break; } } if (deleteIndexes.Count > 0) { file.SourcePosition = deleteIndexes[0]; // Set the source position to the first delete index found (convenience) foreach (int i in deleteIndexes) file.Priority[i] = 1; // Set all delete indexes to priority 1. return true; // Return true since delete operation will occur } return false; // Return false since delete operation will not occur }
// Helper method to handle file creations and updates private static void FileCreateUpdateHelper(FileCompareObject file, int numOfPaths) { int mostUpdatedPos = 0; for (int i = 0; i < numOfPaths; i++) { if (file.Exists[i]) { mostUpdatedPos = i; // Set the most update position to the first file that exists break; } } file.Priority[mostUpdatedPos] = 1; // Set the priority of the first file that exists to 1. All other indexes will be 0. for (int i = mostUpdatedPos + 1; i < numOfPaths; i++) { if (!file.Exists[i]) { file.Priority[i] = -1; // If file does not exists we set the priority to -1. continue; } if (file.Length[mostUpdatedPos] != file.Length[i] || file.Hash[mostUpdatedPos] != file.Hash[i]) { // If last write time of i is more updated than that of the most updated position if (file.LastWriteTimeUtc[i] > file.LastWriteTimeUtc[mostUpdatedPos]) { file.Priority[i] = file.Priority[mostUpdatedPos] + 1; // Set the priority of i as the priority of most updated position + 1 mostUpdatedPos = i; // Set the most updated position to i } else if (file.LastWriteTimeUtc[i] == file.LastWriteTimeUtc[mostUpdatedPos]) // If the hash/length is not equal but the last modified time is, it is a conflict. { //Conflict file.Priority[i] = file.Priority[mostUpdatedPos] - 1; // Decrement the priority of this conflicted file file.ConflictPositions.Add(i); // Add this conflicted file to the list of conflict positions ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_CONFLICT, "Conflicted file detected " + Path.Combine(file.GetSmartParentPath(i), file.Name))); } } else { file.Priority[i] = file.Priority[mostUpdatedPos]; // If length and hash are equal, set the priority of i to the priority of most updated position } } file.SourcePosition = mostUpdatedPos; // Set the source position to the most updated position }
// Populate the file's details from the last known state private void PopulateFromLastKnownState(FileCompareObject file, int counter) { string path = Path.Combine(file.GetSmartParentPath(counter), CommonXMLConstants.LastKnownStatePath); if (File.Exists(path)) { XmlDocument lastKnownXMLDoc = new XmlDocument(); CommonMethods.LoadXML(ref lastKnownXMLDoc, path); XmlNode fileNode = lastKnownXMLDoc.SelectSingleNode(CommonXMLConstants.XPathLastKnownState + CommonXMLConstants.XPathFile + "[name=" + CommonMethods.ParseXPathString(file.Name) + "]"); if (fileNode != null) { XmlNodeList nodeList = fileNode.ChildNodes; for (int i = 0; i < nodeList.Count; i++) { XmlNode childNode = nodeList[i]; switch (childNode.Name) { case CommonXMLConstants.NodeAction: string action = childNode.InnerText; file.LastKnownState[counter] = action.Equals(CommonXMLConstants.ActionDeleted) ? LastKnownState.Deleted : LastKnownState.Renamed; break; case CommonXMLConstants.NodeLastModifiedUtc: file.MetaLastWriteTimeUtc[counter] = long.Parse(childNode.InnerText); break; case CommonXMLConstants.NodeHash: file.MetaHash[counter] = childNode.InnerText; break; case CommonXMLConstants.NodeLastUpdatedUtc: file.MetaUpdated[counter] = long.Parse(childNode.InnerText); break; } } } } }
// Deletes a file node by searching for the name in the last known state file. private void DeleteFileLastKnownState(FileCompareObject file, int counter) { string todoXMLPath = Path.Combine(file.GetSmartParentPath(counter), CommonXMLConstants.LastKnownStatePath); if (!File.Exists(todoXMLPath)) return; XmlDocument todoXMLDoc = new XmlDocument(); CommonMethods.LoadXML(ref todoXMLDoc, todoXMLPath); XmlNode fileNode = todoXMLDoc.SelectSingleNode(CommonXMLConstants.XPathLastKnownState + CommonXMLConstants.XPathFile + "[name=" + CommonMethods.ParseXPathString(file.Name) + "]"); if (fileNode != null) fileNode.ParentNode.RemoveChild(fileNode); CommonMethods.SaveXML(ref todoXMLDoc, todoXMLPath); }
// Checks if the last known state document exists and calls the AppendActionFileLastKnownState method private void GenerateFileLastKnownState(FileCompareObject file, int numOfPaths) { for (int i = 0; i < numOfPaths; i++) { string parentPath = file.GetSmartParentPath(i); XmlDocument xmlTodoDoc = new XmlDocument(); string todoPath = Path.Combine(parentPath, CommonXMLConstants.LastKnownStatePath); CommonMethods.CreateLastKnownStateFile(parentPath); CommonMethods.LoadXML(ref xmlTodoDoc, todoPath); CommonMethods.DoFileLastKnownCleanUp(xmlTodoDoc,file.Name); AppendActionFileLastKnownState(xmlTodoDoc, file, i, CommonXMLConstants.ActionDeleted); CommonMethods.SaveXML(ref xmlTodoDoc, todoPath); } }
/// <summary> /// Detects possible file renames and handles them accordingly. /// </summary> /// <param name="file">The <see cref="FileCompareObject"/> to process.</param> /// <param name="numOfPaths">The total number of folders to sync.</param> private static void DetectFileRename(FileCompareObject file, int numOfPaths) { FileCompareObject result = null; Dictionary<string, List<int>> newNames = new Dictionary<string, List<int>>(StringComparer.OrdinalIgnoreCase); List<int> unchangedIndexes = new List<int>(); for (int i = 0; i < numOfPaths; i++) { switch (file.ChangeType[i]) { case MetaChangeType.New: case MetaChangeType.Update: return; // Return as long as one of the meta change type is new or updated. } } for (int i = 0; i < numOfPaths; i++) { switch (file.ChangeType[i]) { case MetaChangeType.Delete: // All deletes are also possible renames, thus we call GetIdenticalFile to look for an identical file with a different name, if any FileCompareObject f = file.Parent.GetIdenticalFile(file.Name, file.MetaHash[i], file.MetaCreationTimeUtc[i], i); // If an identical file is found if (f != null) { List<int> pos; if (newNames.TryGetValue(f.Name, out pos)) { pos.Add(i); // Add the position to the existing list of names } else { // Create a new list to add the position if the name does not currently exist pos = new List<int>(); pos.Add(i); newNames.Add(f.Name, pos); result = f; // Point result to f } } break; case MetaChangeType.NoChange: unchangedIndexes.Add(i); break; } } if (newNames.Count == 1) // Meaning only one new/possible rename was detected, proceed with the rename. { file.NewName = result.Name; // Set the new name of this file to the name of the found file. foreach (int i in newNames[result.Name]) file.ChangeType[i] = MetaChangeType.Rename; // Set all the positions to change type rename. result.Invalid = true; // Set the found file to invalid, no need to process it. file.Parent.Dirty = true; // Set the current file's parent to dirty. } else if (newNames.Count > 1) // Meaning multiple renames are detected, void all the renames and treat each type as new. { foreach (int i in unchangedIndexes) file.ChangeType[i] = MetaChangeType.New; // Set all the unchanged to new since they will be treated as new, } }
// For each XMLCompareObjects in the list , create a node and append them to the root node private void AddFileToRoot(List<XMLCompareObject> xmlFileList, RootCompareObject root, int counter, int length) { if (xmlFileList.Count == 0) return; for (int i = 0; i < xmlFileList.Count; i++) { BaseCompareObject o = root.GetChild(xmlFileList[i].Name); FileCompareObject fco; if (o == null) fco = new FileCompareObject(xmlFileList[i].Name, length, root); else fco = (FileCompareObject)o; fco.MetaCreationTimeUtc[counter] = xmlFileList[i].CreatedTimeUtc; fco.MetaHash[counter] = xmlFileList[i].Hash; fco.MetaLastWriteTimeUtc[counter] = xmlFileList[i].LastModifiedTimeUtc; fco.MetaLength[counter] = xmlFileList[i].Size; fco.MetaUpdated[counter] = xmlFileList[i].LastUpdatedTimeUtc; fco.MetaExists[counter] = true; if (o == null) root.AddChild(fco); } }
// Creates a file node in the last known state file based on the values in FileCompareObject private void AppendActionFileLastKnownState(XmlDocument xmlDoc, FileCompareObject file, int counter, string changeType) { XmlText hashText = xmlDoc.CreateTextNode(file.MetaHash[counter]); XmlText actionText = xmlDoc.CreateTextNode(changeType); XmlText lastModifiedUtcText = xmlDoc.CreateTextNode(file.MetaLastWriteTimeUtc[counter].ToString()); XmlText nameText = xmlDoc.CreateTextNode(file.Name); XmlText lastUpdatedUtcText = xmlDoc.CreateTextNode(_dateTime.ToString()); XmlElement fileElement = xmlDoc.CreateElement(CommonXMLConstants.NodeFile); XmlElement nameElement = xmlDoc.CreateElement(CommonXMLConstants.NodeName); XmlElement hashElement = xmlDoc.CreateElement(CommonXMLConstants.NodeHash); XmlElement actionElement = xmlDoc.CreateElement(CommonXMLConstants.NodeAction); XmlElement lastModifiedElement = xmlDoc.CreateElement(CommonXMLConstants.NodeLastModifiedUtc); XmlElement lastUpdatedElement = xmlDoc.CreateElement(CommonXMLConstants.NodeLastUpdatedUtc); hashElement.AppendChild(hashText); actionElement.AppendChild(actionText); lastModifiedElement.AppendChild(lastModifiedUtcText); nameElement.AppendChild(nameText); lastUpdatedElement.AppendChild(lastUpdatedUtcText); fileElement.AppendChild(nameElement); fileElement.AppendChild(actionElement); fileElement.AppendChild(hashElement); fileElement.AppendChild(lastModifiedElement); fileElement.AppendChild(lastUpdatedElement); XmlNode rootNode = xmlDoc.SelectSingleNode(CommonXMLConstants.XPathLastKnownState); if (rootNode == null) return; rootNode.AppendChild(fileElement); }