OSDN Git Service

na-get-lib,AppliStation-GUI,レポジトリリスト編集機能追加
[applistation/AppliStation.git] / na-get-lib / NaGet / Utils.cs
1 using System;\r
2 using System.Collections.Generic;\r
3 using System.IO;\r
4 using System.Text;\r
5 using System.Globalization;\r
6 using System.Security.Principal;\r
7 using System.Security.AccessControl;\r
8 using System.Reflection;\r
9 using System.Diagnostics;\r
10 using Microsoft.Win32;\r
11 \r
12 namespace NaGet\r
13 {\r
14 \r
15         /// <summary>\r
16         /// 雑多な便利メソッドを集めたクラス\r
17         /// </summary>\r
18         public sealed class Utils\r
19         {\r
20                 /// <summary>\r
21                 /// 呼び出し禁止\r
22                 /// </summary>\r
23                 private Utils()\r
24                 {\r
25                 }\r
26                 \r
27                 #region 汎用的なオブジェクト操作メソッド\r
28                 \r
29                 /// <summary>\r
30                 /// オブジェクトのフィールドをコピーしてクローン化する\r
31                 /// </summary>\r
32                 /// <param name="from">コピー元</param>\r
33                 /// <param name="target">コピー先。コピー元のクラスと同一か継承している型でなければならない</param>\r
34                 public static void FieldCopy<T,U>(T from, ref U target) where U : T\r
35                 {\r
36                         foreach(FieldInfo member in typeof(T).GetFields()) {\r
37                                 try {\r
38                                         member.SetValue(target, member.GetValue(from));\r
39                                 } catch (FieldAccessException) {} // アクセス不能は無視\r
40                         }\r
41                 }\r
42                 \r
43                 #endregion\r
44                 \r
45                 #region リスト関連関数\r
46                 \r
47                 /// <summary>\r
48                 /// イテレータを結合して、返す\r
49                 /// </summary>\r
50                 /// <param name="enus">元となる複数のイテレータ</param>\r
51                 /// <returns>結合されたイテレータ</returns>\r
52                 public static IEnumerable<T> MergeEnumerable<T>(params IEnumerable<T>[] enus)\r
53                 {\r
54                         foreach (IEnumerable<T> enu in enus) {\r
55                                 if (enu == null) continue;\r
56                                 \r
57                                 foreach (T elem in enu) {\r
58                                         yield return elem;\r
59                                 }\r
60                         }\r
61                 }\r
62 \r
63                 /// <summary>\r
64                 /// イテレータを結合して、返す\r
65                 /// </summary>\r
66                 /// <param name="enus">元となる複数のイテレータ</param>\r
67                 /// <returns>結合されたイテレータ</returns>\r
68                 public static IEnumerable<T> MergeEnumerable<T>(params IEnumerator<T>[] enus)\r
69                 {\r
70                         foreach (IEnumerator<T> enu in enus) {\r
71                                 if (enu == null) continue;\r
72                                 \r
73                                 try {\r
74                                         while (enu.MoveNext()) {\r
75                                                 yield return enu.Current;\r
76                                         }\r
77                                 } finally {\r
78                                         enu.Dispose();\r
79                                 }\r
80                         }\r
81                 }\r
82                 \r
83                 /// <summary>\r
84                 /// イテレータを結合して、Listとして返す\r
85                 /// </summary>\r
86                 /// <param name="enus">元となる複数のイテレータ</param>\r
87                 /// <returns>結合されたイテレータ</returns>\r
88                 public static List<T> MeargeList<T>(params IEnumerable<T>[] enus)\r
89                 {\r
90                         List<T> list = new List<T>();\r
91                         \r
92                         foreach (IEnumerable<T> enu in enus) {\r
93                                 if (enu == null) continue;\r
94                         \r
95                                 list.AddRange(enu);\r
96                         }\r
97                         return list;\r
98                 }\r
99                 \r
100                 /// <summary>\r
101                 /// イテレータを配列に効率的に変換する\r
102                 /// </summary>\r
103                 /// <remarks>与えられる型が具体的にわかっているならば、それに特化した手続きをとる方が好ましい</remarks>\r
104                 /// <param name="enu">元となるイテレータ</param>\r
105                 /// <returns>変換された配列</returns>\r
106                 public static T[] IEnumerable2Array<T>(IEnumerable<T> enu) {\r
107                         if (enu is T[]) {\r
108                                 return (T[]) enu;\r
109                         } else {\r
110                                 return ((enu is List<T>)? ((List<T>)enu):new List<T>(enu)).ToArray();\r
111                         }\r
112                 }\r
113                 \r
114                 /// <summary>\r
115                 /// リストに対して指定した2つの要素の位置を入れ替える\r
116                 /// </summary>\r
117                 /// <param name="list">操作対象のリスト</param>\r
118                 /// <param name="indexA">位置</param>\r
119                 /// <param name="indexB">位置</param>\r
120                 public static void ListSwap(System.Collections.IList list, int indexA, int indexB)\r
121                 {\r
122                         if ((indexA < 0) || (list.Count <= indexA) || (indexB < 0) || (list.Count <= indexB)) {\r
123                                 throw new IndexOutOfRangeException();\r
124                         } else if (indexA != indexB) {\r
125                                 object temp = list[indexA];\r
126                                 list[indexA] = list[indexB];\r
127                                 list[indexB] = temp;\r
128                         }\r
129                 }\r
130                 \r
131                 #endregion\r
132                                 \r
133                 #region ファイル情報関連ユーテイリティ関数\r
134                 \r
135                 /// <summary>\r
136                 /// パス変数に指定のフォルダを追加する\r
137                 /// </summary>\r
138                 /// <param name="dir">追加するフォルダ</param>\r
139                 public static void AddDirectoryToPath(string dir)\r
140                 {\r
141                         string path = Environment.GetEnvironmentVariable("PATH");\r
142                         \r
143                         if (path.IndexOf(dir) < 0) {\r
144                                 path = dir + Path.PathSeparator + path;\r
145                                 Environment.SetEnvironmentVariable("PATH", path);\r
146                         }\r
147                 }\r
148                 \r
149                 /// <summary>\r
150                 /// バイト単位で表現された容量を接尾語を活用して適切な文字列に変換\r
151                 /// </summary>\r
152                 /// <param name="bytes">バイト単位の容量</param>\r
153                 /// <returns>読みやすい形に直された容量文字列</returns>\r
154         public static string FormatSize(double bytes)\r
155         {\r
156                 string[] units = new string[] {"B", "KB", "MB", "GB", "TB"};\r
157                 \r
158                 double size = bytes;\r
159                 int i;\r
160                 for (i = 0; size >= 1024 && i < units.Length-1 ; i++) {\r
161                         size /= 1024.0;\r
162                 }\r
163                 \r
164                 return string.Format("{0:F2}{1}", size, units[i]);\r
165         }\r
166         \r
167         public static string FormatSize(long bytes)\r
168         {\r
169                 return FormatSize((double) bytes);\r
170         }\r
171                 \r
172                 /// <summary>\r
173                 /// URLからそのファイル名を生成する\r
174                 /// </summary>\r
175                 /// <param name="url">対象のurl</param>\r
176                 public static string Url2filename(string url)\r
177                 {\r
178                         string filename = Path.GetFileName(System.Web.HttpUtility.UrlDecode(url, Encoding.UTF8));\r
179                         \r
180                         int pos;\r
181                         if ((pos = filename.IndexOfAny(Path.GetInvalidFileNameChars())) >= 0) {\r
182                                 // 不正な文字が含まれているならば、それ以降を削除\r
183                                 filename = filename.Substring(0, pos);\r
184                                 // そうしてしまったら文字の内容がまったくなくなってしまったら、ランダムな名に\r
185                                 if (filename.Length == 0) {\r
186                                         filename = Path.GetFileName(Path.GetTempFileName());\r
187                                 }\r
188                         }\r
189                         return filename;\r
190                         //return UrlDecode(Path.GetFileName(url), Encoding.UTF8);\r
191                 }\r
192                 \r
193                 /// <summary>\r
194                 /// 再帰的にファイルの属性を指定します。強制的にフォルダの再帰削除の前に読み込み専用属性を消すのに使います。\r
195                 /// </summary>\r
196                 /// <param name="path">設定するフォルダ</param>\r
197                 /// <param name="attr">設定する属性値</param>\r
198                 public static void SetAttributeRecursive(string path, FileAttributes attr)\r
199                 {\r
200                         // 自分自身の属性を変更\r
201                         File.SetAttributes(path, attr);\r
202                         \r
203                         // 子ファイルの属性変更\r
204                         foreach (string file in Directory.GetFiles(path)) {\r
205                                 File.SetAttributes(file, attr);\r
206                         }\r
207                         \r
208                         // 子フォルダを再帰的に属性変更\r
209                         foreach (string file in Directory.GetDirectories(path)) {\r
210                                 SetAttributeRecursive(file, attr);\r
211                         }\r
212                 }\r
213                 \r
214                 /// <summary>\r
215                 /// 再帰的にファイルのアクセス権限(ユーザ権限など)を設定します\r
216                 /// </summary>\r
217                 /// <param name="path">設定するフォルダ</param>\r
218                 /// <param name="filesec">変更先権限</param>\r
219                 public static void SetAccessControlRecursive(string path, FileSecurity filesec)\r
220                 {\r
221                         // 自分自身の権限を変更\r
222                         File.SetAccessControl(path, filesec);\r
223                         \r
224                         // 子ファイルの権限を変更\r
225                         foreach (string file in Directory.GetFiles(path)) {\r
226                                 File.SetAccessControl(file, filesec);\r
227                         }\r
228                         \r
229                         // 子フォルダを再帰的に権限変更\r
230                         foreach (string file in Directory.GetDirectories(path)) {\r
231                                 SetAccessControlRecursive(file, filesec);\r
232                         }\r
233                 }\r
234                 \r
235                 /// <summary>\r
236                 /// ファイルまたはフォルダの容量を算出して返す\r
237                 /// </summary>\r
238                 /// <param name="path">\r
239                 /// 対象ファイル及びフォルダのパス\r
240                 /// </param>\r
241                 /// <returns>\r
242                 /// 計算された容量(バイト単位)\r
243                 /// </returns>\r
244                 public static ulong GetFileSize(string path)\r
245                 {\r
246                         return ((File.GetAttributes(path) & FileAttributes.Directory) != 0)?\r
247                                 GetDirectoryFileSize(new DirectoryInfo(path)) : ((ulong) (new FileInfo(path)).Length);\r
248                 }\r
249                 \r
250                 /// <summary>\r
251                 /// フォルダの容量を算出して返す\r
252                 /// </summary>\r
253                 /// <param name="dirInfo">\r
254                 /// 対象フォルダ\r
255                 /// </param>\r
256                 /// <returns>\r
257                 /// 計算された容量(バイト単位)\r
258                 /// </returns>\r
259                 public static ulong GetDirectoryFileSize(DirectoryInfo dirInfo)\r
260                 {\r
261                         ulong size = 0;\r
262                         foreach (FileInfo child in dirInfo.GetFiles("*", SearchOption.AllDirectories)) {\r
263                                 size += (ulong) child.Length;\r
264                         }\r
265                         return size;\r
266                 }\r
267                                 \r
268                 /// <summary>\r
269                 /// ワイルドカードを展開したファイルパス文字列を作り出す。\r
270                 /// 戻り値のそれぞれの文字列はフルパスとなる。\r
271                 /// </summary>\r
272                 /// <param name="baseDir">ベース(基点)のディレクトリ</param>\r
273                 /// <param name="pattern">ワイルドカードパターン</param>\r
274                 /// <returns>展開したファイルパス</returns>\r
275                 public static string[] ExtendWildcardFile(string baseDir, string pattern)\r
276                 {\r
277                         if (pattern.IndexOfAny(new char[]{'*','?'}) < 0) {\r
278                                 return new string[]{Path.Combine(baseDir, pattern)}; // ワイルドカードがなければそのまま返す\r
279                         }\r
280 \r
281                         string[] pathArray = pattern.Split(Path.DirectorySeparatorChar);\r
282                         List<string> extended = new List<string>();\r
283                         try {\r
284                                 if (pathArray.Length == 1) {\r
285                                         extended.AddRange(Directory.GetFiles(baseDir, pathArray[0], SearchOption.TopDirectoryOnly));\r
286                                         extended.AddRange(Directory.GetDirectories(baseDir, pathArray[0], SearchOption.TopDirectoryOnly));\r
287                                 } else { // pathArray.Length > 1\r
288                                         string subPattern = string.Join(Path.DirectorySeparatorChar.ToString(), pathArray, 1, pathArray.Length-1);\r
289                                         \r
290                                         foreach (string subDir in Directory.GetDirectories(baseDir, pathArray[0], SearchOption.TopDirectoryOnly)) {\r
291                                                 // 再帰的に追加してゆく\r
292                                                 extended.AddRange(ExtendWildcardFile(subDir, subPattern));\r
293                                         }\r
294                                 }\r
295                         } catch (UnauthorizedAccessException) {\r
296                         }\r
297                         \r
298                         // 存在しないパスは消去する\r
299                         extended.RemoveAll(\r
300                                 delegate(string path) {\r
301                                         return ! File.Exists(path);\r
302                                 }\r
303                         );\r
304                         \r
305                         return extended.ToArray();\r
306                 }\r
307                 \r
308                 /// <summary>\r
309                 /// パスをパス区切り文字列ごとに分割した配列を返す\r
310                 /// </summary>\r
311                 /// <param name="path">パス文字列。相対・絶対は区別しない</param>\r
312                 /// <returns>フォルダ名ごとに分けられた文字列配列</returns>\r
313                 private static string[] splitPath(string path)\r
314                 {\r
315                         return path.Split(new char[]{Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar});\r
316                 }\r
317                 \r
318                 /// <summary>\r
319                 /// Converts a given absolute path and a given base path to a path that leads\r
320                 /// from the base path to the absoulte path. (as a relative path)\r
321                 /// </summary>\r
322                 public static string GetRelativePath(string baseDirectoryPath, string absPath)\r
323                 {\r
324                         // TODO SharpDevelopのICSharpCode.Core.FileUtilityからのコピペ(GPL)\r
325                         \r
326                         string[] bPath = splitPath(baseDirectoryPath);\r
327                         string[] aPath = splitPath(absPath);\r
328                         int indx = 0;\r
329                         for(; indx < Math.Min(bPath.Length, aPath.Length); ++indx){\r
330                                 if(!bPath[indx].Equals(aPath[indx], StringComparison.OrdinalIgnoreCase))\r
331                                         break;\r
332                         }\r
333 \r
334                         if (indx == 0) {\r
335                                 return absPath;\r
336                         }\r
337 \r
338                         StringBuilder erg = new StringBuilder();\r
339 \r
340                         if(indx == bPath.Length) {\r
341 //                              erg.Append('.');\r
342 //                              erg.Append(Path.DirectorySeparatorChar);\r
343                         } else {\r
344                                 for (int i = indx; i < bPath.Length; ++i) {\r
345                                         erg.Append("..");\r
346                                         erg.Append(Path.DirectorySeparatorChar);\r
347                                 }\r
348                         }\r
349                         erg.Append(String.Join(Path.DirectorySeparatorChar.ToString(), aPath, indx, aPath.Length-indx));\r
350                         return erg.ToString();\r
351                 }\r
352                 \r
353                 /// <summary>\r
354                 /// 相対パスに含まれている".."などを消去する\r
355                 /// </summary>\r
356                 /// <param name="aPath"></param>\r
357                 /// <returns></returns>\r
358                 public static string GetDotsRemovedPath(string aPath)\r
359                 {\r
360                         string[] folders = splitPath(aPath);\r
361                         List<string> newFolders = new List<string>();\r
362                         \r
363                         foreach (string fol in folders) {\r
364                                 if (fol == ".") {\r
365                                         // 無視\r
366                                 } else if (fol == "..") {\r
367                                         // 一つ前のフォルダを消す\r
368                                         newFolders.RemoveAt(newFolders.Count-1);\r
369                                 } else {\r
370                                         newFolders.Add(fol);\r
371                                 }\r
372                         }\r
373                         \r
374                         return string.Join(Path.DirectorySeparatorChar.ToString(), newFolders.ToArray());\r
375                 }\r
376                 \r
377                 #endregion\r
378                 \r
379                 #region シリアル化関連\r
380                 \r
381                 /// <summary>\r
382                 /// XMLでシリアル化したオブジェクトのXMLファイルを読み込み、デシリアル化したオブジェクトを取得する\r
383                 /// </summary>\r
384                 /// <param name="path">XMLファイルのパス</param>\r
385                 /// <returns>デシリアル化されたオブジェクト</returns>\r
386                 public static T GetDeserializedObject<T>(string path)\r
387                 {\r
388                         T retVal = default(T);\r
389                         using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) {\r
390                                 System.Xml.Serialization.XmlSerializer sr = new System.Xml.Serialization.XmlSerializer(typeof(T));\r
391                                 retVal = (T) sr.Deserialize(fs);\r
392                         }\r
393                         return retVal;\r
394                 }\r
395                 \r
396                 /// <summary>\r
397                 /// オブジェクトをXMLでシリアル化してファイルに書き込む\r
398                 /// </summary>\r
399                 /// <param name="path">XMLファイルのパス</param>\r
400                 /// <param name="obj">シリアル化する対象のオブジェクト</param>\r
401                 public static void PutSerializeObject<T>(string path, T obj)\r
402                 {\r
403                         using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write)) {\r
404                                 System.Xml.Serialization.XmlSerializer sr = new System.Xml.Serialization.XmlSerializer(typeof(T));\r
405                                 sr.Serialize(fs, obj);\r
406                         }\r
407                 }\r
408                 \r
409                 #endregion\r
410                 \r
411                 #region 権限関連関数群\r
412                 \r
413                 /// <summary>\r
414                 /// 現在のユーザがAdministrators権限を持っているか否かを返す。\r
415                 /// </summary>\r
416                 /// <remarks>UAC有効時には権限昇格後になってtrueを返すようになります</remarks>\r
417                 public static bool IsAdministrators()\r
418                 {\r
419                         // 現在の Windows ユーザーを現在のスレッドのプリンシパルに反映する\r
420                         AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal );\r
421                         IPrincipal prin = System.Threading.Thread.CurrentPrincipal;\r
422                         return prin.IsInRole(@"BUILTIN\Administrators");\r
423                 }\r
424                 \r
425                 /// <summary>\r
426                 /// 現在のPCがUACが有効になっているか否かを返す。\r
427                 /// レジストリのHKLM\SOFTWARE¥Microsoft¥Windows¥CurrentVersion¥Policies¥System\EnableLUAの値を見る。\r
428                 /// </summary>\r
429                 /// <returns>UACが有効ならばtrue</returns>\r
430                 public static bool IsUACEnabled()\r
431                 {\r
432                         try {\r
433                                 using(RegistryKey key = Registry.LocalMachine.CreateSubKey(@"SOFTWARE¥Microsoft¥Windows¥CurrentVersion¥Policies¥System")) {\r
434                                         return ((int) key.GetValue("EnableLUA", 0)) == 1;\r
435                                 }\r
436                         } catch (Exception) {\r
437                                 return false;\r
438                         }\r
439                 }\r
440                 \r
441                 #endregion\r
442 \r
443                 #region プロセス関連便利メソッド群\r
444                 \r
445                 /// <summary>\r
446                 /// プロセスに出力をリダイレクトした上で実行\r
447                 /// </summary>\r
448                 /// <param name="procInfo">プロセス起動情報</param>\r
449                 /// <param name="outputReceived">標準出力用リスナ(null可)</param>\r
450                 /// <param name="errorReceived">エラー出力用リスナ(null可)</param>\r
451                 /// <returns>実行プロセス</returns>\r
452                 public static Process ProcessStartWithOutputCapture(ProcessStartInfo procInfo,\r
453                                                   DataReceivedEventHandler outputReceived,\r
454                                                   DataReceivedEventHandler errorReceived)\r
455                 {\r
456                         if (outputReceived != null) {\r
457                                 procInfo.RedirectStandardOutput = true;\r
458                         }\r
459                         if (errorReceived != null) {\r
460                                 procInfo.RedirectStandardError = true;\r
461                         }\r
462                         procInfo.UseShellExecute = false;\r
463                         \r
464                         Process hProcess = Process.Start(procInfo);\r
465                         if (outputReceived != null) {\r
466                                 hProcess.OutputDataReceived += outputReceived;\r
467                                 hProcess.BeginOutputReadLine();\r
468                         }\r
469                         if (errorReceived != null) {\r
470                                 hProcess.ErrorDataReceived += errorReceived;    \r
471                                 hProcess.BeginErrorReadLine();\r
472                         }\r
473                         \r
474                         return hProcess;\r
475                 }\r
476                 \r
477                 \r
478                 /// <summary>\r
479                 /// プロセスに出力をリダイレクトした上で実行\r
480                 /// </summary>\r
481                 /// <param name="procInfo">プロセス起動情報</param>\r
482                 /// <param name="outputReceived">標準出力用リスナ(null可)</param>\r
483                 /// <param name="errorReceived">エラー出力用リスナ(null可)</param>\r
484                 /// <returns>実行プロセス</returns>\r
485                 public static Process ProcessStartWithOutputCapture(ProcessStartInfo procInfo,\r
486                                                   EventHandler<AnyDataEventArgs<string>> outputReceived,\r
487                                                   EventHandler<AnyDataEventArgs<string>> errorReceived)\r
488                 {\r
489                         return ProcessStartWithOutputCapture(procInfo,\r
490                                                              ConvertToDataReceivedEventHandler(outputReceived),\r
491                                                              ConvertToDataReceivedEventHandler(errorReceived));\r
492                 }\r
493                 \r
494                 public static DataReceivedEventHandler ConvertToDataReceivedEventHandler(EventHandler<AnyDataEventArgs<string>> handler)\r
495                 {\r
496                         if (handler == null) return null;\r
497                         return delegate (object sender, DataReceivedEventArgs e) {\r
498                                 AnyDataEventArgs<string> args = new AnyDataEventArgs<string>(e.Data);\r
499                                 handler.Invoke(sender, args);\r
500                         };\r
501                 }\r
502                 \r
503                 #endregion\r
504 \r
505                 #region イベント情報\r
506                 \r
507                 /// <summary>\r
508                 /// 任意データのイベント情報を表現するクラス\r
509                 /// </summary>\r
510                 public class AnyDataEventArgs<T> : EventArgs\r
511                 {\r
512                         /// <summary>\r
513                         /// データ\r
514                         /// </summary>\r
515                         T data;\r
516                         \r
517                         public AnyDataEventArgs(T data)\r
518                         {\r
519                                 this.data = data;\r
520                         }\r
521                         \r
522                         /// <summary>\r
523                         /// データを返す\r
524                         /// </summary>\r
525                         public T Data {\r
526                                 get { return data; }\r
527                         }\r
528                 }\r
529 \r
530                 #endregion\r
531         }\r
532 }\r