P4Connect.AssetStatusCache.cs #1

  • //
  • guest/
  • anis_sg/
  • perforce_software/
  • p4connect/
  • src/
  • P4Connect/
  • P4Connect/
  • P4Connect.AssetStatusCache.cs
  • View
  • Commits
  • Open Download .zip Download (9 KB)
using UnityEditor;
using UnityEngine;
using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Perforce.P4;
using log4net;

namespace P4Connect
{
	/// <summary>
	/// Stores and periodically updates the Perforce status of all the assets
	/// </summary>
	public class AssetStatusCache
	{
        private static readonly ILog log = LogManager.GetLogger(typeof(AssetStatusCache));

		/// <summary>
		/// Simple asset status struct
		/// Stores the file state (checked out, modified, etc...) and revision state
		/// </summary>
		public struct AssetStatus
		{
			public DateTime TimeStamp;
			public FileState LocalState;
			public FileState OtherState;
			public DepotState DepotState;
			public RevisionState RevisionState;
			public ResolvedState ResolvedState;
			public StorageType StorageType;
			public LockState LockState;
		}

		/// <summary>
		/// This delegate is triggered when the status of an asset changes
		/// </summary>
		public delegate void OnAssetStatusChangedDelegate(PerforceConnection aConnection);
		public static event OnAssetStatusChangedDelegate OnAssetStatusChanged
		{
			add
			{
				if (_OnAssetStatusChanged == null)
				{
					// We had no reason to get updated before, so do it now
					Engine.OnOperationPerformed += OnEngineOperationPerformed;
				}
				_OnAssetStatusChanged += value;
			}
			remove
			{
				_OnAssetStatusChanged -= value;
				if (_OnAssetStatusChanged == null)
				{
					// We no longer have any reason to be updated
					Engine.OnOperationPerformed -= OnEngineOperationPerformed;
				}
			}
		}

		// The internal event that is actually registered with
		static event OnAssetStatusChangedDelegate _OnAssetStatusChanged;

		// The cache
		static Dictionary<string, AssetStatus> _Statuses;

		public static void Initialize()
		{
			_Statuses = new Dictionary<string, AssetStatusCache.AssetStatus>();
		}

		/// <summary>
		/// Generic method that fetches the cached asset status (checked out, out of date, etc...)
		/// </summary>
		public static AssetStatus GetAssetStatus(string aAssetPath)
		{
			AssetStatus ret = new AssetStatus();
			if (_Statuses != null)
			{
				_Statuses.TryGetValue(GetEffectivePath(aAssetPath), out ret);
			}
			return ret;
		}

		/// <summary>
		/// Generic method that fetches the cached asset status (checked out, out of date, etc...)
		/// Faster on multiple files because it batches the P4 requests
		/// </summary>
		public static void GetAssetData(PerforceConnection aConnection, List<string> aAssetPaths, List<AssetStatus> aOutStatuses)
		{
            if (! Config.ValidConfiguration)
                return;

			DateTime startTimestamp = DateTime.Now;
			List<FileSpec> localFiles = GetFileSpecs(aAssetPaths);
			GetAssetData(aConnection, localFiles, aOutStatuses);
			if (aAssetPaths.Count == aOutStatuses.Count)
			{
				for (int i = 0; i < aAssetPaths.Count; ++i)
				{
					_Statuses[GetEffectivePath(aAssetPaths[i])] = aOutStatuses[i];
				}
			}

			if (Config.DisplayP4Timings)
			{
				double deltaInnerTime = (DateTime.Now - startTimestamp).TotalMilliseconds;
				aConnection.AppendTimingInfo("GetAssetData " + deltaInnerTime.ToString() + " ms for " + aAssetPaths.Count + " files (" + aOutStatuses.Count + " retrieved)");
			}
		}

