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