HttpInputItem.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. namespace OSHttpServer
  5. {
  6. /// <summary>
  7. /// represents a HTTP input item. Each item can have multiple sub items, a sub item
  8. /// is made in a HTML form by using square brackets
  9. /// </summary>
  10. /// <example>
  11. /// // <input type="text" name="user[FirstName]" value="jonas" /> becomes:
  12. /// Console.WriteLine("Value: {0}", form["user"]["FirstName"].Value);
  13. /// </example>
  14. /// <remarks>
  15. /// All names in a form SHOULD be in lowercase.
  16. /// </remarks>
  17. public class HttpInputItem : IHttpInput
  18. {
  19. /// <summary> Representation of a non-initialized <see cref="HttpInputItem"/>.</summary>
  20. public static readonly HttpInputItem Empty = new(string.Empty, true);
  21. private readonly IDictionary<string, HttpInputItem> _items = new Dictionary<string, HttpInputItem>();
  22. private readonly List<string> _values = new();
  23. private string _name;
  24. private readonly bool _ignoreChanges;
  25. /// <summary>
  26. /// Initializes an input item setting its name/identifier and value
  27. /// </summary>
  28. /// <param name="name">Parameter name/id</param>
  29. /// <param name="value">Parameter value</param>
  30. public HttpInputItem(string name, string value)
  31. {
  32. Name = name;
  33. Add(value);
  34. }
  35. private HttpInputItem(string name, bool ignore)
  36. {
  37. Name = name;
  38. _ignoreChanges = ignore;
  39. }
  40. /// <summary>Creates a deep copy of the item specified</summary>
  41. /// <param name="item">The item to copy</param>
  42. /// <remarks>The function makes a deep copy of quite a lot which can be slow</remarks>
  43. public HttpInputItem(HttpInputItem item)
  44. {
  45. foreach (KeyValuePair<string, HttpInputItem> pair in item._items)
  46. _items.Add(pair.Key, pair.Value);
  47. foreach (string value in item._values)
  48. _values.Add(value);
  49. _ignoreChanges = item._ignoreChanges;
  50. _name = item.Name;
  51. }
  52. /// <summary>
  53. /// Number of values
  54. /// </summary>
  55. public int Count
  56. {
  57. get { return _values.Count; }
  58. }
  59. /// <summary>
  60. /// Get a sub item
  61. /// </summary>
  62. /// <param name="name">name in lower case.</param>
  63. /// <returns><see cref="HttpInputItem.Empty"/> if no item was found.</returns>
  64. public HttpInputItem this[string name]
  65. {
  66. get
  67. {
  68. return _items.TryGetValue(name, out HttpInputItem hii) ? hii : Empty;
  69. }
  70. }
  71. /// <summary>
  72. /// Name of item (in lower case).
  73. /// </summary>
  74. public string Name
  75. {
  76. get { return _name; }
  77. set { _name = value; }
  78. }
  79. /// <summary>
  80. /// Returns the first value, or null if no value exist.
  81. /// </summary>
  82. public string Value
  83. {
  84. get {
  85. return _values.Count == 0 ? null : _values[0];
  86. }
  87. set
  88. {
  89. if (_values.Count == 0)
  90. _values.Add(value);
  91. else
  92. _values[0] = value;
  93. }
  94. }
  95. /// <summary>
  96. /// Returns the last value, or null if no value exist.
  97. /// </summary>
  98. public string LastValue
  99. {
  100. get
  101. {
  102. return _values.Count == 0 ? null : _values[^1];
  103. }
  104. }
  105. /// <summary>
  106. /// Returns the list with values.
  107. /// </summary>
  108. public IList<string> Values
  109. {
  110. get { return _values.AsReadOnly(); }
  111. }
  112. /// <summary>
  113. /// Add another value to this item
  114. /// </summary>
  115. /// <param name="value">Value to add.</param>
  116. /// <exception cref="InvalidOperationException">Cannot add stuff to <see cref="HttpInput.Empty"/>.</exception>
  117. public void Add(string value)
  118. {
  119. if (value is null)
  120. return;
  121. if (_ignoreChanges)
  122. throw new InvalidOperationException("Cannot add stuff to HttpInput.Empty.");
  123. _values.Add(value);
  124. }
  125. /// <summary>
  126. /// checks if a sub-item exists (and has a value).
  127. /// </summary>
  128. /// <param name="name">name in lower case</param>
  129. /// <returns>true if the sub-item exists and has a value; otherwise false.</returns>
  130. public bool Contains(string name)
  131. {
  132. return _items.TryGetValue(name, out HttpInputItem hii) && hii.Value != null;
  133. }
  134. /// <summary> Returns a formatted representation of the instance with the values of all contained parameters </summary>
  135. public override string ToString()
  136. {
  137. return ToString(string.Empty);
  138. }
  139. /// <summary>
  140. /// Outputs the string in a formatted manner
  141. /// </summary>
  142. /// <param name="prefix">A prefix to append, used internally</param>
  143. /// <param name="asQuerySting">produce a query string</param>
  144. public string ToString(string prefix, bool asQuerySting)
  145. {
  146. string name;
  147. if (string.IsNullOrEmpty(prefix))
  148. name = Name;
  149. else
  150. name = prefix + "[" + Name + "]";
  151. if (asQuerySting)
  152. {
  153. string temp;
  154. if (_values.Count == 0 && _items.Count > 0)
  155. temp = string.Empty;
  156. else
  157. temp = name;
  158. if (_values.Count > 0)
  159. {
  160. temp += '=';
  161. foreach (string value in _values)
  162. temp += value + ',';
  163. temp = temp.Remove(temp.Length - 1, 1);
  164. }
  165. foreach (KeyValuePair<string, HttpInputItem> item in _items)
  166. temp += item.Value.ToString(name, true) + '&';
  167. return _items.Count > 0 ? temp[..^1] : temp;
  168. }
  169. else
  170. {
  171. string temp = name;
  172. if (_values.Count > 0)
  173. {
  174. temp += " = ";
  175. foreach (string value in _values)
  176. temp += value + ", ";
  177. temp = temp.Remove(temp.Length - 2, 2);
  178. }
  179. temp += Environment.NewLine;
  180. foreach (KeyValuePair<string, HttpInputItem> item in _items)
  181. temp += item.Value.ToString(name, false);
  182. return temp;
  183. }
  184. }
  185. #region IHttpInput Members
  186. /// <summary>
  187. ///
  188. /// </summary>
  189. /// <param name="name">name in lower case</param>
  190. /// <returns></returns>
  191. HttpInputItem IHttpInput.this[string name]
  192. {
  193. get
  194. {
  195. return _items.TryGetValue(name, out HttpInputItem hii) ? hii : Empty;
  196. }
  197. }
  198. /// <summary>
  199. /// Add a sub item.
  200. /// </summary>
  201. /// <param name="name">Can contain array formatting, the item is then parsed and added in multiple levels</param>
  202. /// <param name="value">Value to add.</param>
  203. /// <exception cref="ArgumentNullException">Argument is null.</exception>
  204. /// <exception cref="InvalidOperationException">Cannot add stuff to <see cref="HttpInput.Empty"/>.</exception>
  205. public void Add(string name, string value)
  206. {
  207. if (name == null && value != null)
  208. throw new ArgumentNullException("name");
  209. if (name == null)
  210. return;
  211. if (_ignoreChanges)
  212. throw new InvalidOperationException("Cannot add stuff to HttpInput.Empty.");
  213. int pos = name.IndexOf('[');
  214. if (pos != -1)
  215. {
  216. string name1 = name[..pos];
  217. string name2 = HttpInput.ExtractOne(name);
  218. if (!_items.TryGetValue(name1, out HttpInputItem it))
  219. {
  220. it = new HttpInputItem(name1, null);
  221. _items.Add(name1, it);
  222. }
  223. it.Add(name2, value);
  224. /*
  225. HttpInputItem item = HttpInput.ParseItem(name, value);
  226. // Add the value to an existing sub item
  227. if (_items.ContainsKey(item.Name))
  228. _items[item.Name].Add(item.Value);
  229. else
  230. _items.Add(item.Name, item);
  231. */
  232. }
  233. else
  234. {
  235. if (_items.TryGetValue(name, out HttpInputItem it2))
  236. it2.Add(value);
  237. else
  238. _items.Add(name, new HttpInputItem(name, value));
  239. }
  240. }
  241. #endregion
  242. ///<summary>
  243. ///Returns an enumerator that iterates through the collection.
  244. ///</summary>
  245. ///
  246. ///<returns>
  247. ///A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
  248. ///</returns>
  249. ///<filterpriority>1</filterpriority>
  250. IEnumerator<HttpInputItem> IEnumerable<HttpInputItem>.GetEnumerator()
  251. {
  252. return _items.Values.GetEnumerator();
  253. }
  254. #region IEnumerable Members
  255. ///<summary>
  256. ///Returns an enumerator that iterates through a collection.
  257. ///</summary>
  258. ///
  259. ///<returns>
  260. ///An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
  261. ///</returns>
  262. ///<filterpriority>2</filterpriority>
  263. public IEnumerator GetEnumerator()
  264. {
  265. return _items.Values.GetEnumerator();
  266. }
  267. #endregion
  268. /// <summary>
  269. /// Outputs the string in a formatted manner
  270. /// </summary>
  271. /// <param name="prefix">A prefix to append, used internally</param>
  272. /// <returns></returns>
  273. public string ToString(string prefix)
  274. {
  275. return ToString(prefix, false);
  276. }
  277. }
  278. }