		/// <summary>
		/// Force P4Connect to update the status of a file in its status cache
		/// </summary>
        /// <param name="aAssetPaths">List of Linux style Project Relative Paths</param>
		public static void UpdateAssetData(List<string> aAssetPaths)
		{
            if (! Config.ValidConfiguration)
                return;

           // Debug.Log("paths: " + Logger.StringArrayToString(aAssetPaths.ToArray()));
			List<FileSpec> localFiles = GetFileSpecs(aAssetPaths);
			List<AssetStatus> statuses = new List<AssetStatus>();
			Engine.PerformConnectionOperation(con =>
				{
					GetAssetData(con, localFiles, statuses);
				});
			if (statuses.Count == aAssetPaths.Count)
			{
				for (int i = 0; i < aAssetPaths.Count; ++i)
				{
					_Statuses[GetEffectivePath(aAssetPaths[i])] = statuses[i];
				}
			}
		}

		/// <summary>
		/// Given the meta data, figures out the P4Connect icon representation for the file
		/// There is only a subset of all the metadata that P4Connect actually displays
		/// </summary>
		static AssetStatus CreateAssetStatus(FileMetaData aMeta)
		{
			AssetStatus retData = new AssetStatus();

			// Fill out the struct
			switch (aMeta.Type.BaseType)
			{
				case BaseFileType.Text:
					retData.StorageType = StorageType.Text;
					break;
				case BaseFileType.Binary:
					retData.StorageType = StorageType.Binary;
					break;
				default:
					retData.StorageType = StorageType.Other;
					break;
			}

			retData.LocalState = Engine.ParseFileAction(aMeta.Action);
			if (aMeta.OtherActions != null)
			{
				retData.OtherState = Engine.ParseFileAction(aMeta.OtherActions[0]);
			}
			if (aMeta.HaveRev >= aMeta.HeadRev)
				retData.RevisionState = RevisionState.HasLatest;
			else
				retData.RevisionState = RevisionState.OutOfDate;
			if (aMeta.Unresolved)
				retData.ResolvedState = ResolvedState.NeedsResolve;
			else
				retData.ResolvedState = ResolvedState.None;
			if (aMeta.HeadAction == FileAction.MoveDelete || aMeta.HeadAction == FileAction.Delete)
				retData.DepotState = DepotState.Deleted;
			else
				retData.DepotState = DepotState.None;
			retData.TimeStamp = DateTime.Now;

			retData.LockState = Engine.GetLockState(aMeta);
			return retData;
		}

		/// <summary>
		/// This functions gets called when P4Connect does *something* to files
		/// In this case we update the files status
		/// </summary>
		static void OnEngineOperationPerformed(PerforceConnection aConnection, List<FileAndMeta> aFilesAndMetas)
		{
			if (aFilesAndMetas.Count > 0)
			{
				List<FileSpec> allSpecs = new List<FileSpec>();
				foreach (var fileAndMeta in aFilesAndMetas)
				{
					if (fileAndMeta.File != null)
					{
						allSpecs.Add(fileAndMeta.File);
					}
					if (fileAndMeta.Meta != null)
					{
						allSpecs.Add(fileAndMeta.Meta);
					}
				}
				List<AssetStatus> allStatuses = new List<AssetStatus>();
				GetAssetData(aConnection, allSpecs, allStatuses);

				// Cache the returned values
				for (int i = 0; i < allSpecs.Count; ++i)
				{
					string path = null;
					if (allSpecs[i].LocalPath != null)
						path = Utils.LocalPathToAssetPath(allSpecs[i].LocalPath.Path);
					else if (allSpecs[i].ClientPath != null)
						path = Utils.LocalPathToAssetPath(Utils.ClientPathToLocalPath(allSpecs[i].ClientPath.Path));
					else if (allSpecs[i].DepotPath != null)
						path = Utils.LocalPathToAssetPath(Utils.DepotPathToLocalPath(aConnection, allSpecs[i].DepotPath));
#if DEBUG
                    log.Debug("_Statuses[" + Logger.ToStringNullSafe(path) + "] = " + allStatuses[i].LocalState.ToString());
#endif
					if (path != null)
					{
                        _Statuses[GetEffectivePath(path)] = allStatuses[i];
					}
				}

				if (_OnAssetStatusChanged != null)
				{
					_OnAssetStatusChanged(aConnection);
				}
			}
		}

