/Application/Core/Utilities.cs

http://yet-another-music-application.googlecode.com/ · C# · 508 lines · 256 code · 60 blank · 192 comment · 65 complexity · a78cd2d0338adbdce236201135d4e911 MD5 · raw file

  1. /**
  2. * Utilities.cs
  3. *
  4. * The swiss armyknife of Stoffi containing, for example, the
  5. * log function.
  6. *
  7. * * * * * * * * *
  8. *
  9. * Copyright 2012 Simplare
  10. *
  11. * This code is part of the Stoffi Music Player Project.
  12. * Visit our website at: stoffiplayer.com
  13. *
  14. * This program is free software; you can redistribute it and/or
  15. * modify it under the terms of the GNU General Public License
  16. * as published by the Free Software Foundation; either version
  17. * 3 of the License, or (at your option) any later version.
  18. *
  19. * See stoffiplayer.com/license for more information.
  20. **/
  21. using System;
  22. using System.Collections.Generic;
  23. using System.IO;
  24. using System.Linq;
  25. using System.Text;
  26. using System.Diagnostics;
  27. using System.Collections.ObjectModel;
  28. using System.Globalization;
  29. using Tomers.WPF.Localization;
  30. namespace Stoffi
  31. {
  32. /// <summary>
  33. /// This is the utility class containing all helper methods
  34. /// </summary>
  35. static partial class U
  36. {
  37. #region Members
  38. /// <summary>
  39. /// Contains the time when the class was first initialized
  40. /// </summary>
  41. private static DateTime initTime;
  42. #endregion
  43. #region Properties
  44. /// <summary>
  45. /// Gets or sets the path to the logfile
  46. /// </summary>
  47. public static string LogFile { get; set; }
  48. /// <summary>
  49. /// Gets or sets the minimum level of messages to print/write
  50. /// </summary>
  51. public static LogLevel Level { get; set; }
  52. /// <summary>
  53. /// Gets or sets whether the main window should listen for keyboard shortcuts
  54. /// </summary>
  55. public static bool ListenForShortcut { get; set; }
  56. #endregion
  57. #region Constructor
  58. /// <summary>
  59. /// Creates a utility class with a "Stoffi.log" logfile in the TEMP folder and a Level of Warning
  60. /// </summary>
  61. static U()
  62. {
  63. LogFile = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), "Stoffi.log");
  64. Level = LogLevel.Warning;
  65. initTime = DateTime.Now;
  66. }
  67. #endregion
  68. #region Methods
  69. #region Public
  70. /// <summary>
  71. /// Logs a message to file and/or console.
  72. /// </summary>
  73. /// <param name="level">The level of the message (if this is lower than Level the message will be ignored)</param>
  74. /// <param name="caller">The caller of the message</param>
  75. /// <param name="message">The message</param>
  76. public static void L(LogLevel level, string caller, string message)
  77. {
  78. if (LevelToInt(level) < LevelToInt(Level)) return;
  79. TimeSpan ts = (DateTime.Now - initTime);
  80. string logLine = String.Format("{0} {1}:{2:00}:{3:00}.{4:000} ({5:G}) [{6}] {7}: {8}",
  81. ts.Days, ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds, DateTime.Now,
  82. LevelToString(level), // #7
  83. caller.ToUpper(),
  84. message);
  85. if (Level == LogLevel.Debug)
  86. Console.WriteLine(logLine);
  87. #if (!DEBUG)
  88. System.IO.StreamWriter sw = null;
  89. try
  90. {
  91. sw = System.IO.File.AppendText(LogFile);
  92. sw.WriteLine(logLine);
  93. }
  94. catch (Exception e)
  95. {
  96. Console.WriteLine("ERROR: Could not write to logfile: " + e.Message);
  97. }
  98. if (sw != null)
  99. sw.Close();
  100. #endif
  101. }
  102. /// <summary>
  103. /// Logs a HttpWebResponse to file and/or console.
  104. /// </summary>
  105. /// <param name="level">The level of the message (if this is lower than Level the message will be ignored)</param>
  106. /// <param name="caller">The caller of the message</param>
  107. /// <param name="response">The HttpWebResponse object.</param>
  108. public static void L(LogLevel level, string caller, System.Net.HttpWebResponse response)
  109. {
  110. if (response == null)
  111. U.L(level, caller, "Response was empty.");
  112. else
  113. {
  114. U.L(level, caller, String.Format("Content Encoding: {0}", response.ContentEncoding));
  115. U.L(level, caller, String.Format("Content Type: {0}", response.ContentType));
  116. U.L(level, caller, String.Format("Status Description: {0}", response.StatusDescription));
  117. StreamReader sr = new StreamReader(response.GetResponseStream());
  118. string str;
  119. while ((str = sr.ReadLine()) != null)
  120. U.L(level, caller, str);
  121. U.L(level, caller, String.Format("-- End of response. Total bytes: {0} --", response.ContentLength));
  122. }
  123. }
  124. /// <summary>
  125. /// Looks for a track with a specific path in a collection
  126. /// </summary>
  127. /// <param name="collection">A collection of tracks</param>
  128. /// <param name="path">A path to look for</param>
  129. /// <returns>True if any track has a path with <paramref name="path"/> as prefix, otherwise false.</returns>
  130. public static bool ContainsPath(ObservableCollection<TrackData> collection, string path)
  131. {
  132. foreach (TrackData t in collection)
  133. if (t.Path.StartsWith(path))
  134. return true;
  135. return false;
  136. }
  137. /// <summary>
  138. /// Looks for all tracks with a specific path in a collection
  139. /// </summary>
  140. /// <param name="collection">A collection of tracks</param>
  141. /// <param name="path">A path to look for</param>
  142. /// <returns>Any track that has a path with <paramref name="path"/> as prefix</returns>
  143. public static List<TrackData> GetTracks(ObservableCollection<TrackData> collection, string path)
  144. {
  145. List<TrackData> ret = new List<TrackData>();
  146. foreach (TrackData t in collection)
  147. if (t.Path.StartsWith(path))
  148. ret.Add(t);
  149. return ret;
  150. }
  151. /// <summary>
  152. /// Turns a size in bytes into a human readable string
  153. /// </summary>
  154. /// <param name="size">The size</param>
  155. /// <returns>A localized string describing the size</returns>
  156. public static string HumanSize(long size)
  157. {
  158. if (size > Math.Pow(10,12))
  159. return String.Format(U.T("SizeTb"), Math.Round((double)(size / Math.Pow(10, 12)), 2));
  160. else if (size > Math.Pow(10, 9))
  161. return String.Format(U.T("SizeGb"), Math.Round((double)(size / Math.Pow(10, 9)), 2));
  162. else if (size > Math.Pow(10, 6))
  163. return String.Format(U.T("SizeMb"), Math.Round((double)(size / Math.Pow(10, 6)), 2));
  164. else if (size > Math.Pow(10, 3))
  165. return String.Format(U.T("SizeKb"), Math.Round((double)(size / Math.Pow(10, 3)), 2));
  166. else
  167. return String.Format(U.T("SizeBytes"), size);
  168. }
  169. /// <summary>
  170. /// Removes characters that are not valid for an XML file.
  171. /// </summary>
  172. /// <param name="str">The string to be parsed</param>
  173. /// <returns>A copy of the string <paramref name="str"/> but with all invalid characters removed</returns>
  174. public static string CleanXMLString(string str)
  175. {
  176. if (str == null) return null;
  177. string r = "";
  178. foreach (char c in str)
  179. {
  180. int n = (int)c;
  181. if ((n >= 9 && n <= 10) ||
  182. (n == 13) ||
  183. (n >= 32 && n <= 55295) ||
  184. (n >= 57344 && n <= 65533) ||
  185. (n >= 65536 && n <= 1114111))
  186. {
  187. r += c;
  188. }
  189. }
  190. return r;
  191. }
  192. /// <summary>
  193. /// Converts back an escaped string passed via Rails CGI.escapeHTML method.
  194. /// </summary>
  195. /// <param name="str">The escaped string</param>
  196. /// <returns>An unescaped version of str</returns>
  197. public static string UnescapeHTML(string str)
  198. {
  199. return str.Replace("&quot;", "\"").Replace("&gt;", ">").Replace("&lt;", "<").Replace("&amp;", "&");
  200. }
  201. /// <summary>
  202. /// Parses HTTP query parameters
  203. /// </summary>
  204. /// <param name="query">The HTTP query</param>
  205. /// <returns>A dictionary with key-value pairs</returns>
  206. public static Dictionary<string, string> GetParams(string query)
  207. {
  208. string[] parameters = query.Split('&');
  209. Dictionary<string, string> d = new Dictionary<string, string>();
  210. foreach (string parameter in parameters)
  211. {
  212. string[] p = parameter.Split(new char[] { '=' }, 2);
  213. d.Add(p[0], p[1]);
  214. }
  215. return d;
  216. }
  217. /// <summary>
  218. /// Retrieves the query from a HTTP URL.
  219. /// Will clean up multiple question marks.
  220. /// </summary>
  221. /// <param name="url">The HTTP URL</param>
  222. /// <returns>A query string without any question marks</returns>
  223. public static string GetQuery(string url)
  224. {
  225. string[] tokens = url.Split('?');
  226. string ret = "";
  227. for (int i = 1; i < tokens.Count(); i++)
  228. {
  229. ret += "&" + tokens[i];
  230. }
  231. return ret.Substring(1);
  232. }
  233. /// <summary>
  234. /// Creates a HTTP parameter string from a key and a value.
  235. /// If value is null then it will return null instead of an
  236. /// empty assignment.
  237. /// All values and parameters will be encoded
  238. /// </summary>
  239. /// <param name="key">The parameter name</param>
  240. /// <param name="value">The value of the parameter</param>
  241. /// <param name="prefix">A prefix to be set on all parameter names</param>
  242. /// <returns>key=value if value is not null, otherwise null</returns>
  243. public static string CreateParam(string key, string value, string prefix)
  244. {
  245. if (key == null || value == null)
  246. return null;
  247. else
  248. {
  249. if (prefix == null)
  250. return String.Format("{0}={1}",
  251. OAuth.Manager.UrlEncode(key),
  252. OAuth.Manager.UrlEncode(value));
  253. else
  254. return String.Format("{0}[{1}]={2}",
  255. prefix,
  256. OAuth.Manager.UrlEncode(key),
  257. OAuth.Manager.UrlEncode(value));
  258. }
  259. }
  260. /// <summary>
  261. /// Creates a HTTP parameter string from a key and a value.
  262. /// If value is null then it will return null instead of an
  263. /// empty assignment.
  264. /// All values and parameters will be encoded
  265. /// </summary>
  266. /// <param name="key">The parameter name</param>
  267. /// <param name="value">The value of the parameter</param>
  268. /// <param name="prefix">A prefix to be set on all parameter names</param>
  269. /// <returns>key=value if value is not null, otherwise null</returns>
  270. public static string CreateParam(string key, int value, string prefix)
  271. {
  272. return CreateParam(key, value.ToString(), prefix);
  273. }
  274. /// <summary>
  275. /// Creates a HTTP parameter string from a key and a value.
  276. /// If value is null then it will return null instead of an
  277. /// empty assignment.
  278. /// All values and parameters will be encoded
  279. /// </summary>
  280. /// <param name="key">The parameter name</param>
  281. /// <param name="value">The value of the parameter</param>
  282. /// <param name="prefix">A prefix to be set on all parameter names</param>
  283. /// <returns>key=value if value is not null, otherwise null</returns>
  284. public static string CreateParam(string key, uint value, string prefix)
  285. {
  286. return CreateParam(key, value.ToString(), prefix);
  287. }
  288. /// <summary>
  289. /// Creates a HTTP parameter string from a key and a value.
  290. /// If value is null then it will return null instead of an
  291. /// empty assignment.
  292. /// All values and parameters will be encoded
  293. /// </summary>
  294. /// <param name="key">The parameter name</param>
  295. /// <param name="value">The value of the parameter</param>
  296. /// <param name="prefix">A prefix to be set on all parameter names</param>
  297. /// <returns>key=value if value is not null, otherwise null</returns>
  298. public static string CreateParam(string key, double value, string prefix)
  299. {
  300. return CreateParam(key, Convert.ToString(value), prefix);
  301. }
  302. /// <summary>
  303. /// Turns the first letter into uppercase and the rest into lowercase.
  304. /// </summary>
  305. /// <param name="str">The string to modify.</param>
  306. /// <returns>The string str with its first letter in uppercase and the rest in lowercase.</returns>
  307. public static String Capitalize(String str)
  308. {
  309. if (str.Length == 0)
  310. return "";
  311. else
  312. {
  313. str = str.ToLower();
  314. char[] a = str.ToCharArray();
  315. a[0] = Char.ToUpper(a[0]);
  316. return new String(a);
  317. }
  318. }
  319. /// <summary>
  320. /// Turns a timespan into a string in the format:
  321. /// X days, X hours, X minutes, X seconds
  322. /// </summary>
  323. /// <param name="ts">The timespan to turn into a string</param>
  324. /// <returns>The timespan printed out to a string</returns>
  325. public static String TimeSpanToLongString(TimeSpan ts)
  326. {
  327. String ret = "";
  328. if (ts.Days > 0)
  329. ret += String.Format("{0} {1}, ", ts.Days, T("DateDays"));
  330. if (ts.Hours > 0)
  331. ret += String.Format("{0} {1}, ", ts.Hours, T("DateHours"));
  332. ret += String.Format("{0} {1}, ", ts.Minutes, T("DateMinutes"));
  333. ret += String.Format("{0} {1}", ts.Seconds, T("DateSeconds"));
  334. return ret;
  335. }
  336. /// <summary>
  337. /// Turns a timespan to a short and compact string in the format:
  338. /// DD HH:MM:SS (days, hours, minutes, seconds and leading zeroes)
  339. /// </summary>
  340. /// <param name="ts">The timespan to turn into a string</param>
  341. /// <returns>The timespan printed out to a short string</returns>
  342. public static String TimeSpanToString(TimeSpan ts)
  343. {
  344. String ret = "";
  345. if (ts.Days > 0)
  346. ret += String.Format("{0:00}:", ts.Days);
  347. if (ts.Hours > 0)
  348. ret += String.Format("{0:00}:", ts.Hours);
  349. ret += String.Format("{0:00}:", ts.Minutes);
  350. ret += String.Format("{0:00}", ts.Seconds);
  351. return ret;
  352. }
  353. /// <summary>
  354. /// Translates a string
  355. /// </summary>
  356. /// <param name="id">The id of the translation value</param>
  357. /// <param name="field">The field of the translation value</param>
  358. /// <param name="def">The default value (sets to field if empty)</param>
  359. /// <returns>The string associated with the translation value</returns>
  360. public static string T(string id, string field = "Text", string def = "")
  361. {
  362. if (def == "") def = field;
  363. LanguageDictionary dictionary = LanguageDictionary.GetDictionary(LanguageContext.Instance.Culture);
  364. return (string)dictionary.Translate(id, field, def, typeof(string));
  365. }
  366. /// <summary>
  367. /// Formats an integer using local culture
  368. /// </summary>
  369. /// <param name="n">The number to format</param>
  370. /// <returns>The number formatted according to localization</returns>
  371. public static string T(int n)
  372. {
  373. return n.ToString("N0", LanguageContext.Instance.Culture);
  374. }
  375. /// <summary>
  376. /// Formats a double using local culture
  377. /// </summary>
  378. /// <param name="n">The number to format</param>
  379. /// <returns>The number formatted according to localization</returns>
  380. public static string T(double n)
  381. {
  382. return n.ToString("N", LanguageContext.Instance.Culture);
  383. }
  384. /// <summary>
  385. /// Formats a date using local culture
  386. /// </summary>
  387. /// <param name="dt">The date to format</param>
  388. /// <returns>The date formatted according to localization</returns>
  389. public static string T(DateTime dt)
  390. {
  391. return dt.ToString(LanguageContext.Instance.Culture);
  392. }
  393. #endregion
  394. #region Private
  395. /// <summary>
  396. /// Converts a LogLevel to an integer
  397. /// </summary>
  398. /// <param name="level">The level to convert</param>
  399. /// <returns><paramref name="level"/> represented as an integer where Debug &lt; PropertiesWindow &lt; Warning &lt; Error</returns>
  400. private static int LevelToInt(LogLevel level)
  401. {
  402. if (level == LogLevel.Debug) return 1;
  403. else if (level == LogLevel.Information) return 2;
  404. else if (level == LogLevel.Warning) return 3;
  405. else if (level == LogLevel.Error) return 4;
  406. else return 0;
  407. }
  408. /// <summary>
  409. /// Converts a LogLevel to a string
  410. /// </summary>
  411. /// <param name="level">The level to convert</param>
  412. /// <returns><paramref name="level"/> represented as a string</returns>
  413. private static string LevelToString(LogLevel level)
  414. {
  415. if (level == LogLevel.Debug) return "DEBUG";
  416. else if (level == LogLevel.Information) return "INFO";
  417. else if (level == LogLevel.Warning) return "OOPS";
  418. else if (level == LogLevel.Error) return "SHIT";
  419. else return "HUH?";
  420. }
  421. #endregion
  422. #endregion
  423. }
  424. #region Enum
  425. /// <summary>
  426. /// Describes the level of a log message
  427. /// </summary>
  428. public enum LogLevel
  429. {
  430. /// <summary>
  431. /// Messages that are useful when debugging the application
  432. /// </summary>
  433. Debug,
  434. /// <summary>
  435. /// Messages that show general information about the application's actions
  436. /// </summary>
  437. Information,
  438. /// <summary>
  439. /// Messages that informs about something that have gone wrong
  440. /// </summary>
  441. Warning,
  442. /// <summary>
  443. /// Messages informing that something fatal has happened to the application
  444. /// </summary>
  445. Error
  446. }
  447. #endregion
  448. }