using System; using System.Collections.Generic; using Codice; using Codice.Client.Commands; using Codice.Client.Commands.WkTree; using Codice.Client.Common; using Codice.Client.Common.Locks; using Codice.Client.Common.Threading; using Codice.Client.Common.WkTree; using Codice.CM.Common; using Codice.CM.Common.Mount; using Codice.Utils; using PlasticGui.WorkspaceWindow; using PlasticGui.WorkspaceWindow.Items.Locks; namespace Unity.PlasticSCM.Editor.AssetsOverlays.Cache { internal class LockStatusCache { internal LockStatusCache( WorkspaceInfo wkInfo, Action repaintProjectWindow, Action repaintInspector) { mWkInfo = wkInfo; mRepaintProjectWindow = repaintProjectWindow; mRepaintInspector = repaintInspector; } internal AssetStatus GetStatus(string fullPath) { LockStatusData lockStatusData = GetLockStatusData(fullPath); if (lockStatusData == null) return AssetStatus.None; return lockStatusData.Status; } internal LockStatusData GetLockStatusData(string fullPath) { lock (mLock) { if (mStatusByPathCache == null) { mStatusByPathCache = BuildPathDictionary.ForPlatform(); mCurrentCancelToken.Cancel(); mCurrentCancelToken = new CancelToken(); AsyncCalculateStatus(mCurrentCancelToken); return null; } LockStatusData result; if (mStatusByPathCache.TryGetValue(fullPath, out result)) return result; return null; } } internal void Clear() { lock (mLock) { mCurrentCancelToken.Cancel(); mStatusByPathCache = null; } } void AsyncCalculateStatus(CancelToken cancelToken) { Dictionary statusByPathCache = null; IThreadWaiter waiter = ThreadWaiter.GetWaiter(50); waiter.Execute( /*threadOperationDelegate*/ delegate { Dictionary> lockCandidates = new Dictionary>(); FillLockCandidates.ForTree(mWkInfo, lockCandidates); if (cancelToken.IsCancelled()) return; Dictionary lockInfoByNode = SearchLocks.GetLocksInfo(mWkInfo, lockCandidates); if (cancelToken.IsCancelled()) return; statusByPathCache = BuildStatusByNodeCache. ForLocks(mWkInfo.ClientPath, lockInfoByNode); }, /*afterOperationDelegate*/ delegate { if (waiter.Exception != null) { ExceptionsHandler.LogException( "LockStatusCache", waiter.Exception); return; } if (cancelToken.IsCancelled()) return; lock (mLock) { mStatusByPathCache = statusByPathCache; } mRepaintProjectWindow(); mRepaintInspector(); }); } static class BuildStatusByNodeCache { internal static Dictionary ForLocks( string wkPath, Dictionary lockInfoByNode) { Dictionary result = BuildPathDictionary.ForPlatform(); LockOwnerNameResolver nameResolver = new LockOwnerNameResolver(); foreach (WorkspaceTreeNode node in lockInfoByNode.Keys) { LockStatusData lockStatusData = BuildLockStatusData( node, lockInfoByNode[node], nameResolver); string nodeWkPath = WorkspacePath.GetWorkspacePathFromCmPath( wkPath, WorkspaceNodeOperations.GetCmPath(node), PathHelper.GetDirectorySeparatorChar(wkPath)); result.Add(nodeWkPath, lockStatusData); } return result; } static LockStatusData BuildLockStatusData( WorkspaceTreeNode node, LockInfo lockInfo, LockOwnerNameResolver nameResolver) { return new LockStatusData( GetAssetStatus(node, lockInfo), nameResolver.GetSeidName(lockInfo.SEIDData), BranchInfoCache.GetProtectedBranchName( node.RepSpec, lockInfo.HolderBranchId)); } static AssetStatus GetAssetStatus( WorkspaceTreeNode node, LockInfo lockInfo) { if (lockInfo.Status == LockInfo.LockStatus.Retained) return AssetStatus.Retained; return CheckWorkspaceTreeNodeStatus.IsCheckedOut(node) ? AssetStatus.Locked : AssetStatus.LockedRemote; } } CancelToken mCurrentCancelToken = new CancelToken(); Dictionary mStatusByPathCache; readonly Action mRepaintInspector; readonly Action mRepaintProjectWindow; readonly WorkspaceInfo mWkInfo; static object mLock = new object(); } }