/Utilities/Helpers/FileHelper.cs
# · C# · 2070 lines · 1223 code · 191 blank · 656 comment · 106 complexity · b7a0f155bd58030704580436144e2d15 MD5 · raw file
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Security.Cryptography;
- using System.Text;
- using System.Threading;
- using NUnit.Framework;
-
- namespace Delta.Utilities.Helpers
- {
- /// <summary>
- /// File helper class to get text lines, number of text lines, etc. This
- /// extends the existing File and Path functionality of .NET.
- /// <para />
- /// This class also abstracts the use of File.Exists and File.Open for
- /// IsolatedStorage classes are used, which just allow access to the
- /// current directory, which is all we need in the engine internally).
- /// </summary>
- public static class FileHelper
- {
- #region Constants
- /// <summary>
- /// Represents the separator for folders in file paths on microsoft
- /// platforms.
- /// </summary>
- public static readonly char FolderSeparator = '\\';
-
- /// <summary>
- /// Represents the separator for folders in file paths on Unix platforms.
- /// </summary>
- public static readonly char AlternateFolderSeparator = '/';
-
- /// <summary>
- /// Both folder separators together in one char array, used for GetFilename
- /// </summary>
- public static readonly char[] AllFolderSeparators =
- new[]
- {
- FolderSeparator, AlternateFolderSeparator
- };
-
- /// <summary>
- /// The typical file extension separator is just a dot: .
- /// </summary>
- public const char FileExtensionSeperator = '.';
- #endregion
-
- #region Delegates
- internal delegate bool ExistsDelegate(string path);
-
- internal delegate void CopyDelegate(string sourceFile,
- string destinationFile);
-
- internal delegate void MoveDelegate(string sourceFile,
- string destinationFile);
-
- internal delegate FileStream OpenDelegate(string filePath, FileMode mode,
- FileAccess access, FileShare share);
-
- internal delegate void DeleteDelegate(string filePath);
-
- internal delegate bool FileIsNewerDelegate(string fileToCheckIfNewer,
- string originalFile);
-
- /// <summary>
- /// Compare two files with the help of the MD5-checksum
- /// </summary>
- /// <param name="fileName1">Filename 1</param>
- /// <param name="fileName2">Filename 2</param>
- /// <returns>True if both file contents are the same</returns>
- internal delegate bool CompareFilesDelegate(string fileName1,
- string fileName2);
-
- /// <summary>
- /// Check if file exists and is not written to recently. This is mostly
- /// used for update checks to see if we can assume this file is complete
- /// and we can now open it, etc. (e.g. used in the RestartTool).
- /// </summary>
- /// <param name="filename">File to check</param>
- /// <returns>
- /// True if the file exists and was not written to recently.
- /// </returns>
- internal delegate bool FileNotWrittenToDelegate(string filename);
- #endregion
-
- #region Exists (Static)
- /// <summary>
- /// Check if the file exists.
- /// </summary>
- /// <param name="filename">Filename</param>
- /// <returns>filename</returns>
- public static bool Exists(string filename)
- {
- return ExistsCallback(filename);
- }
- #endregion
-
- #region Copy (Static)
- /// <summary>
- /// Copy the source file to the destination file.
- /// Note: this method overwrites existing destination files!
- /// </summary>
- /// <param name="sourceFile">Source filepath.</param>
- /// <param name="destinationFile">Destination Filepath.</param>
- public static void Copy(string sourceFile, string destinationFile)
- {
- CopyCallback(sourceFile, destinationFile);
- }
- #endregion
-
- #region Move (Static)
- /// <summary>
- /// Move the source file to the destination file.
- /// </summary>
- /// <param name="sourceFile">Source filepath.</param>
- /// <param name="destinationFile">Destination filepath.</param>
- public static void Move(string sourceFile, string destinationFile)
- {
- MoveCallback(sourceFile, destinationFile);
- }
- #endregion
-
- #region Open (Static)
- /// <summary>
- /// Opens a the given file (will not create it if it doesn't exists) on
- /// platforms where this is possible.
- /// <para />
- /// Note: The file-mode is "Open" only and the file-access + file-share
- /// mode is "Read".
- /// </summary>
- public static FileStream Open(string filePath)
- {
- return Open(filePath, FileMode.Open, FileAccess.Read,
- FileShare.Read);
- }
-
- /// <summary>
- /// Open, will use File.Open on platforms where this is possible.
- /// </summary>
- public static FileStream Open(string filePath, FileMode mode,
- FileAccess access, FileShare share)
- {
- return OpenCallback(filePath, mode, access, share);
- }
- #endregion
-
- #region Rename (Static)
- /// <summary>
- /// Rename the file.
- /// </summary>
- /// <param name="sourceFile">Path to the file to rename.</param>
- /// <param name="destFile">Path to the file after rename.</param>
- /// <returns>True if succeeded otherwise false.</returns>
- public static bool Rename(string sourceFile, string destFile)
- {
- if (Exists(sourceFile) == false)
- {
- return false;
- }
-
- try
- {
- Move(sourceFile, destFile);
- }
- catch
- {
- return false;
- }
-
- return true;
- }
- #endregion
-
- #region GetFilename (Static)
- /// <summary>
- /// Extracts filename from full path+filename, but don't cuts off the
- /// extension. Can be also used to cut of directories from a path (only
- /// last one will remain).
- /// </summary>
- /// <returns>filename</returns>
- public static string GetFilename(string filePath)
- {
- // Similar code as GetFilename(..), but this method is called more often!
-
- #region Validation
- if (String.IsNullOrEmpty(filePath))
- {
- return "";
- }
- #endregion
-
- // At first we try to find any folder separators (incl. alternate ones)
- int lastSeparatorIndex = filePath.LastIndexOf(FolderSeparator);
- int lastAlternateSeperatorIndex = filePath.LastIndexOf(
- AlternateFolderSeparator);
- // to pick the backmost one
- int backMostIndex = (lastSeparatorIndex > lastAlternateSeperatorIndex)
- ? lastSeparatorIndex
- : lastAlternateSeperatorIndex;
-
- // to get only the filename
- return (backMostIndex != MathHelper.InvalidIndex)
- ? // -> "Path\SubPath\File.tst" -> "File.tst"
- // -> "Path/SubPath\File.tst" -> "File.tst"
- // -> "Path\SubPath/File.tst" -> "File.tst"
- filePath.Substring(backMostIndex + 1)
- : // -> "File.tst" -> "File.tst"
- filePath;
- }
-
- /// <summary>
- /// Extracts filename from full path+filename, but don't cuts off the
- /// extension. Can be also used to cut of directories from a path (only
- /// last one will remain).
- /// </summary>
- public static string GetFilename(string filePath, out string pathOnly)
- {
- #region Validation
- if (String.IsNullOrEmpty(filePath))
- {
- pathOnly = "";
- return "";
- }
- #endregion
-
- // At first we try to find any folder separators (incl. alternate ones)
- int lastSeparatorIndex = filePath.LastIndexOf(FolderSeparator);
- int lastAlternateSeperatorIndex = filePath.LastIndexOf(
- AlternateFolderSeparator);
- // to pick the backmost one
- int backMostIndex = lastSeparatorIndex > lastAlternateSeperatorIndex
- ? lastSeparatorIndex
- : lastAlternateSeperatorIndex;
-
- if (backMostIndex != MathHelper.InvalidIndex)
- {
- // -> "Path\SubPath\File.tst" -> "File.tst"
- // -> "Path/SubPath\File.tst" -> "File.tst"
- // -> "Path\SubPath/File.tst" -> "File.tst"
- pathOnly = filePath.Substring(0, backMostIndex);
- return filePath.Substring(backMostIndex + 1);
- }
- else
- {
- // -> "File.tst" -> "File.tst"
- pathOnly = "";
- return filePath;
- }
- }
-
- /// <summary>
- /// Extracts filename from full path+filename, cuts of extension
- /// if cutExtension is true. Can be also used to cut of directories
- /// from a path (only last one will remain).
- /// </summary>
- public static string GetFilename(string filePath, bool cutExtension)
- {
- #region Validation
- if (String.IsNullOrEmpty(filePath))
- {
- return "";
- }
- #endregion
-
- // At first we try to find any folder separators (incl. alternate ones)
- int lastSeparatorIndex = filePath.LastIndexOf(FolderSeparator);
- int lastAlternateSeperatorIndex = filePath.LastIndexOf(
- AlternateFolderSeparator);
- // to pick the backmost one
- int backMostIndex =
- lastSeparatorIndex > lastAlternateSeperatorIndex
- ? lastSeparatorIndex
- : lastAlternateSeperatorIndex;
-
- // Convert to filename, examples:
- // -> "Path\SubPath\File.tst" -> "File.tst"
- // -> "Path/SubPath\File.tst" -> "File.tst"
- // -> "Path\SubPath/File.tst" -> "File.tst"
- // -> "File.tst" -> "File.tst"
- string filename =
- backMostIndex != MathHelper.InvalidIndex
- ? filePath.Substring(backMostIndex + 1)
- : filePath;
-
- // Return the filename without path (and optionally cut of the extension)
- // -> "File.tst" -> "File"
- return
- cutExtension
- ? CutExtension(filename)
- : filename;
- }
- #endregion
-
- #region IsFilenameOnly (Static)
- /// <summary>
- /// Is filename only
- /// </summary>
- /// <param name="anyFilePath">Any file path</param>
- public static bool IsFilenameOnly(string anyFilePath)
- {
- if (String.IsNullOrEmpty(anyFilePath) ||
- // If this is a filename, it better has an extension. Note: This is
- // also used for GetContentName, so all content follows this rule too.
- HasFileNoExtension(anyFilePath))
- {
- return false;
- }
-
- // Make sure the filename does not contain any path elements
- if (anyFilePath.IndexOf(FolderSeparator) >= 0 ||
- anyFilePath.IndexOf(AlternateFolderSeparator) >= 0)
- {
- return false;
- }
-
- return true;
- }
- #endregion
-
- #region HasFileNoExtension (Static)
- /// <summary>
- /// Has a filename no extension
- /// </summary>
- /// <param name="anyFilePath">Any file path</param>
- /// <returns>True if the given file path has no extension.</returns>
- public static bool HasFileNoExtension(string anyFilePath)
- {
- if (String.IsNullOrEmpty(anyFilePath))
- {
- return false;
- }
-
- // The file extension is always separated with the '.' defined above.
- // If we cannot find it, this file has no extension.
- return anyFilePath.LastIndexOf(FileExtensionSeperator) < 0;
- }
- #endregion
-
- #region GetDirectoryPath (Static)
- /// <summary>
- /// Get directory of path+File, if only a path is given we will cut off
- /// the last sub path!
- /// </summary>
- /// <param name="filePath">File path</param>
- /// <returns>Directory path</returns>
- public static string GetDirectoryPath(string filePath)
- {
- // pathFile must be valid
- if (String.IsNullOrEmpty(filePath))
- {
- return "";
- }
-
- // If pathFile ends with a folder separator, cut that off!
- if (filePath.EndsWith(FolderSeparator.ToString()) ||
- filePath.EndsWith(AlternateFolderSeparator.ToString()))
- {
- filePath = filePath.Substring(0, filePath.Length - 1);
- }
-
- // Normal directory check
- int index = filePath.LastIndexOf(FolderSeparator);
- int alternateIndex = filePath.LastIndexOf(AlternateFolderSeparator);
-
- if (alternateIndex > index)
- {
- index = alternateIndex;
- }
-
- if (index >= 0 &&
- index < filePath.Length)
- {
- // Return directory
- return filePath.Substring(0, index);
- }
-
- // No sub directory found (parent of some directory is "")
- // Only exception is when we are on the file system root level (e.g. c:)
- return
- filePath.Contains(":")
- ? filePath
- : "";
- }
-
- /// <summary>
- /// Get directory sub path of the given path or file path.
- /// (-> e.g. "FileHelper.GetDirectoryPath(@"C:\Ab\cde\App.exe", 2)" will
- /// return "C:\Ab")
- /// </summary>
- /// <param name="filePath">File path</param>
- /// <param name="jumpbackCount">
- /// The number of folders it should be "jumped" back.
- /// </param>
- /// <returns>Directory path</returns>
- public static string GetDirectoryPath(string filePath, int jumpbackCount)
- {
- // pathFile must be valid
- if (String.IsNullOrEmpty(filePath))
- {
- return "";
- }
-
- // At first split the path in its folder (+ filename) parts
- string[] pathParts = filePath.Split(AllFolderSeparators);
-
- // If we less or equal (folder) parts than folders we want to jump back
- int remainingPartCount = pathParts.Length - jumpbackCount;
- if (remainingPartCount <= 0)
- {
- // then we will just return an empty path
- return "";
- }
-
- // Now just count the number of characters we have to cut off
- int cutOffCount = 0;
- for (int index = remainingPartCount; index < pathParts.Length; index++)
- {
- cutOffCount += pathParts[index].Length + 1;
- }
-
- // And finally just return the remaining path
- return filePath.Substring(0, filePath.Length - cutOffCount);
- }
- #endregion
-
- #region GetFirstDirectoryName (Static)
- /// <summary>
- /// Get first directory of path or file path! Returns "c:" for "C:\test.x"
- /// or "bla" for "bla\blub\honk.txt".
- /// </summary>
- /// <param name="pathFile">Path file to search</param>
- /// <returns>First found directory part</returns>
- public static string GetFirstDirectoryName(string pathFile)
- {
- if (String.IsNullOrEmpty(pathFile))
- {
- return "";
- }
-
- string[] parts = pathFile.Split(new char[]
- {
- '/', '\\'
- });
- // Just return the first part
- return parts[0];
- }
- #endregion
-
- #region GetLastDirectoryName (Static)
- /// <summary>
- /// Get last directory of path+File, if only a path is given we will cut
- /// off the last sub path! Returns "Models" for "C:\aoeus\Models\test.x".
- /// Warning: If you just use a path, the results might be different from
- /// what you want (use GetFilename instead): ""C:\aoeus\Models" will return
- /// "aoeus" and not "Models".
- /// </summary>
- /// <param name="pathFile">Path file to search</param>
- /// <returns>Last found directory</returns>
- public static string GetLastDirectoryName(string pathFile)
- {
- if (String.IsNullOrEmpty(pathFile))
- {
- return "";
- }
-
- string dir1 = GetDirectoryPath(pathFile);
- string dir2 = GetDirectoryPath(dir1);
-
- // No other sub directory found?
- if (String.IsNullOrEmpty(dir2))
- {
- return dir1;
- }
-
- // Remove dir2 part including "\\" or "/"
- return dir1.Remove(0, dir2.Length + 1);
- }
- #endregion
-
- #region RemoveFirstDirectory (Static)
- /// <summary>
- /// Remove first directory of path if one exists. "maps\\mymaps\\hehe.map"
- /// becomes "mymaps\\hehe.map". Also used to cut first folder off,
- /// especially useful for paths. e.g. "maps\\test" becomes "test".
- /// </summary>
- /// <param name="path">Path</param>
- /// <returns>Reduced directory path</returns>
- public static string RemoveFirstDirectory(string path)
- {
- int index = path.IndexOf(FolderSeparator);
- if (index >= 0 &&
- index < path.Length)
- {
- // Return rest of path
- return path.Substring(index + 1);
- }
- index = path.IndexOf(AlternateFolderSeparator);
- if (index >= 0 &&
- index < path.Length)
- {
- // Return rest of path
- return path.Substring(index + 1);
- }
-
- // No first directory found, just return original path
- return path;
- }
- #endregion
-
- #region CutExtension (Static)
- /// <summary>
- /// Cut of extension, e.g. "hi.txt" becomes "hi"
- /// </summary>
- /// <param name="file">File</param>
- /// <returns>Filename without extension</returns>
- public static string CutExtension(string file)
- {
- if (String.IsNullOrEmpty(file))
- {
- return "";
- }
-
- // Don't cut below any directory structure (which may contain points)
- int maxLastSeparatorIndex = file.LastIndexOf(FolderSeparator);
- int alternateSeparatorIndex = file.LastIndexOf(AlternateFolderSeparator);
- if (alternateSeparatorIndex > maxLastSeparatorIndex)
- {
- maxLastSeparatorIndex = alternateSeparatorIndex;
- }
-
- // Get extension position.
- int lastPointPos = file.LastIndexOf(FileExtensionSeperator);
-
- // Only cut off stuff if we have valid indices, else return the input
- return
- lastPointPos > 0 &&
- lastPointPos > maxLastSeparatorIndex
- ? file.Remove(lastPointPos, file.Length - lastPointPos)
- : file;
- }
- #endregion
-
- #region GetExtension (Static)
- /// <summary>
- /// Get extension (whatever is behind that '.'), e.g. "test.bmp" will
- /// return "bmp". Only a filename will return "", e.g. "Test" returns ""
- /// </summary>
- /// <param name="file">Filename to get the extension from</param>
- /// <returns>Returns the extension string of the file.</returns>
- public static string GetExtension(string file)
- {
- if (String.IsNullOrEmpty(file))
- {
- return "";
- }
-
- int lastPointPos = file.LastIndexOf(FileExtensionSeperator);
-
- // Don't cut below any directory structure (which may contain points)
- int lastDirectoryPos = file.LastIndexOf(FolderSeparator);
- int alternateSeparatorIndex = file.LastIndexOf(AlternateFolderSeparator);
- if (alternateSeparatorIndex > lastDirectoryPos)
- {
- lastDirectoryPos = alternateSeparatorIndex;
- }
- if (lastPointPos > 0 &&
- lastPointPos > lastDirectoryPos &&
- lastPointPos < file.Length)
- {
- return file.Remove(0, lastPointPos + 1);
- }
-
- // No extension found, return empty string (filename without extension).
- return "";
- }
- #endregion
-
- #region TryToUseRelativePath (Static)
- /// <summary>
- /// Helper function for saving, we check if path starts with same as
- /// our application. If so, better use relative path, then we can use
- /// them even if application is moved or copied over network!
- /// </summary>
- /// <param name="fullPath">Full path to check against</param>
- /// <returns>Relative path starting at fullPath if possible</returns>
- public static string TryToUseRelativePath(string fullPath)
- {
- return TryToUseRelativePath(Environment.CurrentDirectory, fullPath);
- }
-
- /// <summary>
- /// Helper function for saving, we check if path starts with same as
- /// our application. If so, better use relative path, then we can use
- /// them even if application is moved or copied over network!
- /// </summary>
- /// <param name="pathToCheckFrom">Path to check from (e.g. from our
- /// application) and the origin for the relative path that is returned
- /// </param>
- /// <param name="fullPath">Full path to check against</param>
- /// <returns>Relative path starting at fullPath if possible</returns>
- public static string TryToUseRelativePath(string pathToCheckFrom,
- string fullPath)
- {
- if (fullPath != null &&
- fullPath.StartsWith(pathToCheckFrom))
- {
- int len = pathToCheckFrom.Length;
- // +1 to remove '/' too
- if (!pathToCheckFrom.EndsWith(@"\"))
- {
- len++;
- }
- return fullPath.Remove(0, len);
- } // if (fullPath)
- // Startup path not found, so either its relative already or
- // we can't use relative path that easy!
- return fullPath;
- }
- #endregion
-
- #region TryToUseAbsolutePath (Static)
- /// <summary>
- /// Helper method to use an absolute path. If the path is already absolute
- /// nothing will change, but if the path is relative the basePath is added.
- /// </summary>
- /// <param name="basePath">Path to check from</param>
- /// <param name="possibleFullPath">Full or relative path to check.</param>
- /// <returns>Absolute path to the file</returns>
- public static string TryToUseAbsolutePath(string basePath,
- string possibleFullPath)
- {
- // If the path is relative, combine it
- if (Path.IsPathRooted(possibleFullPath) == false)
- {
- return Path.Combine(basePath, possibleFullPath);
- }
- // Otherwise just return the original absolute path.
- return possibleFullPath;
- }
- #endregion
-
- #region IsDirectSubFolder (Static)
- /// <summary>
- /// Check if a folder is a direct sub folder of a main folder.
- /// True is only returned if this is a direct sub folder, not if
- /// it is some sub folder few levels below.
- /// </summary>
- /// <param name="mainFolder">MainFolder</param>
- /// <param name="subFolder">SubFolder</param>
- /// <returns>
- /// True if the subFolder is a direct sub folder of mainFolder.
- /// </returns>
- public static bool IsDirectSubFolder(string subFolder, string mainFolder)
- {
- // First check if subFolder is really a sub folder of mainFolder
- if (subFolder != null &&
- subFolder.StartsWith(mainFolder))
- {
- // Same order?
- if (subFolder.Length <
- mainFolder.Length + 1)
- {
- // Then it ain't a sub folder!
- return false;
- }
-
- // Ok, now check if this is direct sub folder or some sub folder
- // of mainFolder sub folder
- string folder = subFolder.Remove(0, mainFolder.Length + 1);
-
- // Check if this is really a direct sub folder
- if (folder.IndexOf(FolderSeparator) >= 0 ||
- folder.IndexOf(AlternateFolderSeparator) >= 0)
- {
- // No, this is a sub folder of mainFolder sub folder
- return false;
- }
-
- // Ok, this is a direct sub folder of mainFolder!
- return true;
- }
-
- // Not even any sub folder!
- return false;
- }
- #endregion
-
- #region CleanupWindowsPath (Static)
- /// <summary>
- /// Cleans up the given path, so that it's in an homogenous unix/web path.
- /// E.g. ".\MyPath\With/Subfolder" => "My/Path/With/Subfolder"
- /// Also used by our whole content pipeline to make searching easier.
- /// </summary>
- /// <param name="anyPath">Any path in windows or unix format</param>
- /// <returns>Path in windows format</returns>
- public static string CleanupWindowsPath(string anyPath)
- {
- // Validation check
- if (String.IsNullOrEmpty(anyPath))
- {
- return "";
- }
-
- // First we make sure the we have an homogenous windows path format
- if (anyPath.StartsWith("file:///"))
- {
- anyPath = anyPath.Substring("file:///".Length);
- }
- anyPath = anyPath.Replace("/", @"\");
-
- // If the path begins with "./" like e.g. ".\MyFolder\With\Subfolder",
- // then we cut this away, because we don't need that and it makes the
- // path more tight, might happen on windows
- // else return the cleaned path
- return
- anyPath.StartsWith(@".\")
- ? anyPath.Substring(2)
- : anyPath;
- }
- #endregion
-
- #region CleanupLinuxPath (Static)
- /// <summary>
- /// Cleans up the given path, so that it's in an homogenous unix/web path.
- /// E.g. ".\MyPath\With/Subfolder" => "My/Path/With/Subfolder"
- /// Also used by our whole content pipeline to make searching easier.
- /// </summary>
- /// <param name="anyPath">Any path in windows or unix format</param>
- /// <returns>Path in unix format</returns>
- public static string CleanupLinuxPath(string anyPath)
- {
- // Validation check
- if (String.IsNullOrEmpty(anyPath))
- {
- return "";
- }
-
- // First we make sure the we have an homogenous unix/web path format
- if (anyPath.StartsWith(@"\\"))
- {
- anyPath = "file://///" + anyPath.Substring(2);
- }
- anyPath = anyPath.Replace(@"\", "/");
-
- // If the path begins with "./" like e.g. "./MyFolder/With/Subfolder",
- // then we cut this away, because we don't need that and it makes the
- // else return the cleaned path
- return
- anyPath.StartsWith("./")
- ? anyPath.Substring(2)
- : anyPath;
- }
- #endregion
-
- #region GetRelativeFilePath (Static)
- /// <summary>
- /// Get relative file path of a given absolute file path based on basePath.
- /// If the basePath and the absoluteFilePath share something in common this
- /// method will try to remove the common part (e.g. "c:\code\Delta\bla"
- /// in the basePath "c:\code" will be reduced to "Delta\bla" or in the
- /// basePath "c:\code\DeltaEngine" will be "..\Delta\bla").
- /// </summary>
- /// <param name="absoluteFilePath">
- /// Absolute file path, might already be relative or even be on a different
- /// drive, then it will just be returned.
- /// </param>
- /// <param name="basePath">Base path</param>
- /// <returns>Relative path if possible</returns>
- public static string GetRelativeFilePath(string absoluteFilePath,
- string basePath)
- {
- #region Validation checks
- if (String.IsNullOrEmpty(absoluteFilePath))
- {
- return "";
- }
-
- if (String.IsNullOrEmpty(basePath))
- {
- return absoluteFilePath;
- }
- #endregion
-
- // We can only easily "build" the relative content path, if the given
- // file path starts with the given content base path.
- string relativeContentFilePath =
- absoluteFilePath.StartsWith(basePath)
- ? absoluteFilePath.Substring(basePath.Length)
- : absoluteFilePath;
-
- // If that did not worked, we can also check folder by folder if
- // the two path strings are equal up to a certain point.
- if (relativeContentFilePath == absoluteFilePath)
- {
- string[] basePathParts = basePath.Split(new char[]
- {
- '\\', '/'
- });
- string[] absoluteFilePathParts = absoluteFilePath.Split(new char[]
- {
- '\\', '/'
- });
-
- // Continue checking as long as the parts are equal. Note: This will
- // abort in the first loop when the drives are different or one part
- // is not absolute.
- int num = 0;
- for (; num < basePathParts.Length &&
- num < absoluteFilePathParts.Length; num++)
- {
- if (basePathParts[num] != absoluteFilePathParts[num])
- {
- break;
- }
- // Remove this part as it is equal (plus the separator)!
- relativeContentFilePath =
- relativeContentFilePath.Substring(basePathParts[num].Length + 1);
- }
-
- // Nothing matched? Then just return the absolute path again!
- if (num == 0)
- {
- return absoluteFilePath;
- }
-
- // Otherwise add the number of parts we are away from the base path.
- for (; num < basePathParts.Length; num++)
- {
- relativeContentFilePath = "..\\" + relativeContentFilePath;
- }
- }
-
- // If the path starts with a "folder backslash" like "\Folder" then cut
- // it off
- return
- relativeContentFilePath.StartsWith(@"\")
- ? relativeContentFilePath.Substring(1)
- : relativeContentFilePath;
- }
- #endregion
-
- #region TrimPath (Static)
- /// <summary>
- /// Removes all unnecessary relative path fragments and will return a
- /// trimmed path. Useful in combination with GetRelativeFilePath.
- /// <para />
- /// Example: "C:\Path\SubPath1\..\SubPath2\file.exe" will become to
- /// "C:\Path\SubPath2\file.exe". Also works with relative paths and unix
- /// paths (e.g. '/SomePath/../SomeFile.xml' will become 'SomeFile.xml').
- /// </summary>
- /// <param name="filePath">Path to be shortened if possible.</param>
- public static string TrimPath(string filePath)
- {
- // If the filePath has no ..\ in it, ignore it and just return it!
- if (String.IsNullOrEmpty(filePath) ||
- (filePath.Contains("..\\") == false &&
- filePath.Contains("../") == false))
- {
- return filePath;
- }
-
- // Network paths need the \\ added again after we are done processing
- bool isNetworkPath = filePath.StartsWith(@"\\");
-
- // Go through the path and collect all parts.
- string[] pathParts = filePath.Split(new char[]
- {
- '\\', '/'
- });
-
- // Now collect all .. parts and cut off previous directories!
- List<string> result = new List<string>();
- foreach (string part in pathParts)
- {
- // Also ignore the . and empty parts (e.g. \ or at the beginning).
- if (String.IsNullOrEmpty(part))
- {
- continue;
- }
- // For .. entries remove the last real path entry we added
- if (result.Count > 0 &&
- result[result.Count - 1] != ".." &&
- part == "..")
- {
- result.RemoveAt(result.Count - 1);
- }
- else
- {
- // Otherwise add the path part normally (might even be .. if there
- // is nothing more we can cut off).
- result.Add(part);
- }
- }
-
- // Return the result in windows path mode if the incoming path contained
- // backslashes, otherwise return it in linux format.
- return
- (isNetworkPath
- ? @"\\"
- : "") +
- ArrayHelper.Write(result,
- filePath.Contains("\\")
- ? "\\"
- : "/");
- }
- #endregion
-
- #region GetFileSize (Static)
- /// <summary>
- /// Get file size. Returns 0 if file does not exists instead of throwing
- /// an exception. Else it returns file size as a long number.
- /// </summary>
- /// <param name="filename">Filename</param>
- /// <returns>Long</returns>
- public static long GetFileSize(string filename)
- {
- if (Exists(filename))
- {
- using (FileStream fileStream = Open(filename,
- FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
- {
- return fileStream.Length;
- }
- }
-
- return 0;
- }
- #endregion
-
- #region GetLines (Static)
- /// <summary>
- /// Returns the text lines we got in a file.
- /// </summary>
- /// <param name="filename">Filename</param>
- /// <returns>
- /// All text lines from the file or null if no file was found.
- /// </returns>
- public static string[] GetLines(string filename)
- {
- return GetLines(filename, Encoding.UTF8);
- }
-
- /// <summary>
- /// Returns the text lines we got in a file. Will not crash! If file does
- /// not exist, we will just return null, same for an unreadable file.
- /// </summary>
- /// <param name="filename">Filename</param>
- /// <param name="textEncoding">Text encoding</param>
- /// <returns>
- /// All text lines from the file or null if no file was found.
- /// </returns>
- public static string[] GetLines(string filename, Encoding textEncoding)
- {
- // If filename is invalid or file does not exist, just abort!
- if (String.IsNullOrEmpty(filename) ||
- Exists(filename) == false)
- {
- return null;
- }
-
- try
- {
- List<string> lines = new List<string>();
-
- using (FileStream stream = Open(filename, FileMode.Open,
- FileAccess.Read, FileShare.ReadWrite))
- {
- StreamReader reader = new StreamReader(stream, textEncoding);
- do
- {
- lines.Add(reader.ReadLine());
- } while (reader.Peek() >
- -1);
- }
-
- return lines.ToArray();
- }
- catch (Exception ex)
- {
- Log.Warning(String.Format("Failed to GetLines of file={0}: {1}",
- filename, ex));
- // Failed to read, just return null!
- return null;
- }
- }
- #endregion
-
- #region GetText (Static)
- /// <summary>
- /// Gets all the text of a file (e.g. if you just want to the text of a
- /// text file). Will not crash! If file does not exist, we will just return
- /// an empty string. If file is somehow unreadable, we will log an warning
- /// (because this shouldn't happen, we should always open files in a way
- /// that allows reading) and return an empty string too.
- /// </summary>
- /// <param name="filename">Filename for the file to load</param>
- /// <returns>String with all the text from the file</returns>
- public static string GetText(string filename)
- {
- string error;
- string text = GetText(filename, out error);
-
- // Log out the error if there was one
- if (String.IsNullOrEmpty(error) == false)
- {
- Log.Warning(error);
- }
-
- return text;
- }
-
- /// <summary>
- /// Gets all the text of a file (e.g. if you just want to the text of a
- /// text file). Will not crash! If file does not exist, we will just return
- /// an empty string. If file is somehow unreadable, we will log an warning
- /// (because this shouldn't happen, we should always open files in a way
- /// that allows reading) and return an empty string too.
- /// </summary>
- /// <param name="filename">Filename for the file to load</param>
- /// <returns>String with all the text from the file</returns>
- public static string GetText(string filename, out string error)
- {
- // If filename is invalid or file does not exist, just abort!
- if (String.IsNullOrEmpty(filename) ||
- Exists(filename) == false)
- {
- error = "GetText failed to get non existing file: '" + filename + "'";
- return "";
- }
-
- try
- {
- using (StreamReader reader =
- new StreamReader(OpenFileAsStream(filename), Encoding.UTF8))
- {
- error = "";
- return reader.ReadToEnd();
- }
- }
- catch (Exception ex)
- {
- error = "Failed to GetText of file='" + filename + "': " + ex.Message;
- // Failed to read, just return an empty string!
- return "";
- }
- }
- #endregion
-
- #region PathCombine (Static)
- /// <summary>
- /// Path combining methods to ensure each path is compatible. This is
- /// more powerful than Path.Combine and handles all platform cases and
- /// should be used if you need your code to run on all platforms.
- /// </summary>
- /// <param name="path1">Path 1</param>
- /// <param name="path2">Path 2</param>
- /// <returns>Combined path</returns>
- public static string PathCombine(string path1, string path2)
- {
- // If either one of the two paths are empty/null, return nothing.
- if ((path1 == null) ||
- (path2 == null))
- {
- return "";
- }
-
- // If either path contains the wrong "slash", revert to the correct one
- if ((path1.Contains(@"\")) ||
- (path2.Contains(@"\")))
- {
- path1 = path1.Replace(@"\", "/");
- path2 = path2.Replace(@"\", "/");
- }
-
- // If either path starts with either a "/" or a ".", remove them
- if (path1.StartsWith(@"/") ||
- path1.StartsWith("."))
- {
- path1 = path1.Remove(0, 1);
- }
- if (path2.StartsWith(@"/") ||
- path2.StartsWith("."))
- {
- path2 = path2.Remove(0, 1);
- }
-
- // If either path ends with either a "/" or a ".", remove them.
- if (path1.EndsWith(@"/") ||
- path1.EndsWith("."))
- {
- path1 = path1.Substring(0, path1.Length - 1);
- }
- if (path2.EndsWith(@"/") ||
- path2.EndsWith("."))
- {
- path2 = path2.Substring(0, path2.Length - 1);
- }
-
- // Return our combined path with the correct seperator
- return (path1 + "/" + path2);
- }
- #endregion
-
- #region GetLinesCount (Static)
- /// <summary>
- /// Returns the number of text lines we got in a text file.
- /// </summary>
- /// <param name="filename">Filename</param>
- /// <returns>Number of lines in the given text file.</returns>
- public static int GetLinesCount(string filename)
- {
- int lines = 0;
-
- try
- {
- using (StreamReader reader = new StreamReader(Open(
- filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
- Encoding.UTF8))
- {
- do
- {
- reader.ReadLine();
- lines++;
- } while (reader.Peek() >
- -1);
- }
- }
- catch
- {
- } // catch
-
- // Failed to read, just return 0 lines!
- return lines;
- }
- #endregion
-
- #region CreateTextFile (Static)
- /// <summary>
- /// Create text file or overwrite an existing file. Will store all the
- /// given text into it with the optional Encoding (default is UTF8).
- /// </summary>
- /// <param name="filename">Filename to save text to</param>
- /// <param name="textForFile">Text to store in the new file</param>
- /// <param name="encoding">
- /// Encoding to use for file, by default UTF8
- /// </param>
- /// <exception cref="IOException">
- /// Will be thrown if file already exists and could not be overwritten or
- /// if creating the file failed for other reasons (e.g. unable to write).
- /// </exception>
- public static void CreateTextFile(string filename, string textForFile,
- Encoding encoding = null)
- {
- using (TextWriter textWriter = new StreamWriter(Open(
- filename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite),
- encoding ?? Encoding.UTF8))
- {
- /*this is nice, but slow, it should not be required anymore!
- string[] textLines = StringHelper.SplitMultiLineText(textForFile);
- foreach (string line in textLines)
- {
- textWriter.WriteLine(line);
- }
- */
- textWriter.Write(textForFile);
- textWriter.Close();
- }
- }
-
- /// <summary>
- /// Create text file. Will create a new file and store all the given text
- /// lines into it with the optional Encoding (default is UTF8).
- /// </summary>
- /// <param name="filename">Filename to save text to</param>
- /// <param name="textLines">Text lines to store</param>
- /// <param name="encoding">
- /// Encoding to use for file, by default UTF8
- /// </param>
- /// <exception cref="IOException">
- /// Will be thrown if file already exists and could not be overwritten or
- /// if creating the file failed for other reasons (e.g. unable to write).
- /// </exception>
- public static void CreateTextFile(string filename, string[] textLines,
- Encoding encoding = null)
- {
- using (StreamWriter textWriter = new StreamWriter(Open(
- filename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite),
- encoding ?? Encoding.UTF8))
- {
- foreach (string line in textLines)
- {
- textWriter.WriteLine(line);
- }
- textWriter.Close();
- }
- }
- #endregion
-
- #region CreateBinaryFile (Static)
- /// <summary>
- /// Helper method to create a binary file with the given byte data array.
- /// /// <para />
- /// Note: If the file exists, it will just be overwritten. The path to
- /// the filename must exist however, else this will throw an exception.
- /// Also if writing this file is not possible an access denied IOException
- /// will be thrown.
- /// </summary>
- /// <param name="filename">Filename to save binary data to</param>
- /// <param name="binaryData">The data to save</param>
- public static void CreateBinaryFile(string filename, byte[] binaryData)
- {
- using (FileStream fileStream = Open(
- filename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
- {
- fileStream.Write(binaryData, 0, binaryData.Length);
- }
- }
-
- /// <summary>
- /// Helper method to create a binary file with the given memory stream.
- /// <para />
- /// Note: If the file exists, it will just be overwritten. The path to
- /// the filename must exist however, else this will throw an exception.
- /// Also if writing this file is not possible an access denied IOException
- /// will be thrown.
- /// </summary>
- /// <param name="filename">Filename to save binary data to</param>
- /// <param name="data">The data to save</param>
- public static void CreateBinaryFile(string filename, MemoryStream data)
- {
- using (FileStream fileStream = Open(
- filename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
- {
- fileStream.Write(data.ToArray(), 0, (int)data.Length);
- }
- }
- #endregion
-
- #region GetBytes (Static)
- /// <summary>
- /// Helper method to grab all bytes of a binary file and then close it
- /// again. Will not crash! If file does not exist, we will just return
- /// an empty array. If file is somehow unreadable, we will log an warning
- /// (because this shouldn't happen, we should always open files in a way
- /// that allows reading) and return an empty array too.
- /// </summary>
- /// <param name="filename">File to load</param>
- /// <returns>Array of bytes from the file</returns>
- public static byte[] GetBytes(string filename)
- {
- // If filename is invalid or file does not exist, just abort!
- if (String.IsNullOrEmpty(filename) ||
- Exists(filename) == false)
- {
- Log.Warning("GetBytes failed to get non existing file: " + filename);
- return new byte[0];
- }
-
- try
- {
- using (Stream fileStream = OpenFileAsStream(filename))
- {
- byte[] bytes = new byte[fileStream.Length];
- fileStream.Read(bytes, 0, bytes.Length);
- return bytes;
- }
- }
- catch (Exception ex)
- {
- Log.Warning("Failed to GetBytes of file '" + filename + "': " + ex);
- // Failed to read, just return an empty array!
- return new byte[0];
- }
- }
- #endregion
-
- #region SafeDelete (Static)
- /// <summary>
- /// Safely deletes a file.
- /// </summary>
- /// <param name="filePath">File to delete</param>
- /// <returns>True if file was deleted or did not exist anymore</returns>
- public static bool SafeDelete(string filePath)
- {
- if (String.IsNullOrEmpty(filePath) ||
- Exists(filePath) == false)
- {
- // File did not exist, so no deletion was required
- // (return true to mark that this file is gone now because it
- // was already gone).
- return true;
- }
-
- try
- {
- DeleteCallback(filePath);
- return true;
- }
- catch (IOException ioEx)
- {
- if (ioEx.Message.Contains("The process cannot access the file"))
- {
- // Try to wait a short while, maybe it is free soon
- Thread.Sleep(50);
-
- // And try to delete it again
- try
- {
- DeleteCallback(filePath);
- // Everything is fine now, quit without logging an error
- return true;
- }
- catch (Exception ex)
- {
- Log.Warning("Couldn't safely delete file even with waiting '" +
- filePath + "' because of: " + ex.Message);
- }
- }
- }
- catch (Exception ex)
- {
- Log.Warning("Couldn't safely delete file '" + filePath +
- "' because of: " + ex.Message);
- }
-
- // File was not deleted
- return false;
- }
- #endregion
-
- #region SafeDeleteFiles (Static)
- /// <summary>
- /// Safely delete the files matching the search pattern in the base
- /// directory and if wanted recursive in sub directories as well.
- /// </summary>
- /// <param name="basePath">Base Path</param>
- /// <param name="recursive">Recursive</param>
- /// <param name="searchPattern">Search Pattern</param>
- /// <returns>True if deleting the files worked (no matter if it actually
- /// deleted something or not), false otherwise (warnings for failed file
- /// deletes will be outputted via the SafeDelete method)</returns>
- public static bool SafeDeleteFiles(string basePath, string searchPattern,
- bool recursive)
- {
- if (DirectoryHelper.Exists(basePath) == false)
- {
- return true;
- }
-
- string[] files = recursive
- ? DirectoryHelper.GetFilesRecursive(basePath, searchPattern)
- : DirectoryHelper.GetFiles(basePath, searchPattern);
-
- foreach (string file in files)
- {
- SafeDelete(file);
- }
-
- return true;
- }
- #endregion
-
- #region CheckIfFileIsNewer (Static)
- /// <summary>
- /// Check if file is newer. Not the fastest method ever as it needs
- /// to load both files and compare their last write times.
- /// </summary>
- /// <param name="fileToCheckIfNewer">File to check if newer</param>
- /// <param name="originalFile">Original File</param>
- /// <returns>True if the file is newer than the original.</returns>
- public static bool CheckIfFileIsNewer(string fileToCheckIfNewer,
- string originalFile)
- {
- // Got no file to check, then we can't compare and should not copy.
- if (Exists(fileToCheckIfNewer) == false)
- {
- return false;
- }
- // For no original file, the new file is obviously newer than nothing!
- if (Exists(originalFile) == false)
- {
- return true;
- }
-
- return FileIsNewerCallback(fileToCheckIfNewer, originalFile);
- }
- #endregion
-
- #region ExistsPath (Static)
- /// <summary>
- /// Exist path, will check if a full file path or just a directory path
- /// exists. This is different from the DirectoryHelper.Exists method,
- /// which just checks if a directory path exists (this one checks for
- /// files too if a valid extension for a file path was given).
- /// </summary>
- /// <param name="path">A path from a file or a directory</param>
- /// <returns>True if the path was found as a file or directory</returns>
- public static bool ExistsPath(string path)
- {
- if (Path.HasExtension(path))
- {
- return Exists(path);
- }
- return DirectoryHelper.Exists(path);
- }
- #endregion
-
- #region CutPath (Static)
- /// <summary>
- /// Cuts off the given "cut path" completely from the given filePath.
- /// </summary>
- /// <param name="filePath">
- /// Any absolute or relative file or folder path.
- /// </param>
- /// <param name="pathToCutOff">
- /// The path (or folder) which should be cut off completely.
- /// </param>
- /// <returns>FilePath without pathToCutOff</returns>
- public static string CutPath(string filePath, string pathToCutOff)
- {
- #region Validation
- if (String.IsNullOrEmpty(filePath))
- {
- return "";
- }
- #endregion
-
- // IF we have a valid path which should be cut off
- if (String.IsNullOrEmpty(pathToCutOff) == false)
- {
- // then try to find that (sub)path or folder in the given file path
- int cutOffIndex = filePath.IndexOf(pathToCutOff);
- // If it really exists in the file path
- if (cutOffIndex != MathHelper.InvalidIndex)
- {
- cutOffIndex += pathToCutOff.Length;
- // then just check if the "cut off path" ends already with an "\"
- if (pathToCutOff[pathToCutOff.Length - 1] != '\\')
- {
- // because if not then we have to increase the index by that
- cutOffIndex++;
- }
-
- // before we can finally cut off the subpath, but only if we
- // don't will cut off a filename
- return (filePath[cutOffIndex - 1] != '.')
- ? filePath.Substring(cutOffIndex)
- : filePath;
- }
- }
-
- return filePath;
- }
- #endregion
-
- #region CompareFiles (Static)
- /// <summary>
- /// Compare two files with the help of the MD5-checksum
- /// </summary>
- /// <param name="fileName1">Filename 1</param>
- /// <param name="fileName2">Filename 2</param>
- /// <returns>True if both file contents are the same</returns>
- public static bool CompareFiles(string fileName1, string fileName2)
- {
- return CompareFilesCallback(fileName1, fileName2);
- }
- #endregion
-
- #region AppendTextToFile (Static)
- /// <summary>
- /// Append text to file, does the same as File.AppendAllText, but it
- /// won't crash and can handle already open files :)
- /// </summary>
- /// <param name="filename">Filename</param>
- /// <param name="text">Text</param>
- public static void AppendTextToFile(string filename, string text)
- {
- try
- {
- using (StreamWriter writer = new StreamWriter(Open(
- filename, FileMode.OpenOrCreate, FileAccess.Write,
- FileShare.ReadWrite), Encoding.UTF8))
- {
- // Go to end of file
- writer.BaseStream.Seek(0, SeekOrigin.End);
- // And write our new text
- writer.Write(text);
- }
- }
- catch (Exception ex)
- {
- Log.Warning("Failed to AppendTextToFile of file " + filename + ": " +
- ex);
- }
- }
-
- /// <summary>
- /// Append text to file, does the same as File.AppendAllText, but it
- /// won't crash and can handle already open files :) This overload checks
- /// if the existing data is more than maxTextFileSizeBeforeAppending
- /// already, then remove data from the beginning before appending.
- /// </summary>
- /// <param name="filename">Filename</param>
- /// <param name="maxTextFileSizeBeforeAppending">Max Text File Size before
- /// appending</param>
- /// <param name="text">Text</param>
- public static void AppendTextToFile(string filename, string text,
- int maxTextFileSizeBeforeAppending)
- {
- //this suxx: File.AppendAllText(filename, text, UTF8);
- try
- {
- FileStream stream = Open(filename, FileMode.OpenOrCreate,
- FileAccess.ReadWrite, FileShare.ReadWrite);
- // File is too big, remove data at the beginning (happens rarely).
- if (stream.Length > maxTextFileSizeBeforeAppending)
- {
- stream.Close();
- // Just read it all into lines and build it up again from bottom up!
- string[] oldLines = GetLines(filename, Encoding.UTF8);
- // Always grab the first 100 lines and keep them!
- List<string> newLines = new List<string>();
- int newSize = 0;
- for (int line = 0; line < 100 && line < oldLines.Length; line++)
- {
- string newLine = oldLines[line];
- newLines.Add(newLine);
- newSize += newLine.Length;
- }
- newLines.Add(
- "\n" +
- "****************************\n" +
- "This text file was exceeding " +
- StringHelper.WriteKbMbGbNumber(
- maxTextFileSizeBeforeAppending) + ", old data will be " +
- "removed from the beginning.\nSkipped the first 100 lines and " +
- "the new text is appended at the end as usually.\n" +
- "****************************\n");
- // Next fill up the lines until we reach the max size.
- int insertLineNum = newLines.Count;
- for (int line = oldLines.Length - 1; line >= 100; line--)
- {
- string newLine = oldLines[line];
- // Always insert at 101 (100 presevered lines + our message),
- // this way we go bottom up
- newLines.Insert(insertLineNum, newLine);
- newSize += newLine.Length;
- // Abort if we reach the max size / 2 (this way we don't have to
- // do this costly operation too often).
- if (newSize > maxTextFileSizeBeforeAppending / 2)
- {
- break;
- }
- }
-
- // And finally create the new file and get another stream handle
- CreateTextFile(filename, newLines.ToArray(),
- Encoding.UTF8);
- stream = Open(filename, FileMode.OpenOrCreate,
- FileAccess.ReadWrite, FileShare.ReadWrite);
- }
-
- using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8))
- {
- // Go to end of file
- writer.BaseStream.Seek(0, SeekOrigin.End);
- // And write our new text
- writer.Write(text);
- }
- }
- catch (Exception ex)
- {
- Log.Warning("Failed to AppendTextToFile of file " + filename + ": " +
- ex);
- }
- }
- #endregion
-
- #region CheckIfFileExistsAndIsNotWrittenTo (Static)
- /// <summary>
- /// Check if file exists and is not written to recently. This is mostly
- /// used for update checks to see if we can assume this file is complete
- /// and we can now open it, etc. (e.g. used in the RestartTool).
- /// </summary>
- /// <param name="filename">File to check</param>
- /// <returns>
- /// True if the file exists and was not written to recently.
- /// </returns>
- public static bool CheckIfFileExistsAndIsNotWrittenTo(string filename)
- {
- // First of all, make sure this file exists at all
- if (Exists(filename) == false)
- {
- return false;
- }
-
- return FileNotWrittenToCallback(filename);
- }
- #endregion
-
- #region OpenFileAsStream (Static)
- /// <summary>
- /// Open file as stream. Make sure the filePath is in the correct format
- /// for this platform!
- /// </summary>
- /// <param name="filePath">File path</param>
- /// <returns>Stream of the opened file for reading</returns>
- public static Stream OpenFileAsStream(string filePath)
- {
-
- return Open(filePath, FileMode.Open, FileAccess.Read,
- FileShare.ReadWrite);
- }
- #endregion
-
- #region Load (Static)
- /// <summary>
- /// Helper method to load data into any ISaveLoadBinary object just with
- /// a given filePath. This helps reducing writing the same code over and
- /// over again in each content class or ISaveLoadBinary data class.
- /// </summary>
- /// <typeparam name="T">Type to load into, must be derived from
- /// ISaveLoadBinary</typeparam>
- /// <param name="filePath">File path to load the binary data from</param>
- /// <param name="objectToLoad">Object to load all binary data into</param>
- public static void Load<T>(string filePath, T objectToLoad)
- where T : ISaveLoadBinary
- {
- using (Stream file = Open(filePath, FileMode.Open,
- FileAccess.Read, FileShare.Read))
- {
- objectToLoad.Load(new BinaryReader(file));
- }
- }
- #endregion
-
- #region Save (Static)
- /// <summary>
- /// Helper method to save data from any ISaveLoadBinary object into a
- /// given filePath. This helps reducing writing the same code over and
- /// over again in each content class or ISaveLoadBinary data class.
- /// </summary>
- /// <typeparam name="T">Type to save into, must be derived from
- /// ISaveLoadBinary</typeparam>
- /// <param name="filePath">File path to save the binary data to</param>
- /// <param name="objectToSave">Object we want to save</param>
- public static void Save<T>(string filePath, T objectToSave)
- where T : ISaveLoadBinary
- {
- using (Stream file = Open(filePath, FileMode.Create,
- FileAccess.Write, FileShare.Read))
- {
- objectToSave.Save(new BinaryWriter(file));
- }
- }
- #endregion
-
- #region GetFileDate (Static)
- /// <summary>
- /// Get file date via File.GetLastWriteTime if supported by this platform!
- /// </summary>
- /// <param name="filePath">File path to check</param>
- /// <returns>Last time this file was written to.</returns>
- public static DateTime GetFileDate(string filePath)
- {
- return File.GetLastWriteTime(filePath);
- }
- #endregion
-
- #region Internal
-
- #region ExistsCallback (Internal)
- internal static ExistsDelegate ExistsCallback;
- #endregion
-
- #region CopyCallback (Internal)
- internal static CopyDelegate CopyCallback;
- #endregion
-
- #region MoveCallback (Internal)
- internal static MoveDelegate MoveCallback;
- #endregion
-
- #region OpenCallback (Internal)
- internal static OpenDelegate OpenCallback;
- #endregion
-
- #region DeleteCallback (Internal)
- internal static DeleteDelegate DeleteCallback;
- #endregion
-
- #region FileIsNewerCallback (Internal)
- internal static FileIsNewerDelegate FileIsNewerCallback;
- #endregion
-
- #region CompareFilesCallback (Internal)
- /// <summary>
- /// Callback for comparing files. Some platforms won't support this as
- /// efficiently as others. Normally MD5 Checksums are used.
- /// </summary>
- internal static CompareFilesDelegate CompareFilesCallback;
- #endregion
-
- #region FileNotWrittenToCallback (Internal)
- /// <summary>
- /// Delegate for CheckIfFileExistsAndIsNotWrittenTo.
- /// </summary>
- internal static FileNotWrittenToDelegate FileNotWrittenToCallback;
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Static FileHelper constructor to setup all the callbacks used here!
- /// </summary>
- static FileHelper()
- {
- ExistsCallback = File.Exists;
- MoveCallback = File.Move;
- OpenCallback = File.Open;
- DeleteCallback = File.Delete;
-
- CopyCallback = delegate(string sourceFile, string destinationFile)
- {
- File.Copy(sourceFile, destinationFile, true);
- };
-
- CompareFilesCallback = delegate(string fileName1, string fileName2)
- {
- MD5 md5 = new MD5CryptoServiceProvider();
-
- byte[] md5Hash;
- // Compute checksum 1
- using (FileStream fileCheck = File.OpenRead(fileName1))
- {
- md5Hash = md5.ComputeHash(fileCheck);
- }
-
- byte[] md5Hash2;
- // Now compute checksum 2
- using (FileStream fileCheck2 = File.OpenRead(fileName2))
- {
- md5Hash2 = md5.ComputeHash(fileCheck2);
- }
-
- string checksum1 =
- BitConverter.ToString(md5Hash).Replace("-", "").ToLower();
- string checksum2 =
- BitConverter.ToString(md5Hash2).Replace("-", "").ToLower();
-
- // Compare checksums
- return checksum1 == checksum2;
- };
-
- FileIsNewerCallback = delegate(string fileToCheckIfNewer,
- string originalFile)
- {
- return
- File.GetLastWriteTime(fileToCheckIfNewer) >
- File.GetLastWriteTime(originalFile);
- };
-
- FileNotWrittenToCallback = delegate(string filename)
- {
- DateTime now = DateTime.Now;
- // Next make sure this file is really written and completed
- // If there is still an FTP upload happening, we have to wait!
- if ((now - File.GetLastAccessTime(filename)).TotalSeconds < 1 ||
- (now - File.GetLastWriteTime(filename)).TotalSeconds < 1)
- {
- // Wait a little longer, <1s since last write action is pretty short
- return false;
- }
-
- // And finally check if we can write to the file. If another process
- // is writing to it, it wont allow us to write!
- try
- {
- using (FileStream testFile = Open(filename, FileMode.Open,
- FileAccess.ReadWrite, FileShare.ReadWrite))
- {
- // Nothing to do here, close the file again!
- }
- return true;
- }
- catch
- {
- // That failed, this file is still open in FTP or some program ..
- return false;
- }
- };
- }
- #endregion
-
- /// <summary>
- /// Test file helper
- /// </summary>
- [Category("LongRunning")]
- internal class FileHelperTests
- {
- #region TestCreateTextFileAndSafeDelete (Static)
- /// <summary>
- /// Test create text file and safe delete
- /// </summary>
- [Test]
- public static void TestCreateTextFileAndSafeDelete()
- {
- const string TestFilename = "TestTextFile.txt";
- const string SomeText = "Just some text for the test file ...";
-
- try
- {
- if (File.Exists(TestFilename))
- {
- SafeDelete(TestFilename);
- //throw new Exception("Unable to execute this unit test if " +
- // TestFilename + " does already exists!");
- }
-
- CreateTextFile(TestFilename, SomeText);
- // File contains now SomeText and "\n\r\0" characters.
- // Check if file size matches.
- Assert.Equal(SomeText.Length + 3,
- GetFileSize(TestFilename));
- }
- finally
- {
- // And now delete it safely again
- SafeDelete(TestFilename);
- // And make sure its gone which may take some millisecs on slow
- // hardware!
- Thread.Sleep(10);
- // There should be nothing left now
- Assert.False(File.Exists(TestFilename), "File exists");
- }
- }
- #endregion
-
- #region TestCompareFiles (Static)
- /// <summary>
- /// Test check if file is newer. Note: Too slow for a dynamic unit test.
- /// </summary>
- [Test]
- public static void TestCompareFiles()
- {
- CreateTextFile("test1.txt", "Hello world!");
- CreateTextFile("test2.txt", "Hello!");
-
- Assert.True(CompareFiles("test1.txt", "test1.txt"));
- Assert.False(CompareFiles("test1.txt", "test2.txt"));
-
- SafeDelete("test1.txt");
- SafeDelete("test2.txt");
- }
- #endregion
-
- #region TestPathCombine (Static)
- /// <summary>
- /// Test to see if our path combine methods correctly output the right path
- /// </summary>
- [Test]
- public static void TestPathCombine()
- {
- Assert.Equal(PathCombine("path1", "path2"), "path1/path2");
- Assert.Equal(PathCombine("path1", "/path2"), "path1/path2");
- Assert.Equal(PathCombine(@"path1\", "/path2"), "path1/path2");
- Assert.Equal(PathCombine(@"path1\", @"\path2"), "path1/path2");
- Assert.Equal(PathCombine(".path1/", "/path2."), "path1/path2");
- Assert.Equal(PathCombine(@"C:\path1", "/path2"), "C:/path1/path2");
- }
- #endregion
-
- #region OpenFileAsStream (Static)
- /// <summary>
- /// Test OpenFileAsStream
- /// </summary>
- [Test]
- public static void OpenFileAsStream()
- {
- const string filepath = @"C:\Windows\winhlp32.exe";
- Stream stream = FileHelper.OpenFileAsStream(filepath);
- Assert.Equal(GetFileSize(filepath), stream.Length);
- stream.Close();
- stream = null;
- }
- #endregion
-
- #region SafeDeleteFiles (Static)
- /// <summary>
- /// Test SafeDeleteFiles with some xml documentation files in the
- /// current directory.
- /// </summary>
- [Test]
- public static void SafeDeleteFiles()
- {
- FileHelper.SafeDeleteFiles(
- DirectoryHelper.GetCurrentDirectory(), "Delta.*.xml", true);
- }
- #endregion
-
- #region TrimPath (Static)
- /// <summary>
- /// Trim path
- /// </summary>
- [Test]
- public static void TrimPath()
- {
- // Existing file path
- Assert.Equal(FileHelper.TrimPath(
- @"C:\Windows\Temp\..\System32\notepad.exe"),
- @"C:\Windows\System32\notepad.exe");
-
- // Fictional absolute file path
- Assert.Equal(FileHelper.TrimPath(
- @"C:\Path\SubPath1\..\SubPath2\file.exe"),
- @"C:\Path\SubPath2\file.exe");
-
- // Fictional relative file path
- Assert.Equal(FileHelper.TrimPath(
- @"SubPath1\..\SubPath2\file.exe"),
- @"SubPath2\file.exe");
-
- // Test with network path
- Assert.Equal(FileHelper.TrimPath(
- @"\\blub\Bla\Honk\..\..\ContentSystem\Delta.ContentSystem.csproj"),
- @"\\blub\ContentSystem\Delta.ContentSystem.csproj");
-
- // Test unix path and also add a trailing slash into the mix!
- Assert.Equal(FileHelper.TrimPath(
- "/SomePath/../SomeFile.xml"),
- "SomeFile.xml");
-
- // Trying to trim a part away without providing a full path should not
- // kill the relative structure. We just want to trim after all.
- Assert.Equal(FileHelper.TrimPath(
- @"..\..\..\file.exe"),
- @"..\..\..\file.exe");
-
- // Going all the way down 4 levels.
- Assert.Equal(FileHelper.TrimPath(
- @"C:\SomePath\AnotherPath\ThirdPath\ForthPath\..\..\..\..\file.exe"),
- @"C:\file.exe");
-
- // Do the same with a relative path, we should not get an absolute path
- // back. Note: Very long path to test if converting the absolute path
- // to a relative one actually works.
- Assert.Equal(FileHelper.TrimPath(
- @"SomePath\AnotherPath\ThirdParth\ForthPath\..\..\..\..\file.exe"),
- @"file.exe");
- }
- #endregion
-
- #region CleanupWindowsPath
- [Test]
- public static void CleanupWindowsPath()
- {
- // Test windows paths
- Assert.Equal(FileHelper.CleanupWindowsPath(
- @"C:\Windows\System32\notepad.exe"),
- @"C:\Windows\System32\notepad.exe");
- Assert.Equal(FileHelper.CleanupWindowsPath(
- @"SomePath/SomeFile.xml"),
- @"SomePath\SomeFile.xml");
- }
- #endregion
-
- #region CleanupLinuxPath
- [Test]
- public static void CleanupLinuxPath()
- {
- // And do the same with Linux paths
- Assert.Equal(FileHelper.CleanupLinuxPath(
- @"C:\Windows\System32\notepad.exe"),
- @"C:/Windows/System32/notepad.exe");
- Assert.Equal(FileHelper.CleanupLinuxPath(
- @"SomePath/SomeFile.xml"),
- @"SomePath/SomeFile.xml");
- }
- #endregion
-
- #region FileExists (Static)
- /// <summary>
- /// Test the File Exists method.
- /// </summary>
- [Test]
- public static void FileExists()
- {
- Assert.True(Exists("C:\\Windows\\regedit.exe"));
- Assert.False(Exists("C:\\awdawdok.test"));
- }
- #endregion
-
- #region TestCutAndGetExtension
- /// <summary>
- /// Test cut and get extension
- /// </summary>
- [Test]
- public void TestCutAndGetExtension()
- {
- // Test cut extension, it should only remove extensions, nothing more.
- Assert.Equal("hi", CutExtension("hi.txt"));
- Assert.Equal("hi", CutExtension("hi"));
- Assert.Equal("SomeApplication.exe",
- CutExtension("SomeApplication.exe.config"));
- Assert.Equal(@"..\..\Yaya\Honk",
- CutExtension(@"..\..\Yaya\Honk.exe"));
- Assert.Equal(@"..\..\Yaya\Honk",
- CutExtension(@"..\..\Yaya\Honk"));
-
- // Test get extension, should return the extension the file has.
- // Should work the same way as CutExtension.
- Assert.Equal("txt", GetExtension("hi.txt"));
- Assert.Equal("", GetExtension("hi"));
- Assert.Equal("config",
- GetExtension("SomeApplication.exe.config"));
- Assert.Equal("exe", GetExtension(@"..\..\Yaya\Honk.exe"));
- }
- #endregion
-
- #region GetFilename
- /// <summary>
- /// Get filename
- /// </summary>
- [Test]
- public void GetFilename()
- {
- Assert.Equal("File.tst",
- FileHelper.GetFilename(@"Path\SubPath\File.tst"));
-
- Assert.Equal("File.tst",
- FileHelper.GetFilename(@"Path\SubPath/File.tst"));
-
- Assert.Equal("File",
- FileHelper.GetFilename(@"Path\SubPath\File.tst", true));
-
- string pathOnly;
- Assert.Equal("File.tst",
- FileHelper.GetFilename(@"Path\SubPath\File.tst", out pathOnly));
- Assert.Equal(@"Path\SubPath", pathOnly);
- }
- #endregion
-
- #region IsFilenameOnly
- /// <summary>
- /// Is filename only
- /// </summary>
- [Test]
- public void IsFilenameOnly()
- {
- Assert.True(FileHelper.IsFilenameOnly("File123.tst"));
- Assert.False(FileHelper.IsFilenameOnly("Folder"));
- }
- #endregion
-
- #region TestCheckIfFileIsNewer
- /// <summary>
- /// Test check if file is newer
- /// </summary>
- [Test]
- public void TestCheckIfFileIsNewer()
- {
- //does not work this way, a full rebuild changes the dates around :(
- //// Delta.Utilities.Helpers.dll is the newest file of all, because we
- //// just compiled it to start this test. Delta.Utilities.Testing.dll
- //// doesn't change that often (and even if, it was compiled before).
- //Assert.True(FileHelper.CheckIfFileIsNewer(
- // "Delta.Utilities.Helpers.dll", "Delta.Utilities.Testing.dll"));
- //Assert.False(FileHelper.CheckIfFileIsNewer(
- // "Delta.Utilities.Testing.dll", "Delta.U