OSDN Git Service

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