		static void GetAssetData(PerforceConnection aConnection, List<FileSpec> aFiles, List<AssetStatus> aOutStatuses)
		{
			DateTime startTimestamp = DateTime.Now;
			if (aFiles.Count > 0)
			{
				Revision invalid = new Revision(-1);
				List<FileSpec> validSpecs = new List<FileSpec>();
				for (int i = 0; i < aFiles.Count; ++i)
				{
					FileSpec spec = aFiles[i];
					if (!Version.Equals(spec.Version, invalid))
					{
						validSpecs.Add(spec);
					}
				}

				if (validSpecs.Count != 0)
				{
					IList<FileMetaData> allMetas = Engine.GetFileMetaData(aConnection, validSpecs, null);
					List<FileMetaData> matchingMetas = new List<FileMetaData>();
					DateTime matchingMetaStartTime = DateTime.Now;
					Utils.GetMatchingMetaData(aFiles, allMetas, matchingMetas);
					if (Config.DisplayP4Timings)
					{
						double deltaInnerTime = (DateTime.Now - matchingMetaStartTime).TotalMilliseconds;
						aConnection.AppendTimingInfo("GetMatchingMetaData " + deltaInnerTime.ToString() + " ms");
					}

					for (int i = 0; i < aFiles.Count; ++i)
					{
						if (matchingMetas[i] != null)
						{
							aOutStatuses.Add(CreateAssetStatus(matchingMetas[i]));
						}
						else
						{
							aOutStatuses.Add(new AssetStatus());
						}
					}
				}
				else
				{
					for (int i = 0; i < aFiles.Count; ++i)
					{
						aOutStatuses.Add(new AssetStatus());
					}
				}
			}
			if (Config.DisplayP4Timings)
			{
				double deltaInnerTime = (DateTime.Now - startTimestamp).TotalMilliseconds;
				aConnection.AppendTimingInfo("GetAssetData " + deltaInnerTime.ToString() + " ms");
			}
		}

		/// <summary>
		/// Returns a proper list of file spec given a list of paths, accounting for directories
		/// </summary>
		static List<FileSpec> GetFileSpecs(List<string> aAssetPaths)
		{
			List<FileSpec> localFiles = new List<FileSpec>();
			foreach (var path in aAssetPaths)
			{
				localFiles.Add(FileSpec.LocalSpec(Utils.AssetPathToLocalPath(GetEffectivePath(path))));
			}
			return localFiles;
		}

		/// <summary>
		/// Returns the path to use when printing the name, or looking up status info
		/// </summary>
		static string GetEffectivePath(string aAssetPath)
		{
			if (Utils.IsDirectory(aAssetPath))
			{
				return Utils.MetaFromAsset(aAssetPath);
			}
			else
			{
				return aAssetPath;
			}
		}
	}
}
# Change User Description Committed
#1 12954 anis_sg Populate -o //guest/perforce_software/p4connect/...
//guest/anis_sg/perforce_software/p4connect/....
//guest/perforce_software/p4connect/src/P4Connect/P4Connect/P4Connect.AssetStatusCache.cs
#4 12945 Norman Morse Changes from internal main  GA.1
#3 12565 Norman Morse Integrated from Dev Branch
Made ChangeManager into Static Class.
Improved close window behavior for when connection is invalid
Fixed localOpenFiles not updating on submit
#2 12553 Norman Morse integrate from internal main
Build fixes for EC.
Major changes to Configuration and re-initialization code.
Bug fixes
#1 10940 Norman Morse Inital Workshop release of P4Connect.
Released under BSD-2 license