using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnrealBuildTool
{
///
/// Base class for file system objects (files or directories).
///
public abstract class FullFileSystemName
{
///
/// The path to this object. Stored as an absolute path, with O/S preferred separator characters, and no trailing slash for directories.
///
public readonly string FullName;
///
/// The canonical full name for this object.
///
public readonly string CanonicalName;
///
/// Constructs a filesystem object for the given path.
///
public FullFileSystemName(string InPath)
{
FullName = Path.GetFullPath(InPath);
CanonicalName = FullName.ToLowerInvariant();
}
///
/// Direct constructor for a path
///
protected FullFileSystemName(string InFullName, string InCanonicalName)
{
FullName = InFullName;
CanonicalName = InCanonicalName;
}
///
/// Determines if the given object is at or under the given directory
///
///
///
public bool IsUnderDirectory(FullDirectoryName Other)
{
return CanonicalName.StartsWith(Other.CanonicalName) && (CanonicalName.Length == Other.CanonicalName.Length || CanonicalName[Other.CanonicalName.Length] == Path.DirectorySeparatorChar);
}
///
/// Creates a relative path from the given base directory
///
/// The directory to create a relative path from
/// A relative path from the given directory
public string MakeRelativeTo(FullDirectoryName Directory)
{
// Find how much of the path is common between the two paths. This length does not include a trailing directory separator character.
int CommonDirectoryLength = -1;
for (int Idx = 0;;Idx++)
{
if(Idx == CanonicalName.Length)
{
// The two paths are identical. Just return the "." character.
if(Idx == Directory.CanonicalName.Length)
{
return ".";
}
// Check if we're finishing on a complete directory name
if(Directory.CanonicalName[Idx] == Path.DirectorySeparatorChar)
{
CommonDirectoryLength = Idx;
}
break;
}
else if(Idx == Directory.CanonicalName.Length)
{
// Check whether the end of the directory name coincides with a boundary for the current name.
if(CanonicalName[Idx] == Path.DirectorySeparatorChar)
{
CommonDirectoryLength = Idx;
}
break;
}
else
{
// Check the two paths match, and bail if they don't. Increase the common directory length if we've reached a separator.
if (CanonicalName[Idx] != Directory.CanonicalName[Idx])
{
break;
}
if (CanonicalName[Idx] == Path.DirectorySeparatorChar)
{
CommonDirectoryLength = Idx;
}
}
}
// If there's no relative path, just return the absolute path
if(CommonDirectoryLength == -1)
{
return FullName;
}
// Append all the '..' separators to get back to the common directory, then the rest of the string to reach the target item
StringBuilder Result = new StringBuilder();
for(int Idx = CommonDirectoryLength + 1; Idx < Directory.CanonicalName.Length; Idx++)
{
// Move up a directory
Result.Append("..");
Result.Append(Path.DirectorySeparatorChar);
// Scan to the next directory separator
while(Idx < Directory.CanonicalName.Length && Directory.CanonicalName[Idx] != Path.DirectorySeparatorChar)
{
Idx++;
}
}
if(CommonDirectoryLength + 1 < FullName.Length)
{
Result.Append(FullName, CommonDirectoryLength + 1, FullName.Length - CommonDirectoryLength - 1);
}
return Result.ToString();
}
///
/// Returns a string representation of this filesystem object
///
/// Full path to the object
public override string ToString()
{
return FullName;
}
}
///
/// Representation of a directory in the filesystem
///
public class FullDirectoryName : FullFileSystemName
{
///
/// Default constructor.
///
/// Path to this directory.
public FullDirectoryName(string InPath)
: base(InPath)
{
}
///
/// Constructor for creating a directory object directly from two strings.
///
///
///
protected FullDirectoryName(string InFullName, string InCanonicalName)
: base(InFullName, InCanonicalName)
{
}
///
/// Gets the directory containing this object
///
/// A new directory object representing the directory containing this object
public FullDirectoryName GetParentDirectory()
{
if (IsRootDirectory())
{
return null;
}
int ParentLength = CanonicalName.LastIndexOf(Path.DirectorySeparatorChar);
if (ParentLength == 2 && CanonicalName[1] == ':')
{
ParentLength++;
}
return new FullDirectoryName(FullName.Substring(0, ParentLength), CanonicalName.Substring(0, ParentLength));
}
///
/// Gets the parent directory for a file
///
/// The file to get directory for
/// The full directory name containing the given file
public static FullDirectoryName GetParentDirectory(FullFileName File)
{
int ParentLength = File.CanonicalName.LastIndexOf(Path.DirectorySeparatorChar);
return new FullDirectoryName(File.FullName.Substring(0, ParentLength), File.CanonicalName.Substring(0, ParentLength));
}
///
/// 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 CanonicalName[CanonicalName.Length - 1] == Path.DirectorySeparatorChar;
}
///
/// 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 ==(FullDirectoryName A, FullDirectoryName B)
{
return A.CanonicalName == B.CanonicalName;
}
///
/// 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 !=(FullDirectoryName A, FullDirectoryName B)
{
return A.CanonicalName != B.CanonicalName;
}
///
/// Compares against another object for equality.
///
/// First name to compare.
/// Second name to compare.
/// True if the names represent the same object, false otherwise
public override bool Equals(object Obj)
{
return (Obj is FullDirectoryName) && ((FullDirectoryName)Obj) == this;
}
///
/// Returns a hash code for this object
///
///
public override int GetHashCode()
{
return CanonicalName.GetHashCode();
}
}
///
/// Representation of a file in the filesystem
///
public class FullFileName : FullFileSystemName
{
///
/// Default constructor.
///
/// Path to this file
public FullFileName(string InPath)
: base(InPath)
{
}
///
/// Gets the directory containing this file
///
/// A new directory object representing the directory containing this object
public FullDirectoryName GetDirectory()
{
return FullDirectoryName.GetParentDirectory(this);
}
///
/// 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 ==(FullFileName A, FullFileName B)
{
return A.CanonicalName == B.CanonicalName;
}
///
/// 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 !=(FullFileName A, FullFileName B)
{
return A.CanonicalName != B.CanonicalName;
}
///
/// Compares against another object for equality.
///
/// First name to compare.
/// Second name to compare.
/// True if the names represent the same object, false otherwise
public override bool Equals(object Obj)
{
return (Obj is FullFileName) && ((FullFileName)Obj) == this;
}
///
/// Returns a hash code for this object
///
///
public override int GetHashCode()
{
return CanonicalName.GetHashCode();
}
}
}