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 static 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
///
/// Asset Path, including wild cards
/// Asset Paths stripped of ending wild cards
public static string RemoveDirectoryWildcards(string path)
{
if (path.EndsWith("..."))
{
path = path.Substring(0, path.Length - 3);
if (path.EndsWith("/"))
{
path = path.Substring(0, path.Length - 1);
}
}
return path;
}
public static string AddDirectoryWildcards(string path)
{
if (! path.EndsWith("..."))
{
return path + "/...";
}
return path;
}
public static bool HasDirectoryWildcards(string path)
{
return (path.EndsWith("...") || path.EndsWith("*"));
}
///
/// Returns true if running on a case sensitive 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 "";
}
}
///
/// Convert a Depot Path to an Asset Path
/// Does not call back to server for a "where". Uses Config.DepotProjectRoot matching
///
/// depot path
/// asset path
public static string DepotPathToAssetPath(string dpath)
{
string DepotPrefix = RemoveDirectoryWildcards(Config.DepotProjectRoot);
if (dpath.StartsWith(DepotPrefix))
{
return dpath.Substring(DepotPrefix.Length + 1);
}
log.Debug("Malformed DepotPath: " + dpath);
return "Asset/" + dpath;
}
public static string ClientPathToAssetPath(string aClientPath)
{
return LocalPathToAssetPath(ClientPathToLocalPath(aClientPath));
}
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, '/');
}
///
/// Checks an asset path to see if it is a directory
/// Assumes paths ending with "..." are directories
/// Assumes paths without extensions are directories
///
/// Asset Path to test
/// "true" if is directory
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;
}
}
///
/// Test an asset path to see if it contains a non empty FileName
///
///
///
public static bool IsValidFilename(string aAssetPath)
{
var fn = Path.GetFileNameWithoutExtension(aAssetPath);
if (fn == null || fn.Length <= 0)
return false;
return true;
}
///
/// Predicate used to identify which Asset Paths which are directories
///
/// Asset Path to check
/// "true" if path is a directory
public static bool IsDirectoryAsset(string aAssetPath)
{
string fullpath = System.IO.Path.Combine(Main.RootPath, aAssetPath).Replace('/', Path.DirectorySeparatorChar);
return Directory.Exists(fullpath + Path.DirectorySeparatorChar);
}
///
/// Return the .meta path for a given asset
///
/// path to Asset
/// path to .meta file associated with the Asset
public static string MetaFromAsset(string aAssetPath)
{
return aAssetPath + ".meta";
}
///
/// Given a path to a .meta file, return the associated asset path
///
/// Path to .meta file
/// Path to asset
public static string AssetFromMeta(string aMetaPath)
{
return aMetaPath.Substring(0, aMetaPath.Length - 5);
}
///
/// Convert an Asset path to a Full Path
///
/// Asset Path to convert
/// Full Path
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
///
/// asset path to get meta for
/// asset path to matching meta file
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;
}
///
/// Query Unity to get the currently selected objects and convert them into Asset paths.
///
/// An Enumeration of asset paths for the selected objects
public static IEnumerable GetSelectedAssetPaths()
{
var objects = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets);
foreach (var obj in objects)
{
string assetPath = AssetDatabase.GetAssetPath(obj);
yield return assetPath;
}
yield break;
}
public static void LaunchDiffAgainstHaveRev(string aAsset)
{
if (System.IO.File.Exists(Config.DiffToolPathname))
{
Engine.PerformConnectionOperation(con =>
{
FileMetaData metaData = AssetStatusCache.GetCachedMetaDataAndUpdateDefaults(aAsset);
BaseFileType baseFileType = metaData.Type.BaseType;
if ((baseFileType == BaseFileType.Text) || (baseFileType == BaseFileType.Unicode) || (baseFileType == BaseFileType.UTF16))
{
if (metaData.HaveRev != -1)
{
FileSpec headRevSpec = FileSpec.LocalSpec(Utils.AssetPathToLocalPath(aAsset), metaData.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;
}
public static string GetEditorAssetRelativeDirectory()
{
string fullPath = GetEditorAssetFullDirectory();
return FullPathToAssetPath(fullPath);
}
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"));
}
///
/// Compare two local paths to see if one contains the other
///
/// A directory Local path
/// A parent directory Local path
/// true or false
public static bool IsDirOrValidSubDirectoryOf(string aSubPath, string aAncestorPath)
{
if (! IsCaseSensitive())
{
aSubPath = aSubPath.ToLower();
aAncestorPath = aAncestorPath.ToLower();
}
if (aSubPath.StartsWith(aAncestorPath.TrimEnd(System.IO.Path.DirectorySeparatorChar, '/'), StringComparison.InvariantCultureIgnoreCase))
return true;
//Debug.Log("aSubPath: |" + aSubPath + "|");
//Debug.Log("Ancestor: |" + aAncestorPath + "|");
return false;
}
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 ("");
}
///
/// Returns the path to use when printing the name, or looking up status info
/// Directories get EffectivePaths because they are used to associate .meta files.
///
/// Asset Path
/// The effective path
public static string GetEffectivePath(string aAssetPath)
{
if (Utils.IsDirectory(aAssetPath))
{
return Utils.MetaFromAsset(Utils.RemoveDirectoryWildcards(aAssetPath));
}
else
{
return aAssetPath;
}
}
} // Utils class
///
/// Extensions class contains extension methods for use
///
public static class Extensions
{
private static readonly ILog log = LogManager.GetLogger(typeof(Extensions));
static readonly Revision InvalidRevision = new Revision(-1);
#region FileAndOp Enumerable Extensions
///
/// From a Collection of operations, remove all the entries with No operation specified
///
/// Collection of FileAndOperations
/// Filtered Collection
public static IEnumerable RemoveNoOps(this IEnumerable faos)
{
foreach(var fao in faos)
{
if (fao.FileOp != Engine.FileOperation.None)
yield return fao;
}
yield break;
}
#endregion
#region Generic Enumerable Extensions
///
/// An Extension to split an IEnumberable into smaller pieces.
///
///
///
///
///
public static IEnumerable> Split(this IEnumerable source, int len)
{
if (len == 0)
throw new ArgumentNullException();
var enumer = source.GetEnumerator();
while (enumer.MoveNext())
{
yield return Take(enumer.Current, enumer, len);
}
}
private static IEnumerable Take(T head, IEnumerator tail, int len)
{
while (true)
{
yield return head;
if (--len == 0)
break;
if (tail.MoveNext())
head = tail.Current;
else
break;
}
}
///
/// 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;
}
#endregion
#region FileSpec Enumerable Extensions
///
/// Extension method to convert a list of Filespecs into ones with valid localpaths
///
/// An enumerable of
/// a valid PerforceConnection
///
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();
foreach (var entry in aDepotPaths)
{
if (entry.LocalPath != null)
yield return entry.LocalPath;
var assetPath = entry.ToAssetPath();
entry.LocalPath = new LocalPath(Utils.AssetPathToLocalPath(assetPath));
yield return entry;
}
//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;
}
///
/// Extension to convert a FileSpec into an assetpath
///
///
///
public static string ToAssetPath(this FileSpec spec)
{
if (HasLocalPath(spec))
return Utils.LocalPathToAssetPath(spec.LocalPath.Path);
if (HasClientPath(spec))
return Utils.ClientPathToAssetPath(spec.ClientPath.Path);
// Must only have a depot path
return Utils.DepotPathToAssetPath(spec.DepotPath.Path);
}
///
/// 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)
{
if (specs == null)
yield break;
FamManager fm = new FamManager();
foreach (FileSpec spec in specs)
fm.Add(spec);
foreach (var val in fm.Values)
yield return val;
}
///
/// Extension for FileSpec, return only entries with valid versions
///
///
///
public static IEnumerable ValidSpecs(this IEnumerable specs)
{
return specs.Where(s => !Version.Equals(s.Version, InvalidRevision));
}
///
/// 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 ToAssetPaths(this IEnumerable specs)
{
foreach (var fs in specs)
{
yield return fs.ToAssetPath();
}
yield break;
}
///
/// Create a HashSet from an Enumeration of Asset Paths.
///
/// Asset Paths
/// Hashset containing Asset Paths
public static HashSet ToHashSet(this IEnumerable files)
{
HashSet contents = new HashSet(files);
return contents;
}
#endregion
#region PathString Enumerable Extensions
///
/// Scan a collection of asset paths, Identify directories, 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;
}
}
}
///
/// Given an enumeration of asset paths, look for meta files which represent directories
/// Add the directory path (with wildcards) to the enumeration
///
/// asset paths
/// asset paths with additional directory entries
public static IEnumerable AddDirectoriesFromMetas(this IEnumerable paths)
{
var assets = new HashSet(paths);
// Look for Meta files associated with directories, if found, also add the directory with Perforce wildcards
// to the asset list
foreach (var f in paths)
{
if (Utils.IsMetaFile(f))
{
var rname = Utils.AssetFromMeta(f);
if (Utils.IsDirectory(rname))
{
yield return Utils.AddDirectoryWildcards(rname);
}
}
yield return f;
}
}
///
/// 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;
}
}
}
///
/// 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;
}
}
}
///
/// Remove paths containing Directory Wildcards from an AssetPath enumeration
///
/// asset Paths Enumeration
/// asset Paths containing no wildcards
public static IEnumerable RemoveWildCards(this IEnumerable paths)
{
foreach (var path in paths)
{
if (!Utils.HasDirectoryWildcards(path))
yield return path;
}
}
public static IEnumerable AssetToLocalPaths(this IEnumerable paths)
{
foreach (var path in paths)
{
yield return Utils.AssetPathToLocalPath(path);
}
}
public static IEnumerable LocalToAssetPaths(this IEnumerable paths)
{
foreach (var path in paths)
{
yield return Utils.LocalPathToAssetPath(path);
}
}
public static IEnumerable EffectivePaths(this IEnumerable paths)
{
foreach (string path in paths)
{
yield return Utils.GetEffectivePath(path);
}
}
public static IEnumerable ToDepotSpecs(this IEnumerable assetPaths)
{
string depotRoot = Utils.RemoveDirectoryWildcards(Config.DepotProjectRoot) + "/";
foreach (string asset in assetPaths)
{
yield return FileSpec.DepotSpec(depotRoot + asset);
}
}
public static IEnumerable ToLocalSpecs(this IEnumerable assetPaths)
{
string localRoot = Utils.RemoveDirectoryWildcards(Config.LocalProjectRoot) + System.IO.Path.DirectorySeparatorChar;
foreach (string asset in assetPaths)
{
yield return FileSpec.LocalSpec(localRoot + asset);
}
}
///
/// 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(this IEnumerable paths)
{
foreach (string path in paths)
{
if (Utils.IsDirectory(path))
{
var npath = Utils.RemoveDirectoryWildcards(path);
// Get The Folder Contents
var files = Directory.GetFiles(npath, "*.*", SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
yield return file;
}
// Recurse into subdirectories
var dfiles = ExplodeLocalPaths(Directory.GetDirectories(npath));
foreach (string dfile in dfiles)
{
yield return dfile;
}
}
else
{
yield return path;
}
}
yield break;
}
///
/// Takes a list of asset paths.
/// For directories in the path, recurse inside
///
/// list of all paths to examine.
/// list of all "leaf nodes" in the passed in tree.
public static IEnumerable ExplodeAssetPaths(this IEnumerable paths)
{
// Debug.Log("ExplodeAssetPaths " + Logger.StringArrayToString(paths.ToArray()));
foreach (string path in paths.AssetToLocalPaths().ExplodeLocalPaths().LocalToAssetPaths())
{
//Debug.Log("Result Asset: " + path);
yield return path;
}
}
#endregion
#region FileAndMeta Enumerable Extensions
///
/// Create an enumerable of FileSpec from an enumerable of FileAndMeta
///
/// FileAndMeta enumerable
/// FileSpec enumerable
public static IEnumerable ToFileSpecs(this IEnumerable fams)
{
foreach (var fam in fams)
{
if (fam.File != null)
{
yield return fam.File;
}
if (fam.Meta != null)
{
yield return fam.Meta;
}
}
}
#endregion
#region FileMetaData Extensions
public static string ToAssetPath(this FileMetaData fmd)
{
return Utils.ClientPathToAssetPath(fmd.ClientPath.Path);
}
public static IEnumerable ToAssetPaths(this IEnumerable fmds)
{
foreach (FileMetaData fmd in fmds)
{
yield return ToAssetPath(fmd);
}
}
#endregion
#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 existingFam;
if (famDictionary.TryGetValue(assetpath, out existingFam))
{
existingFam.Meta = fspec;
famDictionary[assetpath] = existingFam; // update existing entry
}
else
{
// Add a new entry to the famDictionary
existingFam.Meta = fspec;
existingFam.File = null;
famDictionary[assetpath] = existingFam; // Add new entry to famDictionary
}
}
else
{
string metapath = Utils.MetaFromAsset(path);
FileAndMeta existingFam;
// It's not a meta file, is there a meta as well?
if (famDictionary.TryGetValue(path, out existingFam))
{
existingFam.File = fspec;
famDictionary[path] = existingFam; // 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
{
return famDictionary.Values;
}
}
}
#endregion
#region FileSpec Extensions
///
/// 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;
}
///
/// Return true if a ClientPath exists and is not empty
///
///
/// bool
public static bool HasClientPath(this FileSpec spec)
{
if (spec == null || spec.ClientPath == null || spec.ClientPath.Path == null || spec.ClientPath.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("");
}
#endregion
#region AssetStatus Extensions
///
/// Extension which takes a set of asset paths and makes sure the AssetStatusCache is updated with "Real" values.
/// AssetStatus Updates run SYNCHRONOUSLY
///
/// asset paths to synchronize
/// The same asset paths as originally input
public static IEnumerable InitializeAssetStatuses(this IEnumerable aAssetPaths)
{
List defaults = new List();
foreach (var path in aAssetPaths)
{
if (! AssetStatusCache.GetCachedAssetStatus(path).IsInitialized())
defaults.Add(path);
}
if (defaults.Count > 0)
{
Engine.PerformConnectionOperation(con =>
{
AssetStatusCache.GetAssetMetaData(con, defaults); // Run an fstat command synchronously, when complete it will update entry.
}, false);
}
return aAssetPaths;
}
///
/// Extension which takes a set of asset paths and makes sure that each Uninitialized AssetStatusCache is updated with "Real" values.
/// AssetStatuses are update ASYNCHRONOUSLY
///
/// asset paths to synchronize
/// The same asset paths passed in
public static IEnumerable PrepareAssetStatuses(this IEnumerable aAssetPaths)
{
List defaults = new List();
foreach (var path in aAssetPaths)
{
if (!AssetStatusCache.GetCachedAssetStatus(path).IsInitialized())
defaults.Add(path);
}
if (defaults.Count > 0)
{
AssetStatusCache.GetFstatsFromServer(defaults);
}
return aAssetPaths;
}
///
/// Extension which returns a collection of AssetStatuses from a collection of assetPaths.
///
/// paths of assets to get status
/// Ienumerable of AssetStatuses
public static IEnumerable GetCachedAssetStatuses(this IEnumerable aAssetPaths)
{
foreach (var path in aAssetPaths)
{
yield return AssetStatusCache.GetCachedAssetStatus(path);
}
}
///
/// Extension which returns a collection of FileMetaDatas from a collection of assetPaths.
///
/// paths of assets
/// Ienumerable of FileMetaData
public static IEnumerable GetCachedAssetMetaDatas(this IEnumerable aAssetPaths)
{
foreach (var path in aAssetPaths)
{
yield return AssetStatusCache.GetCachedMetaData(path);
}
}
#endregion
}
}