WorkspaceOperationsMonitor.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Threading;
  6. using Codice.Client.BaseCommands;
  7. using Codice.Client.BaseCommands.Config;
  8. using Codice.Client.Commands;
  9. using Codice.Client.Commands.WkTree;
  10. using Codice.LogWrapper;
  11. using GluonGui;
  12. using PlasticGui;
  13. using PlasticGui.WorkspaceWindow;
  14. using Unity.PlasticSCM.Editor.AssetUtils;
  15. using Unity.PlasticSCM.Editor.UI;
  16. using Unity.PlasticSCM.Editor.Views.IncomingChanges;
  17. using Unity.PlasticSCM.Editor.Views.PendingChanges;
  18. namespace Unity.PlasticSCM.Editor
  19. {
  20. internal class WorkspaceOperationsMonitor
  21. {
  22. public interface IDisableAssetsProcessor
  23. {
  24. void Disable();
  25. }
  26. internal WorkspaceOperationsMonitor(
  27. IPlasticAPI plasticApi,
  28. IDisableAssetsProcessor disableAssetsProcessor,
  29. bool isGluonMode)
  30. {
  31. mPlasticAPI = plasticApi;
  32. mDisableAssetsProcessor = disableAssetsProcessor;
  33. mIsGluonMode = isGluonMode;
  34. }
  35. internal void RegisterWindow(
  36. WorkspaceWindow workspaceWindow,
  37. ViewHost viewHost,
  38. NewIncomingChangesUpdater incomingChangesUpdater)
  39. {
  40. mWorkspaceWindow = workspaceWindow;
  41. mViewHost = viewHost;
  42. mNewIncomingChangesUpdater = incomingChangesUpdater;
  43. }
  44. internal void UnRegisterWindow()
  45. {
  46. mWorkspaceWindow = null;
  47. mViewHost = null;
  48. mNewIncomingChangesUpdater = null;
  49. }
  50. internal void RegisterPendingChangesView(
  51. PendingChangesTab pendingChangesTab)
  52. {
  53. mPendingChangesTab = pendingChangesTab;
  54. }
  55. internal void RegisterIncomingChangesView(
  56. IIncomingChangesTab incomingChangesTab)
  57. {
  58. mIncomingChangesTab = incomingChangesTab;
  59. }
  60. internal void UnRegisterViews()
  61. {
  62. mPendingChangesTab = null;
  63. mIncomingChangesTab = null;
  64. }
  65. internal void Start()
  66. {
  67. mIsRunning = true;
  68. Thread thread = new Thread(TaskLoopThread);
  69. thread.IsBackground = true;
  70. thread.Start();
  71. }
  72. internal void Stop()
  73. {
  74. SetAsFinished();
  75. }
  76. internal void AddAssetsProcessorPathToAdd(
  77. List<string> paths)
  78. {
  79. AddPathsToProcess(
  80. mAssetsProcessorPathsToAdd, paths,
  81. mLock, mResetEvent);
  82. }
  83. internal void AddAssetsProcessorPathToDelete(
  84. string path)
  85. {
  86. AddPathsToProcess(
  87. mAssetsProcessorPathsToDelete,
  88. new List<string> { path },
  89. mLock, mResetEvent);
  90. }
  91. internal void AddAssetsProcessorPathToCheckout(
  92. List<string> paths)
  93. {
  94. AddPathsToProcess(
  95. mAssetsProcessorPathsToCheckout, paths,
  96. mLock, mResetEvent);
  97. }
  98. internal void AddAssetsProcessorPathToMove(
  99. string srcPath,
  100. string dstPath)
  101. {
  102. AddPathToMoveToProcess(
  103. mAssetsProcessorPathsToMove,
  104. new PathToMove(srcPath, dstPath),
  105. mLock, mResetEvent);
  106. }
  107. internal void AddPathsToCheckout(
  108. List<string> paths)
  109. {
  110. AddPathsToProcess(
  111. mPathsToCheckout, paths,
  112. mLock, mResetEvent);
  113. }
  114. void TaskLoopThread()
  115. {
  116. while (true)
  117. {
  118. try
  119. {
  120. if (!mIsRunning)
  121. break;
  122. ProcessAssetProcessorOperations(
  123. mPlasticAPI,
  124. mAssetsProcessorPathsToAdd,
  125. mAssetsProcessorPathsToDelete,
  126. mAssetsProcessorPathsToCheckout,
  127. mAssetsProcessorPathsToMove,
  128. mLock,
  129. mDisableAssetsProcessor);
  130. ProcessCheckoutOperation(
  131. mPlasticAPI,
  132. mPathsToCheckout,
  133. mLock);
  134. bool hasAssetProcessorOperations = false;
  135. bool hasCheckoutOperations = false;
  136. HasPendingOperationsToProcess(
  137. mAssetsProcessorPathsToAdd,
  138. mAssetsProcessorPathsToDelete,
  139. mAssetsProcessorPathsToCheckout,
  140. mAssetsProcessorPathsToMove,
  141. mPathsToCheckout,
  142. mLock,
  143. out hasAssetProcessorOperations,
  144. out hasCheckoutOperations);
  145. if (hasAssetProcessorOperations ||
  146. hasCheckoutOperations)
  147. continue;
  148. if (!hasAssetProcessorOperations)
  149. EditorDispatcher.Dispatch(AfterAssetProcessorOperation);
  150. if (!hasCheckoutOperations)
  151. EditorDispatcher.Dispatch(AfterCheckoutOperation);
  152. SleepUntilNextWorkload();
  153. }
  154. catch (Exception e)
  155. {
  156. mLog.ErrorFormat(
  157. "Error running the tasks loop : {0}", e.Message);
  158. mLog.DebugFormat(
  159. "Stacktrace: {0}", e.StackTrace);
  160. }
  161. }
  162. }
  163. void AfterAssetProcessorOperation()
  164. {
  165. AutoRefresh.PendingChangesView(
  166. mPendingChangesTab);
  167. AutoRefresh.IncomingChangesView(
  168. mIncomingChangesTab);
  169. }
  170. void AfterCheckoutOperation()
  171. {
  172. RefreshAsset.VersionControlCache();
  173. if (mIsGluonMode)
  174. {
  175. RefreshViewsAfterCheckoutForGluon(mViewHost);
  176. return;
  177. }
  178. if (mNewIncomingChangesUpdater != null)
  179. mNewIncomingChangesUpdater.Update();
  180. RefreshViewsAfterCheckoutForDeveloper(mWorkspaceWindow);
  181. }
  182. void SetAsFinished()
  183. {
  184. if (!mIsRunning)
  185. return;
  186. mIsRunning = false;
  187. mResetEvent.Set();
  188. }
  189. void SleepUntilNextWorkload()
  190. {
  191. mResetEvent.Reset();
  192. mResetEvent.WaitOne();
  193. }
  194. static void ProcessAssetProcessorOperations(
  195. IPlasticAPI plasticApi,
  196. List<string> assetsProcessorPathsToAdd,
  197. List<string> assetsProcessorPathsToDelete,
  198. List<string> assetsProcessorPathsToCheckout,
  199. List<PathToMove> assetsProcessorPathsToMove,
  200. object lockObj,
  201. IDisableAssetsProcessor disableAssetsProcessor)
  202. {
  203. try
  204. {
  205. AssetsProcessorOperations.AddIfNotControlled(
  206. plasticApi, ExtractPathsToProcess(
  207. assetsProcessorPathsToAdd, lockObj));
  208. AssetsProcessorOperations.DeleteIfControlled(
  209. plasticApi, ExtractPathsToProcess(
  210. assetsProcessorPathsToDelete, lockObj));
  211. AssetsProcessorOperations.CheckoutIfControlledAndChanged(
  212. plasticApi, ExtractPathsToProcess(
  213. assetsProcessorPathsToCheckout, lockObj));
  214. AssetsProcessorOperations.MoveIfControlled(
  215. plasticApi, ExtractPathsToMoveToProcess(
  216. assetsProcessorPathsToMove, lockObj));
  217. }
  218. catch (Exception ex)
  219. {
  220. LogException(ex);
  221. disableAssetsProcessor.Disable();
  222. }
  223. }
  224. static void ProcessCheckoutOperation(
  225. IPlasticAPI plasticApi,
  226. List<string> pathsToProcess,
  227. object lockObj)
  228. {
  229. List<string> paths = ExtractPathsToProcess(
  230. pathsToProcess, lockObj);
  231. if (paths.Count == 0)
  232. return;
  233. plasticApi.Checkout(
  234. paths.ToArray(),
  235. CheckoutModifiers.ProcessSymlinks);
  236. }
  237. static void AddPathsToProcess(
  238. List<string> pathsToProcess,
  239. List<string> paths,
  240. object lockObj,
  241. ManualResetEvent resetEvent)
  242. {
  243. lock (lockObj)
  244. {
  245. pathsToProcess.AddRange(paths);
  246. }
  247. resetEvent.Set();
  248. }
  249. static void AddPathToMoveToProcess(
  250. List<PathToMove> pathsToProcess,
  251. PathToMove path,
  252. object lockObj,
  253. ManualResetEvent resetEvent)
  254. {
  255. lock (lockObj)
  256. {
  257. pathsToProcess.Add(path);
  258. }
  259. resetEvent.Set();
  260. }
  261. static List<string> ExtractPathsToProcess(
  262. List<string> pathsToProcess,
  263. object lockObj)
  264. {
  265. List<string> result;
  266. lock (lockObj)
  267. {
  268. result = new List<string>(pathsToProcess);
  269. pathsToProcess.Clear();
  270. }
  271. return result;
  272. }
  273. static List<PathToMove> ExtractPathsToMoveToProcess(
  274. List<PathToMove> pathsToProcess,
  275. object lockObj)
  276. {
  277. List<PathToMove> result;
  278. lock (lockObj)
  279. {
  280. result = new List<PathToMove>(pathsToProcess);
  281. pathsToProcess.Clear();
  282. }
  283. return result;
  284. }
  285. static void HasPendingOperationsToProcess(
  286. List<string> assetsProcessorPathsToAdd,
  287. List<string> assetsProcessorPathsToDelete,
  288. List<string> assetsProcessorPathsToCheckout,
  289. List<PathToMove> assetsProcessorPathsToMove,
  290. List<string> pathsToCheckout,
  291. object lockObj,
  292. out bool hasAssetProcessorOperations,
  293. out bool hasCheckoutOperations)
  294. {
  295. lock (lockObj)
  296. {
  297. hasAssetProcessorOperations =
  298. assetsProcessorPathsToAdd.Count > 0 ||
  299. assetsProcessorPathsToDelete.Count > 0 ||
  300. assetsProcessorPathsToCheckout.Count > 0 ||
  301. assetsProcessorPathsToMove.Count > 0;
  302. hasCheckoutOperations =
  303. pathsToCheckout.Count > 0;
  304. }
  305. }
  306. static void RefreshViewsAfterCheckoutForDeveloper(
  307. IWorkspaceWindow workspaceWindow)
  308. {
  309. if (workspaceWindow == null)
  310. return;
  311. workspaceWindow.RefreshView(ViewType.BranchExplorerView);
  312. workspaceWindow.RefreshView(ViewType.PendingChangesView);
  313. workspaceWindow.RefreshView(ViewType.HistoryView);
  314. }
  315. static void RefreshViewsAfterCheckoutForGluon(
  316. ViewHost viewHost)
  317. {
  318. if (viewHost == null)
  319. return;
  320. viewHost.RefreshView(ViewType.WorkspaceExplorerView);
  321. viewHost.RefreshView(ViewType.CheckinView);
  322. viewHost.RefreshView(ViewType.IncomingChangesView);
  323. viewHost.RefreshView(ViewType.SearchView);
  324. }
  325. static void LogException(Exception ex)
  326. {
  327. mLog.WarnFormat("Message: {0}", ex.Message);
  328. mLog.DebugFormat(
  329. "StackTrace:{0}{1}",
  330. Environment.NewLine, ex.StackTrace);
  331. }
  332. static class AssetsProcessorOperations
  333. {
  334. internal static void AddIfNotControlled(
  335. IPlasticAPI plasticApi,
  336. List<string> paths)
  337. {
  338. List<string> fullPaths = new List<string>();
  339. IgnoredFilesFilter ignoredFilter = new IgnoredFilesFilter(
  340. GlobalConfig.Instance);
  341. foreach (string path in paths)
  342. {
  343. string fullPath = Path.GetFullPath(path);
  344. string fullPathMeta = MetaPath.GetMetaPath(fullPath);
  345. if (plasticApi.GetWorkspaceFromPath(fullPath) == null)
  346. return;
  347. if (plasticApi.GetWorkspaceTreeNode(fullPath) == null &&
  348. !ignoredFilter.IsIgnored(fullPath))
  349. fullPaths.Add(fullPath);
  350. if (File.Exists(fullPathMeta) &&
  351. plasticApi.GetWorkspaceTreeNode(fullPathMeta) == null &&
  352. !ignoredFilter.IsIgnored(fullPath))
  353. fullPaths.Add(fullPathMeta);
  354. }
  355. if (fullPaths.Count == 0)
  356. return;
  357. IList checkouts;
  358. plasticApi.Add(
  359. fullPaths.ToArray(),
  360. GetDefaultAddOptions(),
  361. out checkouts);
  362. }
  363. internal static void DeleteIfControlled(
  364. IPlasticAPI plasticApi,
  365. List<string> paths)
  366. {
  367. foreach (string path in paths)
  368. {
  369. string fullPath = Path.GetFullPath(path);
  370. string fullPathMeta = MetaPath.GetMetaPath(fullPath);
  371. if (plasticApi.GetWorkspaceTreeNode(fullPath) != null)
  372. {
  373. plasticApi.DeleteControlled(
  374. fullPath, DeleteModifiers.None);
  375. }
  376. if (plasticApi.GetWorkspaceTreeNode(fullPathMeta) != null)
  377. {
  378. plasticApi.DeleteControlled(
  379. fullPathMeta, DeleteModifiers.None);
  380. }
  381. }
  382. }
  383. internal static void MoveIfControlled(
  384. IPlasticAPI plasticApi,
  385. List<PathToMove> paths)
  386. {
  387. foreach (PathToMove pathToMove in paths)
  388. {
  389. string fullSrcPath = Path.GetFullPath(pathToMove.SrcPath);
  390. string fullSrcPathMeta = MetaPath.GetMetaPath(fullSrcPath);
  391. string fullDstPath = Path.GetFullPath(pathToMove.DstPath);
  392. string fullDstPathMeta = MetaPath.GetMetaPath(fullDstPath);
  393. if (plasticApi.GetWorkspaceTreeNode(fullSrcPath) != null)
  394. {
  395. plasticApi.Move(
  396. fullSrcPath, fullDstPath,
  397. MoveModifiers.None);
  398. }
  399. if (plasticApi.GetWorkspaceTreeNode(fullSrcPathMeta) != null)
  400. {
  401. plasticApi.Move(
  402. fullSrcPathMeta, fullDstPathMeta,
  403. MoveModifiers.None);
  404. }
  405. }
  406. }
  407. internal static void CheckoutIfControlledAndChanged(
  408. IPlasticAPI plasticApi,
  409. List<string> paths)
  410. {
  411. List<string> fullPaths = new List<string>();
  412. foreach (string path in paths)
  413. {
  414. string fullPath = Path.GetFullPath(path);
  415. string fullPathMeta = MetaPath.GetMetaPath(fullPath);
  416. WorkspaceTreeNode node =
  417. plasticApi.GetWorkspaceTreeNode(fullPath);
  418. WorkspaceTreeNode nodeMeta =
  419. plasticApi.GetWorkspaceTreeNode(fullPathMeta);
  420. if (node != null && ChangedFileChecker.IsChanged(
  421. node.LocalInfo, fullPath, false))
  422. fullPaths.Add(fullPath);
  423. if (nodeMeta != null && ChangedFileChecker.IsChanged(
  424. nodeMeta.LocalInfo, fullPathMeta, false))
  425. fullPaths.Add(fullPathMeta);
  426. }
  427. if (fullPaths.Count == 0)
  428. return;
  429. plasticApi.Checkout(
  430. fullPaths.ToArray(),
  431. CheckoutModifiers.None);
  432. }
  433. static AddOptions GetDefaultAddOptions()
  434. {
  435. AddOptions options = new AddOptions();
  436. options.AddPrivateParents = true;
  437. options.NeedCheckPlatformPath = true;
  438. return options;
  439. }
  440. }
  441. struct PathToMove
  442. {
  443. internal readonly string SrcPath;
  444. internal readonly string DstPath;
  445. internal PathToMove(string srcPath, string dstPath)
  446. {
  447. SrcPath = srcPath;
  448. DstPath = dstPath;
  449. }
  450. }
  451. object mLock = new object();
  452. volatile bool mIsRunning;
  453. volatile ManualResetEvent mResetEvent = new ManualResetEvent(false);
  454. List<string> mAssetsProcessorPathsToAdd = new List<string>();
  455. List<string> mAssetsProcessorPathsToDelete = new List<string>();
  456. List<string> mAssetsProcessorPathsToCheckout = new List<string>();
  457. List<PathToMove> mAssetsProcessorPathsToMove = new List<PathToMove>();
  458. List<string> mPathsToCheckout = new List<string>();
  459. PendingChangesTab mPendingChangesTab;
  460. IIncomingChangesTab mIncomingChangesTab;
  461. NewIncomingChangesUpdater mNewIncomingChangesUpdater;
  462. ViewHost mViewHost;
  463. IWorkspaceWindow mWorkspaceWindow;
  464. readonly bool mIsGluonMode = false;
  465. readonly IDisableAssetsProcessor mDisableAssetsProcessor;
  466. readonly IPlasticAPI mPlasticAPI;
  467. static readonly ILog mLog = LogManager.GetLogger("WorkspaceOperationsMonitor");
  468. }
  469. }