Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
GrammarLoader.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Reflection;
6 using Irony.Parsing;
7 using System.IO;
8 using System.Threading;
9 
10 namespace Irony.GrammarExplorer {
11  /// <summary>
12  /// Maintains grammar assemblies, reloads updated files automatically.
13  /// </summary>
14  class GrammarLoader {
15  private TimeSpan _autoRefreshDelay = TimeSpan.FromMilliseconds(500);
16  private Dictionary<string, CachedAssembly> _cachedAssemblies = new Dictionary<string, CachedAssembly>();
17 
18  class CachedAssembly {
19  public long FileSize;
20  public DateTime LastWriteTime;
21  public FileSystemWatcher Watcher;
22  public Assembly Assembly;
23  }
24 
25  public event EventHandler AssemblyUpdated;
26 
27  public GrammarItem SelectedGrammar { get; set; }
28 
30  if (SelectedGrammar == null)
31  return null;
32 
33  var type = SelectedGrammarAssembly.GetType(SelectedGrammar.TypeName, true, true);
34  return Activator.CreateInstance(type) as Parsing.Grammar;
35  }
36 
37  Assembly SelectedGrammarAssembly {
38  get {
39  if (SelectedGrammar == null)
40  return null;
41 
42  // create assembly cache entry as needed
43  var location = SelectedGrammar.Location;
44  if (!_cachedAssemblies.ContainsKey(location)) {
45  var fileInfo = new FileInfo(location);
46  _cachedAssemblies[location] =
47  new CachedAssembly {
48  LastWriteTime = fileInfo.LastWriteTime,
49  FileSize = fileInfo.Length,
50  Assembly = null
51  };
52 
53  // set up file system watcher
54  _cachedAssemblies[location].Watcher = CreateFileWatcher(location);
55  }
56 
57  // get loaded assembly from cache if possible
58  var assembly = _cachedAssemblies[location].Assembly;
59  if (assembly == null) {
60  assembly = LoadAssembly(location);
61  _cachedAssemblies[location].Assembly = assembly;
62  }
63 
64  return assembly;
65  }
66  }
67 
68  private FileSystemWatcher CreateFileWatcher(string location) {
69  var folder = Path.GetDirectoryName(location);
70  var watcher = new FileSystemWatcher(folder);
71  watcher.Filter = Path.GetFileName(location);
72 
73  watcher.Changed += (s, args) => {
74  if (args.ChangeType != WatcherChangeTypes.Changed)
75  return;
76 
77  // check if assembly was changed indeed to work around multiple FileSystemWatcher event firing
78  var cacheEntry = _cachedAssemblies[location];
79  var fileInfo = new FileInfo(location);
80  if (cacheEntry.LastWriteTime == fileInfo.LastWriteTime && cacheEntry.FileSize == fileInfo.Length)
81  return;
82 
83  // clear cached assembly and save last file update time
84  cacheEntry.LastWriteTime = fileInfo.LastWriteTime;
85  cacheEntry.FileSize = fileInfo.Length;
86  cacheEntry.Assembly = null;
87 
88  // delay auto-refresh for safety reasons
89  ThreadPool.QueueUserWorkItem(_ => {
90  Thread.Sleep(_autoRefreshDelay);
91  OnAssemblyUpdated(location);
92  });
93  };
94 
95  watcher.EnableRaisingEvents = true;
96  return watcher;
97  }
98 
99  private void OnAssemblyUpdated(string location) {
100  if (AssemblyUpdated == null || SelectedGrammar == null || SelectedGrammar.Location != location)
101  return;
102  AssemblyUpdated(this, EventArgs.Empty);
103  }
104 
105  Assembly LoadAssembly(string fileName) {
106  // 1. Assembly.Load doesn't block the file
107  // 2. Assembly.Load doesn't check if the assembly is already loaded in the current AppDomain
108  return Assembly.LoadFile(fileName);
109  }
110  }
111 }
Maintains grammar assemblies, reloads updated files automatically.
function s(a)