// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Tools.DotNETCommon { /// /// Representation of an absolute directory path. Allows fast hashing and comparisons. /// [Serializable] public class DirectoryReference : FileSystemReference, IEquatable { /// /// Special value used to invoke the non-sanitizing constructor overload /// public enum Sanitize { None } /// /// Default constructor. /// /// Path to this directory. public DirectoryReference(string InPath) : base(FixTrailingPathSeparator(Path.GetFullPath(InPath))) { } /// /// Construct a DirectoryReference from a DirectoryInfo object. /// /// Path to this file public DirectoryReference(DirectoryInfo InInfo) : base(FixTrailingPathSeparator(InInfo.FullName)) { } /// /// Constructor for creating a directory object directly from two strings. /// /// The full, sanitized path name /// Dummy argument used to resolve this overload public DirectoryReference(string InFullName, Sanitize InSanitize) : base(InFullName) { } /// /// Ensures that the correct trailing path separator is appended. On Windows, the root directory (eg. C:\) always has a trailing path separator, but no other /// path does. /// /// Absolute path to the directory /// Path to the directory, with the correct trailing path separator private static string FixTrailingPathSeparator(string DirName) { if(DirName.Length == 2 && DirName[1] == ':') { return DirName + Path.DirectorySeparatorChar; } else if(DirName.Length == 3 && DirName[1] == ':' && DirName[2] == Path.DirectorySeparatorChar) { return DirName; } else if(DirName.Length > 1 && DirName[DirName.Length - 1] == Path.DirectorySeparatorChar) { return DirName.TrimEnd(Path.DirectorySeparatorChar); } else { return DirName; } } /// /// Gets the top level directory name /// /// The name of the directory public string GetDirectoryName() { return Path.GetFileName(FullName); } /// /// Gets the directory containing this object /// /// A new directory object representing the directory containing this object public DirectoryReference ParentDirectory { get { if (IsRootDirectory()) { return null; } int ParentLength = FullName.LastIndexOf(Path.DirectorySeparatorChar); if (ParentLength == 2 && FullName[1] == ':') { ParentLength++; } return new DirectoryReference(FullName.Substring(0, ParentLength), Sanitize.None); } } /// /// Gets the parent directory for a file /// /// The file to get directory for /// The full directory name containing the given file public static DirectoryReference GetParentDirectory(FileReference File) { int ParentLength = File.FullName.LastIndexOf(Path.DirectorySeparatorChar); if(ParentLength == 2 && File.FullName[1] == ':') { ParentLength++; } return new DirectoryReference(File.FullName.Substring(0, ParentLength), Sanitize.None); } /// /// Gets the path for a special folder /// /// The folder to receive the path for /// Directory reference for the given folder, or null if it is not available public static DirectoryReference GetSpecialFolder(Environment.SpecialFolder Folder) { string FolderPath = Environment.GetFolderPath(Folder); return String.IsNullOrEmpty(FolderPath)? null : new DirectoryReference(FolderPath); } /// /// Determines whether this path represents a root directory in the filesystem /// /// True if this path is a root directory, false otherwise public bool IsRootDirectory() { return FullName[FullName.Length - 1] == Path.DirectorySeparatorChar; } /// /// Combine several fragments with a base directory, to form a new directory name /// /// The base directory /// Fragments to combine with the base directory /// The new directory name public static DirectoryReference Combine(DirectoryReference BaseDirectory, params string[] Fragments) { string FullName = FileSystemReference.CombineStrings(BaseDirectory, Fragments); return new DirectoryReference(FullName, Sanitize.None); } /// /// Compares two filesystem object names for equality. Uses the canonical name representation, not the display name representation. /// /// First object to compare. /// Second object to compare. /// True if the names represent the same object, false otherwise public static bool operator ==(DirectoryReference A, DirectoryReference B) { if ((object)A == null) { return (object)B == null; } else { return (object)B != null && A.FullName.Equals(B.FullName, Comparison); } } /// /// Compares two filesystem object names for inequality. Uses the canonical name representation, not the display name representation. /// /// First object to compare. /// Second object to compare. /// False if the names represent the same object, true otherwise public static bool operator !=(DirectoryReference A, DirectoryReference B) { return !(A == B); } /// /// Compares against another object for equality. /// /// other instance to compare. /// True if the names represent the same object, false otherwise public override bool Equals(object Obj) { return (Obj is DirectoryReference) && ((DirectoryReference)Obj) == this; } /// /// Compares against another object for equality. /// /// other instance to compare. /// True if the names represent the same object, false otherwise public bool Equals(DirectoryReference Obj) { return Obj == this; } /// /// Returns a hash code for this object /// /// public override int GetHashCode() { return Comparer.GetHashCode(FullName); } /// /// Helper function to create a remote directory reference. Unlike normal DirectoryReference objects, these aren't converted to a full path in the local filesystem. /// /// The absolute path in the remote file system /// New directory reference public static DirectoryReference MakeRemote(string AbsolutePath) { return new DirectoryReference(AbsolutePath, Sanitize.None); } /// /// Gets the parent directory for a file, or returns null if it's null. /// /// The file to create a directory reference for /// The directory containing the file public static DirectoryReference FromFile(FileReference File) { if(File == null) { return null; } else { return File.Directory; } } /// /// Create a DirectoryReference from a string. If the string is null, returns a null DirectoryReference. /// /// Path for the new object /// Returns a FileReference representing the given string, or null. public static DirectoryReference FromString(string DirectoryName) { if(String.IsNullOrEmpty(DirectoryName)) { return null; } else { return new DirectoryReference(DirectoryName); } } #region System.IO.Directory Wrapper Methods /// /// Finds the current directory /// /// The current directory public static DirectoryReference GetCurrentDirectory() { return new DirectoryReference(Directory.GetCurrentDirectory()); } /// /// Creates a directory /// /// Location of the directory public static void CreateDirectory(DirectoryReference Location) { Directory.CreateDirectory(Location.FullName); } /// /// Deletes a directory /// /// Location of the directory public static void Delete(DirectoryReference Location) { Directory.Delete(Location.FullName); } /// /// Deletes a directory /// /// Location of the directory /// Whether to remove directories recursively public static void Delete(DirectoryReference Location, bool bRecursive) { Directory.Delete(Location.FullName, bRecursive); } /// /// Checks whether the directory exists /// /// Location of the directory /// True if this directory exists public static bool Exists(DirectoryReference Location) { return Directory.Exists(Location.FullName); } /// /// Enumerate files from a given directory /// /// Base directory to search in /// Sequence of file references public static IEnumerable EnumerateFiles(DirectoryReference BaseDir) { foreach (string FileName in Directory.EnumerateFiles(BaseDir.FullName)) { yield return new FileReference(FileName, FileReference.Sanitize.None); } } /// /// Enumerate files from a given directory /// /// Base directory to search in /// Pattern for matching files /// Sequence of file references public static IEnumerable EnumerateFiles(DirectoryReference BaseDir, string Pattern) { foreach (string FileName in Directory.EnumerateFiles(BaseDir.FullName, Pattern)) { yield return new FileReference(FileName, FileReference.Sanitize.None); } } /// /// Enumerate files from a given directory /// /// Base directory to search in /// Pattern for matching files /// Options for the search /// Sequence of file references public static IEnumerable EnumerateFiles(DirectoryReference BaseDir, string Pattern, SearchOption Option) { foreach (string FileName in Directory.EnumerateFiles(BaseDir.FullName, Pattern, Option)) { yield return new FileReference(FileName, FileReference.Sanitize.None); } } /// /// Enumerate subdirectories in a given directory /// /// Base directory to search in /// Sequence of directory references public static IEnumerable EnumerateDirectories(DirectoryReference BaseDir) { foreach (string DirectoryName in Directory.EnumerateDirectories(BaseDir.FullName)) { yield return new DirectoryReference(DirectoryName, Sanitize.None); } } /// /// Enumerate subdirectories in a given directory /// /// Base directory to search in /// Pattern for matching directories /// Sequence of directory references public static IEnumerable EnumerateDirectories(DirectoryReference BaseDir, string Pattern) { foreach (string DirectoryName in Directory.EnumerateDirectories(BaseDir.FullName, Pattern)) { yield return new DirectoryReference(DirectoryName, Sanitize.None); } } /// /// Enumerate subdirectories in a given directory /// /// Base directory to search in /// Pattern for matching files /// Options for the search /// Sequence of directory references public static IEnumerable EnumerateDirectories(DirectoryReference BaseDir, string Pattern, SearchOption Option) { foreach (string DirectoryName in Directory.EnumerateDirectories(BaseDir.FullName, Pattern, Option)) { yield return new DirectoryReference(DirectoryName, Sanitize.None); } } /// /// Sets the current directory /// /// Location of the new current directory public static void SetCurrentDirectory(DirectoryReference Location) { Directory.SetCurrentDirectory(Location.FullName); } #endregion } /// /// Extension methods for passing DirectoryReference arguments /// public static class DirectoryReferenceExtensionMethods { /// /// Manually serialize a file reference to a binary stream. /// /// Binary writer to write to /// The directory reference to write public static void Write(this BinaryWriter Writer, DirectoryReference Directory) { Writer.Write((Directory == null) ? String.Empty : Directory.FullName); } /// /// Manually deserialize a directory reference from a binary stream. /// /// Binary reader to read from /// New DirectoryReference object public static DirectoryReference ReadDirectoryReference(this BinaryReader Reader) { string FullName = Reader.ReadString(); return (FullName.Length == 0) ? null : new DirectoryReference(FullName, DirectoryReference.Sanitize.None); } /// /// Writes a directory reference to a binary archive /// /// The writer to output data to /// The item to write public static void WriteDirectoryReference(this BinaryArchiveWriter Writer, DirectoryReference Directory) { if(Directory == null) { Writer.WriteString(null); } else { Writer.WriteString(Directory.FullName); } } /// /// Reads a directory reference from a binary archive /// /// Reader to serialize data from /// New directory reference instance public static DirectoryReference ReadDirectoryReference(this BinaryArchiveReader Reader) { string FullName = Reader.ReadString(); if(FullName == null) { return null; } else { return new DirectoryReference(FullName, DirectoryReference.Sanitize.None); } } } }