OSDN Git Service

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