/ScriptEngine/ScriptManager.cs

https://bitbucket.org/masakimuto/masalibs · C# · 358 lines · 239 code · 34 blank · 85 comment · 11 complexity · a9a8e38edfbd0c68b5ce4f8871546c8b MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.IO;
  6. using System.Diagnostics;
  7. using System.Reflection;
  8. namespace Masa.ScriptEngine
  9. {
  10. using TypeScriptDictionary = Dictionary<string, ScriptDataBase>;
  11. /// <summary>
  12. /// ScriptDataをさらにラップしたクラス。エラーログ出力機能付き
  13. /// </summary>
  14. public class ScriptManager
  15. {
  16. protected Dictionary<Type, TypeScriptDictionary> items;
  17. public Func<string, string> PathToKey;
  18. //List<CompiledExpressionTree> CompiledTrees;
  19. Dictionary<string, System.Reflection.Assembly> assemblyDict;
  20. public string ErrorLogFileName
  21. {
  22. get;
  23. set;
  24. }
  25. PackedScriptManager PackedData;
  26. bool UsePackedData;
  27. string rootDirectory;
  28. /// <summary>
  29. /// ファイルシステムから直接スクリプトを読むときのroot
  30. /// </summary>
  31. public string RootDirectory
  32. {
  33. get { return rootDirectory; }
  34. set
  35. {
  36. rootDirectory = Path.Combine(value, "");
  37. }
  38. }
  39. string FullRoot
  40. {
  41. get { return Path.GetFullPath(rootDirectory); }
  42. }
  43. /// <summary>
  44. /// 非Packファイルからスクリプトを読む時の文字列変換関数
  45. /// </summary>
  46. public Func<string, string> CodeMapper { get; set; }
  47. Dictionary<string, string> HeaderDictionary;
  48. /// <summary>
  49. /// Rootと結合したパスを返す
  50. /// </summary>
  51. /// <param name="path"></param>
  52. /// <returns></returns>
  53. string GetScriptDirectory(string path)
  54. {
  55. return Path.Combine(RootDirectory, path);
  56. }
  57. public ScriptManager()
  58. {
  59. CodeMapper = s => s;
  60. items = new Dictionary<Type, TypeScriptDictionary>();
  61. //CompiledTrees = new List<CompiledExpressionTree>();
  62. assemblyDict = new Dictionary<string, System.Reflection.Assembly>();
  63. PathToKey = s => Path.GetFileNameWithoutExtension(s);
  64. ErrorLogFileName = "log.txt";
  65. RootDirectory = "";
  66. }
  67. public ScriptManager(string rootDirectory)
  68. : this()
  69. {
  70. RootDirectory = rootDirectory;
  71. }
  72. public void LoadHeaders(string directory)
  73. {
  74. if (UsePackedData)
  75. {
  76. HeaderDictionary = PackedData.Files
  77. .Where(g => Path.GetDirectoryName(g.Key) == directory)
  78. .ToDictionary(g => this.PathToKey(g.Key), g => g.Value);
  79. }
  80. else
  81. {
  82. HeaderDictionary = this.EnumrateDirectoryScript(directory)
  83. .ToDictionary(x => Path.GetFileNameWithoutExtension(x), x => File.ReadAllText(RootDirectory + x));
  84. }
  85. }
  86. public void LoadPackedScript(string fileName)
  87. {
  88. PackedData = new PackedScriptManager(fileName);
  89. UsePackedData = true;
  90. }
  91. /// <summary>
  92. /// スクリプト読み込み。失敗するとエラーログに記録して、その例外を投げる
  93. /// </summary>
  94. /// <param name="fileName">スクリプトファイル名</param>
  95. /// <param name="target">対象の型</param>
  96. /// <param name="table"></param>
  97. /// <param name="labels"></param>
  98. public void Load(string fileName, Type target, string[] table = null, string[] labels = null)
  99. {
  100. ScriptData data;
  101. string key = PathToKey(fileName);
  102. try
  103. {
  104. if (UsePackedData)
  105. {
  106. data = new PackedScriptData(fileName, PackedData.Files[fileName], target, table, labels)
  107. {
  108. HeaderDictionary = this.HeaderDictionary
  109. };
  110. }
  111. else
  112. {
  113. string p = Path.Combine(RootDirectory, fileName);
  114. p = RootDirectory + fileName;
  115. data = new FileScriptData(p, target, CodeMapper, table, labels)
  116. {
  117. HeaderDictionary = this.HeaderDictionary
  118. };
  119. }
  120. data.Load();
  121. }
  122. catch (Exception e)
  123. {
  124. WriteLog(e.Message);
  125. throw;
  126. }
  127. //if (items.ContainsKey(key))
  128. //{
  129. // string msg = fileName + ":同じKeyのスクリプトがすでに読み込まれている";
  130. // WriteLog(msg);
  131. // //throw new Exception(msg);
  132. //}
  133. TypeScriptDictionary dict;
  134. if (!items.TryGetValue(target, out dict))
  135. {
  136. dict = new TypeScriptDictionary();
  137. items[target] = dict;
  138. }
  139. dict[key] = data;
  140. //items[target][key] = data;
  141. }
  142. /// <summary>
  143. /// root\dirのディレクトリ(当該ディレクトリひとつだけ)が含む*.mssファイルを全て読み込む
  144. /// </summary>
  145. /// <param name="dir"></param>
  146. /// <param name="target"></param>
  147. /// <param name="table"></param>
  148. /// <param name="labels"></param>
  149. public void LoadFromDirectory(string dir, Type target, string[] table = null, string[] labels = null)
  150. {
  151. IEnumerable<string> src = EnumrateDirectoryScript(dir);
  152. dir = dir.ToLower();
  153. //if (UsePackedData)
  154. //{
  155. // var d = dir.Split('\\');
  156. // src = PackedData.Files.Select(i => i.Key).Where(i =>
  157. // {
  158. // var sp = i.Split('\\');
  159. // return (d.Length + 1 == sp.Length && d.All(pt=>sp.Contains(pt)));
  160. // });//奥のディレクトリまでは取らない
  161. //}
  162. //else
  163. //{
  164. // src = EnumrateDirectoryScript(dir);
  165. //}
  166. foreach (var item in src)
  167. {
  168. Load(item, target, table, labels);
  169. }
  170. }
  171. #if ASSEMBLY
  172. public void LoadFromAssembly(string assembly, Type target)
  173. {
  174. //var trees = CompiledExpressionTree.LoadAll(System.Reflection.Assembly.LoadFile(assembly));
  175. Assembly asm;
  176. if (assemblyDict.ContainsKey(assembly))
  177. {
  178. asm = assemblyDict[assembly];
  179. }
  180. else
  181. {
  182. asm = LoadAssembly(assembly);
  183. }
  184. var trees = CompiledExpressionTree.LoadAll(asm);
  185. //CompiledTrees.AddRange());
  186. foreach (var item in trees)
  187. {
  188. items.Add(item.FileName, new CompiledScriptData(target, item));
  189. }
  190. }
  191. #endif
  192. public string[] GetScriptNames(string assembly)
  193. {
  194. if (!assemblyDict.ContainsKey(assembly))
  195. {
  196. LoadAssembly(assembly);
  197. }
  198. return assemblyDict[assembly].GetExportedTypes().Select(t => t.Name).ToArray();
  199. //System.Reflection.Assembly.
  200. }
  201. /// <summary>
  202. /// Root\\directoryにあるスクリプトファイルを列挙
  203. /// </summary>
  204. /// <param name="directory"></param>
  205. /// <param name="option"></param>
  206. /// <returns></returns>
  207. public IEnumerable<string> EnumrateDirectoryScript(string directory)
  208. {
  209. directory = directory.ToLower();
  210. if (UsePackedData)
  211. {
  212. var d = directory.Split('\\');
  213. return PackedData.Files.Select(i => i.Key).Where(i =>
  214. {
  215. var sp = i.Split('\\');
  216. return (d.Length + 1 == sp.Length && d.All(pt => sp.Contains(pt)));
  217. });//奥のディレクトリまでは取らない
  218. }
  219. else
  220. {
  221. return Directory.EnumerateFiles(GetScriptDirectory(directory), "*.mss", SearchOption.TopDirectoryOnly).Select(i => Path.GetFullPath(i).Substring(FullRoot.Length));
  222. }
  223. }
  224. /// <summary>
  225. /// Root\\directoryにあるスクリプトファイルのKey(拡張子抜きファイル名)を列挙
  226. /// </summary>
  227. /// <param name="directory"></param>
  228. /// <param name="option"></param>
  229. /// <returns></returns>
  230. public IEnumerable<string> EnumrateDirectoryScriptByKey(string directory)
  231. {
  232. return EnumrateDirectoryScript(directory).Select(i => Path.GetFileNameWithoutExtension(i));
  233. }
  234. Assembly LoadAssembly(string asm)
  235. {
  236. var a = System.Reflection.Assembly.LoadFrom(asm);
  237. assemblyDict.Add(asm, a);
  238. return a;
  239. }
  240. public void Reload()
  241. {
  242. try
  243. {
  244. items
  245. .SelectMany(pair => pair.Value.Values)
  246. .OfType<FileScriptData>()
  247. .AsParallel()
  248. .ForAll(s => s.Load());
  249. //items
  250. // .Where(i => i.Value is FileScriptData)
  251. // .Select(i => (FileScriptData)i.Value)
  252. // .AsParallel()
  253. // .ForAll(s => s.Load());
  254. //items.AsParallel().ForAll(p => p.Value.Load());
  255. }
  256. catch (Exception e)
  257. {
  258. WriteLog(e.Message);
  259. throw;
  260. }
  261. }
  262. /*
  263. /// <summary>
  264. /// スクリプトを破棄する。指定したスクリプトが存在しなくても問題は起こさない
  265. /// </summary>
  266. /// <param name="name"></param>
  267. public void DeleteScript(string name)
  268. {
  269. if (items.ContainsKey(name))
  270. {
  271. items.Remove(name);
  272. }
  273. }
  274. * */
  275. public ScriptDataBase GetScriptData(object target, string key)
  276. {
  277. ScriptDataBase data;
  278. TypeScriptDictionary dict;
  279. Type type = target.GetType();
  280. if (items.TryGetValue(type, out dict))
  281. {
  282. if (dict.TryGetValue(key, out data))
  283. {
  284. return data;
  285. }
  286. else
  287. {
  288. throw new Exception(type.ToString() + "のスクリプト " + key + "は読み込まれていない");
  289. }
  290. }
  291. else
  292. {
  293. throw new Exception(type.ToString() + "のスクリプトが存在しない");
  294. }
  295. }
  296. public ScriptRunner GetScript(object target, string key)
  297. {
  298. return GetScriptData(target, key).GetScriptRunner(target);
  299. }
  300. void WriteLog(string txt)
  301. {
  302. //return;
  303. try
  304. {
  305. File.AppendAllText(ErrorLogFileName, txt + "\n");
  306. }
  307. catch
  308. {
  309. }
  310. }
  311. public void OutputDocument(string fileName)
  312. {
  313. var str = new StringBuilder();
  314. foreach (var item in items)
  315. {
  316. str.AppendLine("*" + item.Key.ToString());
  317. str.Append(item.Value.First().Value.GetDocument());
  318. str.AppendLine("-----");
  319. str.AppendLine();
  320. }
  321. File.WriteAllText(fileName, str.ToString());
  322. }
  323. }
  324. }