using UnityEditor;
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Perforce.P4;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using log4net;
namespace P4Connect
{
///
/// Utility methods for P4Connect
///
public class Utils
{
static bool caseKnown = false;
static bool caseSensitive = false;
private static readonly ILog log = LogManager.GetLogger(typeof(Utils));
///
/// Remove directory wild cards from the end of a path
///
///
///
public static string RemoveDirectoryWildcards(string path)
{
if (path.EndsWith("..."))
{
return path.Substring(0, path.Length - 4);
}
return path;
}
public static string AddDirectoryWildcards(string path)
{
if (! path.EndsWith("..."))
{
return path + "/...";
}
return path;
}
///
/// Returns true if running on a case insensitive file system.
///
public static bool IsCaseSensitive()
{
// Return cached results
if (caseKnown)
return caseSensitive;
// The assets directory should always exist in a project
string path = Main.DataPath;
bool u = System.IO.Directory.Exists(path.ToUpper());
bool l = System.IO.Directory.Exists(path.ToLower());
if (u ^ l)
return true; // default to true for a new project, but don't cache the result.
if (u && l){
caseSensitive = false;
} else {
caseSensitive = true;
}
caseKnown = true;
return caseSensitive;
}
public static bool IsRunningUnity5()
{
if (Application.unityVersion.Trim()[0] == '5')
return true;
return false;
}
///
/// Retrieves a displayable filename from a perforce file spec
///
public static string GetFilename(FileAndMeta aFileAndMeta)
{
return GetFilename(aFileAndMeta, Config.ShowPaths);
}
///
/// Retrieves a displayable filename from a perforce file spec
///
public static string GetFilename(FileAndMeta aFileAndMeta, bool aShowPath)
{
// Base version uses whatever prefs the user set
if (aFileAndMeta.File != null)
return GetFilename(aFileAndMeta.File, aShowPath);
else if (aFileAndMeta.Meta != null)
return GetFilename(aFileAndMeta.Meta, aShowPath);
else
return "";
}
///
/// Retrieves a displayable filename from a perforce file spec
///
public static string GetFilename(Perforce.P4.FileSpec aFile)
{
// Base version uses whatever prefs the user set
return GetFilename(aFile, Config.ShowPaths);
}
///
/// Retrieves a displayable filename from a perforce file spec
///
public static string GetFilename(Perforce.P4.FileSpec aFile, bool aShowPath)
{
string filename = "";
if (aFile == null)
return "null";
if (aFile.ClientPath != null)
filename = aFile.ClientPath.Path;
else if (aFile.DepotPath != null)
filename = aFile.DepotPath.Path;
else
filename = aFile.LocalPath.Path;
if (!filename.EndsWith("...") && !aShowPath)
{
// parse out the file name
filename = Path.GetFileName(filename);
}
return UnescapeFilename(filename);
}
///
/// Separates the files based on their extension
///
public static void FilterTextAndBinary(List aFiles, List aOutText, List aOutBinary, List aOutUnknown)
{
foreach (var file in aFiles)
{
switch (FilterTextAndBinary(file))
{
case FilterTextAndBinaryResult.Binary:
aOutBinary.Add(file);
break;
case FilterTextAndBinaryResult.Text:
aOutText.Add(file);
break;
case FilterTextAndBinaryResult.Unknown:
aOutUnknown.Add(file);
break;
}
}
}
public enum FilterTextAndBinaryResult
{
Text,
Binary,
Unknown
}
///
/// Separates the files based on their extension
///
public static FilterTextAndBinaryResult FilterTextAndBinary(FileSpec aFile)
{
bool defaultToBinary = false;
if (EditorSettings.serializationMode == SerializationMode.ForceBinary || EditorSettings.serializationMode == SerializationMode.Mixed)
{
defaultToBinary = true;
}
string extension = System.IO.Path.GetExtension(GetFilename(aFile, false)).ToLowerInvariant();
// Certain files are forced as text!
if (extension == ".meta" ||
extension == ".txt" ||
extension == ".html" ||
extension == ".htm" ||
extension == ".xml" ||
// extension == ".bytes" ||
extension == ".json" ||
extension == ".csv" ||
extension == ".yaml" ||
extension == ".fnt" ||
extension == ".js" ||
extension == ".cs" ||
extension == ".sln" ||
extension == ".csproj" ||
extension == ".compute" ||
extension == ".unityproj")
{
return FilterTextAndBinaryResult.Text;
}
// Others depend on the serialization setting
else if (extension == ".scene" ||
extension == ".prefab" ||
extension == ".mat" ||
extension == ".cubemap" ||
extension == ".flare" ||
extension == ".rendertexture" ||
extension == ".controller" ||
extension == ".anim" ||
extension == ".overridecontroller" ||
extension == ".mask" ||
extension == ".physicmaterial" ||
extension == ".physicmaterial2d" ||
extension == ".guiskin" ||
extension == ".fontsettings")
{
if (defaultToBinary)
{
return FilterTextAndBinaryResult.Binary;
}
else
{
return FilterTextAndBinaryResult.Text;
}
}
// And the rest, we leave up to perforce to determine.
else
{
return FilterTextAndBinaryResult.Unknown;
}
}
///
/// Escape filenames for perforce
///
public static string EscapeFilename(string aFilename)
{
return aFilename.Replace("%", "%25").Replace("@", "%40").Replace("#", "%23").Replace("*", "%2A");
}
///
/// Unescape filenames for perforce
///
public static string UnescapeFilename(string aFilename)
{
return aFilename.Replace("%25", "%").Replace("%40", "@").Replace("%23", "#").Replace("%2A", "*");
}
///
/// Compare the list of submitted files to those retrieved.
/// If there are unmatched files, they are reported using aErrorMessage
///
/// Submitted Files
/// Files returned from operation
/// error message appropriate for left-overs
public static void CheckForMissingFilesAndWarn(IList aSubmitted, IList aRetrieved, string aErrorMessage)
{
#if DEBUG
//log.Debug("CheckForMissingFilesAndWarn in: " + Logger.FileSpecListToString(aSubmitted) + " out: " + Logger.FileSpecListToString(aRetrieved));
//log.Debug("ErrorMessage: " + aErrorMessage);
#endif
if (aSubmitted != null && aSubmitted.Count > 0)
{
// Make a copy of the submitted list
List remainder = new List(aSubmitted.UnversionedSpecs());
// Remove special entries
remainder.RemoveAll(spec => spec == null || (spec.ToString().EndsWith("...")));
//log.Debug("remainder: " + Logger.FileSpecListToString(remainder));
// Remove all the files we did get back
if (aRetrieved != null)
{
foreach (var spec in aRetrieved.UnversionedSpecs())
{
for (int i = 0; i < remainder.Count; ++i)
{
// Submitted is Asset Path
// Retrieved is Depot Paths
#if DEBUG
// log.Debug("\ttry this: " + LocalPathToRelativePath(remainder[i].LocalPath.Path));
// log.Debug("\t against: " + spec.LocalPath.Path);
#endif
if (spec.LocalPath.Path.EndsWith(LocalPathToRelativePath(remainder[i].LocalPath.Path), !IsCaseSensitive(), null))
{
//log.Debug("\t removed element : " + i.ToString());
remainder.RemoveAt(i);
break;
}
}
}
}
if (remainder.Count > 0)
{
StringBuilder builder = new StringBuilder();
builder.AppendLine(aErrorMessage);
foreach (var spec in remainder)
{
if (spec != null)
{
builder.AppendLine("\t" + GetFilename(spec, true));
}
}
string errorMsg = builder.ToString();
#if DEBUG
log.Debug("\tP4Connect - " + errorMsg);
#endif
EditorUtility.DisplayDialog("P4Connect - Error", errorMsg, "Ok");
}
}
}
#region LogFiles
///
/// Logs a message to the console, formatting the filename properly
///
public static void LogFile(FileAndMeta aFileAndMeta, string aMsg)
{
if (aFileAndMeta.File != null)
{
string filename = GetFilename(aFileAndMeta.File);
if (aFileAndMeta.Meta != null)
filename += " (and meta)";
Debug.Log("P4Connect - " + string.Format(aMsg, filename));
}
else
{
if (aFileAndMeta.Meta != null)
{
string filename = GetFilename(aFileAndMeta.Meta);
Debug.Log("P4Connect - " + string.Format(aMsg, filename));
}
else
{
Debug.LogError("null file AND meta passed in to LogFile()");
}
}
}
///
/// Logs a message to the console, formatting the filename properly
///
public static void LogFile(Perforce.P4.FileSpec aFile, string aMsg)
{
LogFile(new FileAndMeta(aFile, null), aMsg);
}
///
/// Logs a message to the console, formatting the filename properly
///
public static void LogFiles(IList aFiles, string aMsg)
{
if (aFiles != null)
{
List fileAndMetas = aFiles.ToFileAndMetas().ToList();
LogFiles(fileAndMetas, aMsg);
}
}
///
/// Logs a message to the console, formatting the filename properly
///
public static void LogFiles(IList aFilesAndMetas, string aMsg)
{
if (aFilesAndMetas != null && aFilesAndMetas.Count > 0)
{
if (aFilesAndMetas.Count == 1)
{
LogFile(aFilesAndMetas[0], aMsg);
}
else
{
StringBuilder builder = new StringBuilder();
builder.Append(aFilesAndMetas.Count.ToString());
builder.Append(" assets.\n");
foreach (var fileAndMeta in aFilesAndMetas)
{
if (fileAndMeta.File != null)
{
string filename = GetFilename(fileAndMeta.File, true);
if (fileAndMeta.Meta != null)
filename += " (and meta)";
builder.Append(filename);
builder.Append("\n");
}
else
{
if (fileAndMeta.Meta != null)
{
string filename = GetFilename(fileAndMeta.Meta, true);
builder.Append(filename);
builder.Append("\n");
}
else
{
Debug.LogError("null file AND meta passed in to LogFile()");
}
}
}
builder.Remove(builder.Length - 1, 1);
Debug.Log("P4Connect - " + string.Format(aMsg, builder.ToString()));
}
}
}
///
/// Logs a message to the console, formatting the filename properly
///
public static void LogFile(FileAndMeta aFileAndMeta, FileAndMeta aMoveToFileAndMeta, string aMsg, bool aShowPath)
{
if (aFileAndMeta.File != null && aMoveToFileAndMeta.File != null)
{
string filename = GetFilename(aFileAndMeta.File, aShowPath);
string moveToFilename = GetFilename(aMoveToFileAndMeta.File, aShowPath);
if (aFileAndMeta.Meta != null)
filename += " (and meta)";
Debug.Log("P4Connect - " + string.Format(aMsg, filename, moveToFilename));
}
else
{
if (aFileAndMeta.Meta != null && aMoveToFileAndMeta.Meta != null)
{
string filename = GetFilename(aFileAndMeta.Meta, aShowPath);
string moveToFilename = GetFilename(aMoveToFileAndMeta.Meta, aShowPath);
Debug.Log("P4Connect - " + string.Format(aMsg, filename, moveToFilename));
}
else
{
Debug.LogError("null file AND meta passed in to LogFile()");
}
}
}
///
/// Logs a message to the console, formatting the filename properly
///
public static void LogFiles(IList aFilesAndMetas, IList aMoveToFilesAndMetas, string aMsg)
{
if (aFilesAndMetas != null && aMoveToFilesAndMetas != null && aFilesAndMetas.Count > 0 && aMoveToFilesAndMetas.Count > 0)
{
if (aFilesAndMetas.Count == 1 && aMoveToFilesAndMetas.Count == 1)
{
LogFile(aFilesAndMetas[0], aMoveToFilesAndMetas[0], aMsg);
}
else
{
StringBuilder builderFrom = new StringBuilder();
foreach (var fileAndMeta in aFilesAndMetas)
{
if (fileAndMeta.File != null)
{
string filename = GetFilename(fileAndMeta.File, true);
if (fileAndMeta.Meta != null)
filename += " (and meta)";
builderFrom.Append(filename);
builderFrom.Append("\n");
}
else
{
if (fileAndMeta.Meta != null)
{
string filename = GetFilename(fileAndMeta.Meta, true);
builderFrom.Append(filename);
builderFrom.Append("\n");
}
else
{
Debug.LogError("null file AND meta passed in to LogFile()");
}
}
}
builderFrom.Remove(builderFrom.Length - 1, 1);
Debug.Log("P4Connect - " + string.Format(aMsg, aFilesAndMetas.Count.ToString(), "a new location") + "\n" + builderFrom.ToString());
}
}
}
///
/// Logs a message to the console, formatting the filename properly
///
public static void LogFiles(IList aFiles, IList aMoveToFilesAndMetas, string aMsg)
{
if (aFiles != null && aMoveToFilesAndMetas != null)
{
List fileAndMetas = aFiles.ToFileAndMetas().ToList();
LogFiles(fileAndMetas, aMoveToFilesAndMetas, aMsg);
}
}
///
/// Logs a message to the console, formatting the filename properly
///
public static void LogFile(FileAndMeta aFileAndMeta, FileAndMeta aMoveToFileAndMeta, string aMsg)
{
LogFile(aFileAndMeta, aMoveToFileAndMeta, aMsg, Config.ShowPaths);
}
///
/// Logs a message to the console, formatting the filename properly
///
public static void LogFile(Perforce.P4.FileSpec aFile, Perforce.P4.FileSpec aMoveToFile, string aMsg, bool aShowPath)
{
LogFile(new FileAndMeta(aFile, null), new FileAndMeta(aMoveToFile, null), aMsg, aShowPath);
}
///
/// Logs a message to the console, formatting the filename properly
///
public static void LogFile(Perforce.P4.FileSpec aFile, Perforce.P4.FileSpec aMoveToFile, string aMsg)
{
LogFile(aFile, aMoveToFile, aMsg, Config.ShowPaths);
}
///
/// Logs a warning to the console, formatting the filename properly
///
public static void LogFileWarning(Perforce.P4.FileSpec aFile, string aMsg)
{
Debug.LogWarning("P4Connect - " + GetFilename(aFile) + aMsg);
}
///
/// Logs a warning to the console, formatting the filename properly
///
public static void LogFileWarning(string aFile, string aMsg)
{
Debug.LogWarning("P4Connect - " + aFile + aMsg);
}
#endregion
///
/// Checks whether the filepath can be used by perforce
///
public static bool IsFilePathValid(string aFilePath)
{
bool ret = true;
char[] forbiddenChars = new char[] { '@', '#', '*', '%' };
for (int i = 0; ret && i < forbiddenChars.Length; ++i)
{
if (aFilePath.Contains(forbiddenChars[i]))
{
ret = false;
}
}
return ret;
}
public static string ClientPathToLocalPath(string aClientPath)
{
string ClientPrefix = "//" + Config.Workspace;
return aClientPath.Substring(ClientPrefix.Length + 1).Replace('/', System.IO.Path.DirectorySeparatorChar);
}
public static string ClientPathToFullPath(string aClientPath)
{
return LocalPathToFullPath(ClientPathToLocalPath(aClientPath));
}
public static string DepotPathToLocalPath(PerforceConnection aConnection, FileSpec aDepotPath)
{
IList mapping = aConnection.P4Client.GetClientFileMappings(null, aDepotPath);
if (mapping != null && mapping.Count > 0)
{
return ClientPathToLocalPath(mapping[0].ClientPath.Path);
}
else
{
return "";
}
}
public static string DepotPathToFullPath(PerforceConnection aConnection, FileSpec aDepotPath)
{
IList mapping = aConnection.P4Client.GetClientFileMappings(null, aDepotPath);
if (mapping != null && mapping.Count > 0)
{
return ClientPathToFullPath(mapping[0].ClientPath.Path);
}
else
{
return "";
}
}
public static string LocalPathToRelativePath(string aLocalPath)
{
string result = aLocalPath;
string project = Main.RootPath;
if (aLocalPath.StartsWith(project, !IsCaseSensitive(), null))
{
if (aLocalPath.Length > project.Length)
{
result = aLocalPath.Substring(project.Length);
}
}
//Debug.Log("LocalPathToRelativePath returns " + result);
return result;
}
public static string AssetPathToLocalPath(string aAssetPath)
{
return System.IO.Path.Combine(Main.RootPath, aAssetPath).Replace('/', Path.DirectorySeparatorChar);
}
public static string LocalPathToAssetPath(string aLocalPath)
{
if (aLocalPath.StartsWith(Main.RootPath, !IsCaseSensitive(), null))
{
var cleanedRoot = Main.RootPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
if (aLocalPath.Length > cleanedRoot.Length + 1)
{
return aLocalPath.Substring(cleanedRoot.Length + 1).Replace(Path.DirectorySeparatorChar, '/');
}
else
{
return aLocalPath.Replace(Path.DirectorySeparatorChar, '/');
}
}
else
{
return aLocalPath.Replace(Path.DirectorySeparatorChar, '/');
}
}
public static string LocalPathToClientPath(string aLocalPath)
{
string ClientPrefix = "//" + Config.Workspace;
return ClientPrefix + "/" + aLocalPath.Replace(Path.DirectorySeparatorChar, '/');
}
public static bool IsDirectory(string aAssetPath)
{
if (aAssetPath.EndsWith("..."))
return true;
else
{
string fullPath = LocalPathToFullPath(aAssetPath);
if (Directory.Exists(fullPath + Path.DirectorySeparatorChar))
return true;
else if (Path.GetExtension(aAssetPath) == string.Empty)
return true;
else
return false;
}
}
public static string MetaFromAsset(string aAssetPath)
{
return aAssetPath + ".meta";
}
public static string AssetFromMeta(string aMetaPath)
{
return aMetaPath.Substring(0, aMetaPath.Length - 5);
}
public static string AssetPathToFullPath(string aAssetPath)
{
return Path.Combine(Main.RootPath, aAssetPath.Replace('/', Path.DirectorySeparatorChar));
}
public static string LocalPathToFullPath(string aLocalPath)
{
return AssetPathToFullPath(LocalPathToAssetPath(aLocalPath));
}
public static string FullPathToAssetPath(string aFullPath)
{
return aFullPath.Substring(Main.RootPath.Length).Replace(System.IO.Path.DirectorySeparatorChar, '/');
}
public static void GetFileAndMeta(string aName, out string aFileName, out string aMetaName)
{
// Is this a .meta file? if so we need to fetch the original file name from it
if (IsMetaFile(aName))
{
aMetaName = Utils.AssetPathToLocalPath(aName);
aFileName = aMetaName.Substring(0, aMetaName.Length - 5);
}
// If it's not a meta file, generate the meta file name from it
else
{
aFileName = Utils.AssetPathToLocalPath(aName);
aMetaName = aFileName + ".meta";
}
}
///
/// Given a filename, compute the meta file name
///
/// file to get meta for
/// meta filename
public static string GetMetaFile(string aName)
{
if (IsMetaFile(aName))
return aName;
return aName + ".meta";
}
///
/// Given a FileState, return a string description.
///
///
///
public static string GetFileStateString(FileState aState)
{
string ret = "";
switch (aState)
{
case FileState.None:
ret = "";
break;
case FileState.InDepot:
ret = "None";
break;
case FileState.MarkedForEdit:
ret = "Edit";
break;
case FileState.MarkedForAdd:
ret = "Add";
break;
case FileState.MarkedForDelete:
ret = "Delete";
break;
case FileState.MarkedForAddMove:
ret = "Move/Add";
break;
case FileState.MarkedForDeleteMove:
ret = "Move/Delete";
break;
}
return ret;
}
public static string GetStorageTypeString(StorageType aType)
{
string ret = "Other";
switch (aType)
{
case StorageType.Text:
ret = "Text";
break;
case StorageType.Binary:
ret = "Binary";
break;
}
return ret;
}
public static List GetDeepSelectedEffectivePaths()
{
var objects = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets);
List assets = new List();
foreach (var obj in objects)
{
string assetPath = AssetDatabase.GetAssetPath(obj);
assets.Add(assetPath);
}
return assets;
}
///
/// Given a Collection of Local Paths, Explode the list to include all children in the output Collection
/// Only return the files with full paths, not directories
///
///
///
public static IEnumerable ExplodeLocalPaths(IEnumerable paths)
{
foreach(string path in paths)
{
if (System.IO.Directory.Exists(path))
{
// Get The Folder Contents
var files = System.IO.Directory.GetFiles(path, "*.*", System.IO.SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
yield return file;
}
var dfiles = ExplodeLocalPaths(System.IO.Directory.GetDirectories(path));
foreach (string dfile in dfiles)
{
yield return dfile;
}
}
else
{
yield return path;
}
}
yield break;
}
public static void LaunchDiffAgainstHaveRev(string aAsset)
{
if (System.IO.File.Exists(Config.DiffToolPathname))
{
Engine.PerformConnectionOperation(con =>
{
FileSpec localSpec = FileSpec.LocalSpec(Utils.AssetPathToLocalPath(aAsset));
IList metaDataList = Engine.GetFileMetaData(con, null, localSpec);
if (metaDataList != null && metaDataList.Count == 1)
{
BaseFileType baseFileType = metaDataList[0].Type.BaseType;
if ((baseFileType == BaseFileType.Text) || (baseFileType == BaseFileType.Unicode) || (baseFileType == BaseFileType.UTF16))
{
if (metaDataList[0].HaveRev != -1)
{
FileSpec headRevSpec = FileSpec.LocalSpec(Utils.AssetPathToLocalPath(aAsset), metaDataList[0].HaveRev);
IList contents = con.P4Depot.GetFileContents(null, headRevSpec);
if (contents != null && contents.Count == 2)
{
// Create a temp file ad dump the content in there
string haveRevPathname = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetFileName(headRevSpec.ToString()));
System.IO.StreamWriter tempFile = System.IO.File.CreateText(haveRevPathname);
tempFile.Write(contents[1]);
tempFile.Close();
// Fetch the full pathname of the original file
string localPathname = Utils.AssetPathToFullPath(aAsset);
// Now run the diff tool
string arguments = haveRevPathname + " " + localPathname;
System.Diagnostics.Process.Start(Config.DiffToolPathname, arguments);
}
}
else
{
EditorUtility.DisplayDialog("File not Synced", "You have never synced this file before and cannot compare it against your HAVE revision", "Ok");
}
}
}
});
}
else
{
EditorUtility.DisplayDialog("No Diff Utility Set", "You must point P4Connect to your favorite Diff Utility in Edit->Perforce Settings to use this feature.", "Ok");
}
}
public static string GetBridgeDirectory()
{
return System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) +
System.IO.Path.DirectorySeparatorChar + (IntPtr.Size == 4 ? "x86" : "x86_64" ) + System.IO.Path.DirectorySeparatorChar;
}
public static string GetEditorAssetFullDirectory()
{
return System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + System.IO.Path.DirectorySeparatorChar;
}
static string GetFileSpecPath(FileSpec aSpec)
{
string filePath = "";
if (aSpec.LocalPath != null)
filePath = aSpec.LocalPath.Path;
else if (aSpec.ClientPath != null)
filePath = aSpec.ClientPath.Path;
else if (aSpec.DepotPath != null)
filePath = aSpec.DepotPath.Path;
return filePath;
}
///
/// Matches up a list of meta data to the list of files it was generated from
/// Uses local paths.
/// Which files to find metadata for
/// A list of random Metadata
/// One Entry per file, contains Metadata or null
///
public static void GetMatchingMetaData(List aFiles, IList aMetaData, List aOutMeta)
{
if (aMetaData == null || aFiles == null || aOutMeta == null)
return;
//Debug.Log(Logger.FileMetaDataListToString(aMetaData));
var metaDataPaths = aMetaData.ToDictionary(g => g.LocalPath.Path.ToLowerInvariant(), g => g);
//Debug.Log(Logger.FileSpecListToString(aFiles));
foreach (var file in aFiles)
{
if (file != null)
{
FileMetaData meta = null;
if (! metaDataPaths.TryGetValue(GetLocalPath(file).ToLowerInvariant(), out meta))
{
meta = new FileMetaData();
}
aOutMeta.Add(meta);
}
else
{
//log.Debug("FileData Null!");
aOutMeta.Add(null);
}
}
}
public static bool IsSolutionProjectFile(string aPath)
{
return aPath.EndsWith(".csproj") || aPath.EndsWith(".sln");
}
public static bool IsMetaFile(string aPath)
{
return (aPath.EndsWith(".meta"));
}
public static bool IsDirOrValidSubDirectoryOf(string aSubPath, string aAncestorPath)
{
System.IO.DirectoryInfo subDir = new System.IO.DirectoryInfo(aSubPath.TrimEnd(System.IO.Path.DirectorySeparatorChar, '/'));
System.IO.DirectoryInfo ancestorDir = new System.IO.DirectoryInfo(aAncestorPath.TrimEnd(System.IO.Path.DirectorySeparatorChar, '/'));
bool isParent = false;
while (subDir != null)
{
if (string.Compare(subDir.FullName, ancestorDir.FullName, StringComparison.InvariantCultureIgnoreCase) == 0)
{
isParent = true;
break;
}
else subDir = subDir.Parent;
}
return isParent;
}
public static string GetLocalPath(FileSpec fs)
{
if (fs.LocalPath != null && fs.LocalPath.Path != null)
return fs.LocalPath.Path;
else if (fs.ClientPath != null && fs.ClientPath.Path != null)
return ClientPathToLocalPath(fs.ClientPath.Path);
Debug.Log("no local or clientpath in FileSpec");
return ("");
}
} // Utils class
///
/// Extensions class contains extension methods for use
///
public static class Extensions
{
private static readonly ILog log = LogManager.GetLogger(typeof(Extensions));
///
/// From a Collection of operations, remove all the entries with No operation specified
///
/// Collection of FileAndOperations
/// Filtered Collection
public static IEnumerable NoNoOps(this IEnumerable faos)
{
foreach(var fao in faos)
{
if (fao.FileOp != Engine.FileOperation.None)
yield return fao;
}
yield break;
}
///
/// Extension method to convert a list of Filespecs into ones with valid localpaths
///
///
///
///
public static IEnumerable FixLocalPaths(this IEnumerable aDepotPaths, PerforceConnection aConnection)
{
if (aDepotPaths == null || !aDepotPaths.Any())
{
//log.Debug("no depot paths");
yield break;
}
var needed = aDepotPaths.UnversionedSpecs();
IList mapping = aConnection.P4Client.GetClientFileMappings(needed.ToArray());
if (mapping == null || mapping.Count == 0)
{
//log.Debug("no mappings");
yield break;
}
//log.Debug("ClientFileMappings: " + Logger.FileSpecListToString(aDepotPaths.ToList()));
foreach (var entry in mapping)
{
//log.Debug("fix: " + entry.ToStringNullSafe());
FileSpec val = entry;
if (entry.LocalPath == null && entry.ClientPath != null)
{
val = FileSpec.LocalSpec(Utils.ClientPathToLocalPath(entry.ClientPath.Path));
}
yield return val;
}
yield break;
}
///
/// Scan a collection of asset paths, Identify directoryies, add Perforce wildcard "/..."
///
/// paths to scan
/// paths with directories containing wildcards
public static IEnumerable AddDirectoryWildcards(this IEnumerable paths)
{
foreach( string path in paths)
{
if (Utils.IsDirectory(path) && (! path.EndsWith("...")))
{
yield return path + "/...";
}
else
{
yield return path;
}
}
yield break;
}
///
/// Strip Directory paths from a path collection
///
/// path collection
/// Filtered path collection
public static IEnumerable StripDirectories(this IEnumerable paths)
{
foreach (string path in paths)
{
if (! Utils.IsDirectory(path))
{
yield return path;
}
}
yield break;
}
///
/// Return only Directory paths from an asset path collection
///
/// path collection
/// Just the directory paths
public static IEnumerable JustDirectories(this IEnumerable paths)
{
foreach (string path in paths)
{
if (Utils.IsDirectory(path))
{
yield return path;
}
}
yield break;
}
///
/// Extension for FileSpec which allows the creation of FileAndMeta collections from FileSpec collections
///
/// Filespecs to process
/// FileAndMetas (matched pairs when possible)
public static IEnumerable ToFileAndMetas(this IEnumerable specs)
{
FamManager fm = new FamManager();
foreach(FileSpec spec in specs)
fm.Add(spec);
foreach (var val in fm.Values)
yield return (val);
yield break;
}
#region FamManager
///
/// Class used by ToFilesAndMetas to collect files into pairs (asset and meta) with appropriate types.
/// Results are returned as a FileAndMeta collection
///
class FamManager
{
private static readonly ILog log = LogManager.GetLogger(typeof(FamManager));
Dictionary famDictionary;
public FamManager()
{
famDictionary = new Dictionary();
}
///
/// Add an asset path to the Fam Manager
///
///
public void Add(FileSpec fspec)
{
//log.Debug("Add: " + path);
fspec = fspec.StripVersion(); // remove any version information from FileSpec
string path = fspec.ToString();
if (Utils.IsMetaFile(path))
{
// It's a meta file, is there an asset in the list as well?
string assetpath = Utils.AssetFromMeta(path);
FileAndMeta existing_fam = new FileAndMeta();
if (famDictionary.TryGetValue(assetpath, out existing_fam))
{
existing_fam.Meta = fspec;
famDictionary[assetpath] = existing_fam; // update existing entry
}
else
{
// Add a new entry to the famDictionary
existing_fam.Meta = fspec;
existing_fam.File = null;
famDictionary[assetpath] = existing_fam; // Add new entry to famDictionary
}
}
else
{
string metapath = Utils.MetaFromAsset(path);
FileAndMeta existing_fam = new FileAndMeta();
// It's not a meta file, is there a meta as well?
if (famDictionary.TryGetValue(path, out existing_fam))
{
existing_fam.File = fspec;
famDictionary[path] = existing_fam; // update existing entry
}
else
{
FileAndMeta fam = new FileAndMeta();
fam.File = fspec;
fam.Meta = null;
famDictionary[path] = fam; // Add entry to famDictionary
}
}
}
///
/// Return all famDictionary elements
///
public ICollection Values
{
get
{
//foreach (var entry in famDictionary.Values)
//{
// log.Debug("Dict entry: " + entry.ToString());
//}
// log.Debug("fams: " + Logger.FileAndMetaListToString(famDictionary.Values.ToList()));
return famDictionary.Values;
}
}
}
#endregion
///
/// Generic extension method that removes all null entries from a IEnumerable
///
public static IEnumerable NonNullElements(this IEnumerable elements)
{
foreach (T fs in elements)
{
if (fs == null)
continue;
yield return fs;
}
yield break;
}
///
/// Extension for FileSpec to return asset path
///
/// FileSpec to convert to asset path
/// If provided, this routine could query the server to determine asset path from depot path
/// Asset Path, relative to project
public static string ToAssetPath(this FileSpec spec, PerforceConnection con = null)
{
string path = "";
if (spec.LocalPath != null)
path = Utils.LocalPathToAssetPath(spec.LocalPath.Path);
else if (spec.ClientPath != null)
path = Utils.LocalPathToAssetPath(Utils.ClientPathToLocalPath(spec.ClientPath.Path));
else if (spec.DepotPath != null && con != null)
path = Utils.LocalPathToAssetPath(Utils.DepotPathToLocalPath(con, spec.DepotPath));
#if DEBUG
if (path.Length == 0)
Debug.Log("ToAssetPath came up EMPTY!" + spec.ToStringNullSafe());
#endif
return path;
}
///
/// Return true if a LocalPath exists and is not empty
///
///
/// bool
public static bool HasLocalPath(this FileSpec spec)
{
if (spec == null || spec.LocalPath == null || spec.LocalPath.Path == null || spec.LocalPath.Path.Length == 0)
return false;
return true;
}
///
/// Extension to convert FileSpec into a localpath
///
///
///
public static string ToLocalPath(this FileSpec spec)
{
if (spec.LocalPath != null)
return(spec.LocalPath.Path);
else if (spec.ClientPath != null)
return(Utils.ClientPathToLocalPath(spec.ClientPath.Path));
#if DEBUG
log.Debug("Missing Local Path");
#endif
return("");
}
static Revision invalid_revision = new Revision(-1);
///
/// Extension for FileSpec, return only entries with valid versions
///
///
///
public static IEnumerable ValidSpecs(this IEnumerable specs)
{
return specs.Where( s => ! Version.Equals(s.Version, invalid_revision));
}
///
/// From a list of filespecs, just select the ones with NonLocal Specs
///
///
///
public static IEnumerable NonLocalSpecs(this IEnumerable specs)
{
return specs.Where(s => ! s.HasLocalPath());
}
public static IEnumerable JustLocalSpecs(this IEnumerable specs)
{
return specs.Where(s => s.HasLocalPath());
}
///
/// Create a list of Unversioned Filespecs from an existing list
///
/// FileSpecs to copy
/// FileSpecs with Versions stripped
public static IEnumerable UnversionedSpecs(this IEnumerable specs)
{
foreach (var fs in specs)
{
yield return fs.StripVersion();
}
yield break;
}
public static IEnumerable ToLocalPaths(this IEnumerable specs)
{
foreach (var fs in specs)
{
yield return fs.ToLocalPath();
}
yield break;
}
public static IEnumerable AssetToLocalPaths(this IEnumerable paths)
{
foreach(var path in paths)
{
yield return Utils.AssetPathToLocalPath(path);
}
yield break;
}
public static IEnumerable LocalToAssetPaths(this IEnumerable paths)
{
foreach (var path in paths)
{
yield return Utils.LocalPathToAssetPath(path);
}
yield break;
}
}
}