using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Threading; using Codice.Client.BaseCommands; using Codice.Client.BaseCommands.Config; using Codice.Client.Commands; using Codice.Client.Commands.WkTree; using Codice.LogWrapper; using GluonGui; using PlasticGui; using PlasticGui.WorkspaceWindow; using Unity.PlasticSCM.Editor.AssetUtils; using Unity.PlasticSCM.Editor.UI; using Unity.PlasticSCM.Editor.Views.IncomingChanges; using Unity.PlasticSCM.Editor.Views.PendingChanges; namespace Unity.PlasticSCM.Editor { internal class WorkspaceOperationsMonitor { public interface IDisableAssetsProcessor { void Disable(); } internal WorkspaceOperationsMonitor( IPlasticAPI plasticApi, IDisableAssetsProcessor disableAssetsProcessor, bool isGluonMode) { mPlasticAPI = plasticApi; mDisableAssetsProcessor = disableAssetsProcessor; mIsGluonMode = isGluonMode; } internal void RegisterWindow( WorkspaceWindow workspaceWindow, ViewHost viewHost, NewIncomingChangesUpdater incomingChangesUpdater) { mWorkspaceWindow = workspaceWindow; mViewHost = viewHost; mNewIncomingChangesUpdater = incomingChangesUpdater; } internal void UnRegisterWindow() { mWorkspaceWindow = null; mViewHost = null; mNewIncomingChangesUpdater = null; } internal void RegisterPendingChangesView( PendingChangesTab pendingChangesTab) { mPendingChangesTab = pendingChangesTab; } internal void RegisterIncomingChangesView( IIncomingChangesTab incomingChangesTab) { mIncomingChangesTab = incomingChangesTab; } internal void UnRegisterViews() { mPendingChangesTab = null; mIncomingChangesTab = null; } internal void Start() { mIsRunning = true; Thread thread = new Thread(TaskLoopThread); thread.IsBackground = true; thread.Start(); } internal void Stop() { SetAsFinished(); } internal void AddAssetsProcessorPathToAdd( List paths) { AddPathsToProcess( mAssetsProcessorPathsToAdd, paths, mLock, mResetEvent); } internal void AddAssetsProcessorPathToDelete( string path) { AddPathsToProcess( mAssetsProcessorPathsToDelete, new List { path }, mLock, mResetEvent); } internal void AddAssetsProcessorPathToCheckout( List paths) { AddPathsToProcess( mAssetsProcessorPathsToCheckout, paths, mLock, mResetEvent); } internal void AddAssetsProcessorPathToMove( string srcPath, string dstPath) { AddPathToMoveToProcess( mAssetsProcessorPathsToMove, new PathToMove(srcPath, dstPath), mLock, mResetEvent); } internal void AddPathsToCheckout( List paths) { AddPathsToProcess( mPathsToCheckout, paths, mLock, mResetEvent); } void TaskLoopThread() { while (true) { try { if (!mIsRunning) break; ProcessAssetProcessorOperations( mPlasticAPI, mAssetsProcessorPathsToAdd, mAssetsProcessorPathsToDelete, mAssetsProcessorPathsToCheckout, mAssetsProcessorPathsToMove, mLock, mDisableAssetsProcessor); ProcessCheckoutOperation( mPlasticAPI, mPathsToCheckout, mLock); bool hasAssetProcessorOperations = false; bool hasCheckoutOperations = false; HasPendingOperationsToProcess( mAssetsProcessorPathsToAdd, mAssetsProcessorPathsToDelete, mAssetsProcessorPathsToCheckout, mAssetsProcessorPathsToMove, mPathsToCheckout, mLock, out hasAssetProcessorOperations, out hasCheckoutOperations); if (hasAssetProcessorOperations || hasCheckoutOperations) continue; if (!hasAssetProcessorOperations) EditorDispatcher.Dispatch(AfterAssetProcessorOperation); if (!hasCheckoutOperations) EditorDispatcher.Dispatch(AfterCheckoutOperation); SleepUntilNextWorkload(); } catch (Exception e) { mLog.ErrorFormat( "Error running the tasks loop : {0}", e.Message); mLog.DebugFormat( "Stacktrace: {0}", e.StackTrace); } } } void AfterAssetProcessorOperation() { AutoRefresh.PendingChangesView( mPendingChangesTab); AutoRefresh.IncomingChangesView( mIncomingChangesTab); } void AfterCheckoutOperation() { RefreshAsset.VersionControlCache(); if (mIsGluonMode) { RefreshViewsAfterCheckoutForGluon(mViewHost); return; } if (mNewIncomingChangesUpdater != null) mNewIncomingChangesUpdater.Update(); RefreshViewsAfterCheckoutForDeveloper(mWorkspaceWindow); } void SetAsFinished() { if (!mIsRunning) return; mIsRunning = false; mResetEvent.Set(); } void SleepUntilNextWorkload() { mResetEvent.Reset(); mResetEvent.WaitOne(); } static void ProcessAssetProcessorOperations( IPlasticAPI plasticApi, List assetsProcessorPathsToAdd, List assetsProcessorPathsToDelete, List assetsProcessorPathsToCheckout, List assetsProcessorPathsToMove, object lockObj, IDisableAssetsProcessor disableAssetsProcessor) { try { AssetsProcessorOperations.AddIfNotControlled( plasticApi, ExtractPathsToProcess( assetsProcessorPathsToAdd, lockObj)); AssetsProcessorOperations.DeleteIfControlled( plasticApi, ExtractPathsToProcess( assetsProcessorPathsToDelete, lockObj)); AssetsProcessorOperations.CheckoutIfControlledAndChanged( plasticApi, ExtractPathsToProcess( assetsProcessorPathsToCheckout, lockObj)); AssetsProcessorOperations.MoveIfControlled( plasticApi, ExtractPathsToMoveToProcess( assetsProcessorPathsToMove, lockObj)); } catch (Exception ex) { LogException(ex); disableAssetsProcessor.Disable(); } } static void ProcessCheckoutOperation( IPlasticAPI plasticApi, List pathsToProcess, object lockObj) { List paths = ExtractPathsToProcess( pathsToProcess, lockObj); if (paths.Count == 0) return; plasticApi.Checkout( paths.ToArray(), CheckoutModifiers.ProcessSymlinks); } static void AddPathsToProcess( List pathsToProcess, List paths, object lockObj, ManualResetEvent resetEvent) { lock (lockObj) { pathsToProcess.AddRange(paths); } resetEvent.Set(); } static void AddPathToMoveToProcess( List pathsToProcess, PathToMove path, object lockObj, ManualResetEvent resetEvent) { lock (lockObj) { pathsToProcess.Add(path); } resetEvent.Set(); } static List ExtractPathsToProcess( List pathsToProcess, object lockObj) { List result; lock (lockObj) { result = new List(pathsToProcess); pathsToProcess.Clear(); } return result; } static List ExtractPathsToMoveToProcess( List pathsToProcess, object lockObj) { List result; lock (lockObj) { result = new List(pathsToProcess); pathsToProcess.Clear(); } return result; } static void HasPendingOperationsToProcess( List assetsProcessorPathsToAdd, List assetsProcessorPathsToDelete, List assetsProcessorPathsToCheckout, List assetsProcessorPathsToMove, List pathsToCheckout, object lockObj, out bool hasAssetProcessorOperations, out bool hasCheckoutOperations) { lock (lockObj) { hasAssetProcessorOperations = assetsProcessorPathsToAdd.Count > 0 || assetsProcessorPathsToDelete.Count > 0 || assetsProcessorPathsToCheckout.Count > 0 || assetsProcessorPathsToMove.Count > 0; hasCheckoutOperations = pathsToCheckout.Count > 0; } } static void RefreshViewsAfterCheckoutForDeveloper( IWorkspaceWindow workspaceWindow) { if (workspaceWindow == null) return; workspaceWindow.RefreshView(ViewType.BranchExplorerView); workspaceWindow.RefreshView(ViewType.PendingChangesView); workspaceWindow.RefreshView(ViewType.HistoryView); } static void RefreshViewsAfterCheckoutForGluon( ViewHost viewHost) { if (viewHost == null) return; viewHost.RefreshView(ViewType.WorkspaceExplorerView); viewHost.RefreshView(ViewType.CheckinView); viewHost.RefreshView(ViewType.IncomingChangesView); viewHost.RefreshView(ViewType.SearchView); } static void LogException(Exception ex) { mLog.WarnFormat("Message: {0}", ex.Message); mLog.DebugFormat( "StackTrace:{0}{1}", Environment.NewLine, ex.StackTrace); } static class AssetsProcessorOperations { internal static void AddIfNotControlled( IPlasticAPI plasticApi, List paths) { List fullPaths = new List(); IgnoredFilesFilter ignoredFilter = new IgnoredFilesFilter( GlobalConfig.Instance); foreach (string path in paths) { string fullPath = Path.GetFullPath(path); string fullPathMeta = MetaPath.GetMetaPath(fullPath); if (plasticApi.GetWorkspaceFromPath(fullPath) == null) return; if (plasticApi.GetWorkspaceTreeNode(fullPath) == null && !ignoredFilter.IsIgnored(fullPath)) fullPaths.Add(fullPath); if (File.Exists(fullPathMeta) && plasticApi.GetWorkspaceTreeNode(fullPathMeta) == null && !ignoredFilter.IsIgnored(fullPath)) fullPaths.Add(fullPathMeta); } if (fullPaths.Count == 0) return; IList checkouts; plasticApi.Add( fullPaths.ToArray(), GetDefaultAddOptions(), out checkouts); } internal static void DeleteIfControlled( IPlasticAPI plasticApi, List paths) { foreach (string path in paths) { string fullPath = Path.GetFullPath(path); string fullPathMeta = MetaPath.GetMetaPath(fullPath); if (plasticApi.GetWorkspaceTreeNode(fullPath) != null) { plasticApi.DeleteControlled( fullPath, DeleteModifiers.None); } if (plasticApi.GetWorkspaceTreeNode(fullPathMeta) != null) { plasticApi.DeleteControlled( fullPathMeta, DeleteModifiers.None); } } } internal static void MoveIfControlled( IPlasticAPI plasticApi, List paths) { foreach (PathToMove pathToMove in paths) { string fullSrcPath = Path.GetFullPath(pathToMove.SrcPath); string fullSrcPathMeta = MetaPath.GetMetaPath(fullSrcPath); string fullDstPath = Path.GetFullPath(pathToMove.DstPath); string fullDstPathMeta = MetaPath.GetMetaPath(fullDstPath); if (plasticApi.GetWorkspaceTreeNode(fullSrcPath) != null) { plasticApi.Move( fullSrcPath, fullDstPath, MoveModifiers.None); } if (plasticApi.GetWorkspaceTreeNode(fullSrcPathMeta) != null) { plasticApi.Move( fullSrcPathMeta, fullDstPathMeta, MoveModifiers.None); } } } internal static void CheckoutIfControlledAndChanged( IPlasticAPI plasticApi, List paths) { List fullPaths = new List(); foreach (string path in paths) { string fullPath = Path.GetFullPath(path); string fullPathMeta = MetaPath.GetMetaPath(fullPath); WorkspaceTreeNode node = plasticApi.GetWorkspaceTreeNode(fullPath); WorkspaceTreeNode nodeMeta = plasticApi.GetWorkspaceTreeNode(fullPathMeta); if (node != null && ChangedFileChecker.IsChanged( node.LocalInfo, fullPath, false)) fullPaths.Add(fullPath); if (nodeMeta != null && ChangedFileChecker.IsChanged( nodeMeta.LocalInfo, fullPathMeta, false)) fullPaths.Add(fullPathMeta); } if (fullPaths.Count == 0) return; plasticApi.Checkout( fullPaths.ToArray(), CheckoutModifiers.None); } static AddOptions GetDefaultAddOptions() { AddOptions options = new AddOptions(); options.AddPrivateParents = true; options.NeedCheckPlatformPath = true; return options; } } struct PathToMove { internal readonly string SrcPath; internal readonly string DstPath; internal PathToMove(string srcPath, string dstPath) { SrcPath = srcPath; DstPath = dstPath; } } object mLock = new object(); volatile bool mIsRunning; volatile ManualResetEvent mResetEvent = new ManualResetEvent(false); List mAssetsProcessorPathsToAdd = new List(); List mAssetsProcessorPathsToDelete = new List(); List mAssetsProcessorPathsToCheckout = new List(); List mAssetsProcessorPathsToMove = new List(); List mPathsToCheckout = new List(); PendingChangesTab mPendingChangesTab; IIncomingChangesTab mIncomingChangesTab; NewIncomingChangesUpdater mNewIncomingChangesUpdater; ViewHost mViewHost; IWorkspaceWindow mWorkspaceWindow; readonly bool mIsGluonMode = false; readonly IDisableAssetsProcessor mDisableAssetsProcessor; readonly IPlasticAPI mPlasticAPI; static readonly ILog mLog = LogManager.GetLogger("WorkspaceOperationsMonitor"); } }