OSDN Git Service

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