PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/ExpressionCollection.cs

https://bitbucket.org/danipen/mono
C# | 291 lines | 199 code | 50 blank | 42 comment | 73 complexity | 55aac9d6e38945c17805b0589dfb11aa MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. //
  2. // ExpressionCollection.cs
  3. //
  4. // Author:
  5. // Marek Sieradzki (marek.sieradzki@gmail.com)
  6. // Ankit Jain (jankit@novell.com)
  7. //
  8. // (C) 2006 Marek Sieradzki
  9. // Copyright 2009 Novell, Inc (http://www.novell.com)
  10. //
  11. // Permission is hereby granted, free of charge, to any person obtaining
  12. // a copy of this software and associated documentation files (the
  13. // "Software"), to deal in the Software without restriction, including
  14. // without limitation the rights to use, copy, modify, merge, publish,
  15. // distribute, sublicense, and/or sell copies of the Software, and to
  16. // permit persons to whom the Software is furnished to do so, subject to
  17. // the following conditions:
  18. //
  19. // The above copyright notice and this permission notice shall be
  20. // included in all copies or substantial portions of the Software.
  21. //
  22. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  25. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  26. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  27. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  28. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29. using System;
  30. using System.Collections;
  31. using System.Collections.Generic;
  32. using System.Text;
  33. using Microsoft.Build.Framework;
  34. using Microsoft.Build.Utilities;
  35. namespace Microsoft.Build.BuildEngine {
  36. internal class ExpressionCollection {
  37. IList objects;
  38. static Dictionary<string, bool> boolValues;
  39. static ExpressionCollection ()
  40. {
  41. string[] trueValuesArray = new string[] {"true", "on", "yes"};
  42. string[] falseValuesArray = new string[] {"false", "off", "no"};
  43. boolValues = new Dictionary<string, bool> (StringComparer.OrdinalIgnoreCase);
  44. foreach (string s in trueValuesArray)
  45. boolValues.Add (s, true);
  46. foreach (string s in falseValuesArray)
  47. boolValues.Add (s, false);
  48. }
  49. public ExpressionCollection ()
  50. {
  51. objects = new ArrayList ();
  52. }
  53. public int Count {
  54. get { return objects.Count; }
  55. }
  56. public void Add (IReference reference)
  57. {
  58. objects.Add (reference);
  59. }
  60. public void Add (string s)
  61. {
  62. objects.Add (s);
  63. }
  64. public object ConvertTo (Project project, Type type, ExpressionOptions options)
  65. {
  66. if (type.IsArray) {
  67. if (type == typeof (ITaskItem[]))
  68. return ConvertToITaskItemArray (project, options);
  69. else
  70. return ConvertToArray (project, type, options);
  71. } else {
  72. if (type == typeof (ITaskItem))
  73. return ConvertToITaskItem (project, options);
  74. else
  75. return ConvertToNonArray (project, type, options);
  76. }
  77. }
  78. public IEnumerator GetEnumerator ()
  79. {
  80. foreach (object o in objects)
  81. yield return o;
  82. }
  83. object ConvertToNonArray (Project project, Type type, ExpressionOptions options)
  84. {
  85. return ConvertToObject (ConvertToString (project, options), type, options);
  86. }
  87. object ConvertToArray (Project project, Type type, ExpressionOptions options)
  88. {
  89. ITaskItem[] items = ConvertToITaskItemArray (project, options);
  90. Type element_type = type.GetElementType ();
  91. Array arr = Array.CreateInstance (element_type, items.Length);
  92. for (int i = 0; i < arr.Length; i ++)
  93. arr.SetValue (ConvertToObject (items [i].ItemSpec, element_type, options), i);
  94. return arr;
  95. }
  96. object ConvertToObject (string raw, Type type, ExpressionOptions options)
  97. {
  98. if (type == typeof (bool)) {
  99. bool value;
  100. if (boolValues.TryGetValue (raw, out value))
  101. return value;
  102. else
  103. return false;
  104. }
  105. if (type == typeof (string))
  106. return raw;
  107. if (type.IsPrimitive)
  108. return Convert.ChangeType (raw, type);
  109. if (type == typeof (DateTime))
  110. return DateTime.Parse (raw);
  111. throw new Exception (String.Format ("Unsupported type: {0}", type));
  112. }
  113. string ConvertToString (Project project, ExpressionOptions options)
  114. {
  115. StringBuilder sb = new StringBuilder ();
  116. foreach (object o in objects) {
  117. string s = o as string;
  118. if (s != null) {
  119. sb.Append (s);
  120. continue;
  121. }
  122. IReference br = o as IReference;
  123. if (br != null)
  124. sb.Append (br.ConvertToString (project, options));
  125. else
  126. throw new Exception ("BUG: Invalid type in objects collection.");
  127. }
  128. return sb.ToString ();
  129. }
  130. ITaskItem ConvertToITaskItem (Project project, ExpressionOptions options)
  131. {
  132. if (objects == null)
  133. throw new Exception ("Cannot cast empty expression to ITaskItem.");
  134. ITaskItem[] items = ConvertToITaskItemArray (project, options);
  135. if (items.Length > 1)
  136. //FIXME: msbuild gives better errors
  137. throw new Exception (String.Format ("Exactly one item required, but got: {0}", items.Length));
  138. if (items.Length == 0) return null;
  139. return items [0];
  140. }
  141. // Concat rules (deduced)
  142. // - ItemRef can concat only with a string ';' or PropertyRef ending in ';'
  143. // - MetadataRef can concat with anything other than ItemRef
  144. // - PropertyRef cannot be right after a ItemRef
  145. // PropertyRef concats if it doesn't end in ';'
  146. // - string cannot concat with ItemRef unless it is ';'.
  147. // string concats if it ends in ';'
  148. ITaskItem[] ConvertToITaskItemArray (Project project, ExpressionOptions options)
  149. {
  150. List <ITaskItem> finalItems = new List <ITaskItem> ();
  151. object prev = null;
  152. bool prev_can_concat = false;
  153. foreach (object o in objects) {
  154. bool can_concat = prev_can_concat;
  155. string str = o as string;
  156. if (str != null) {
  157. string trimmed_str = str.Trim ();
  158. if (!IsSemicolon (str) && trimmed_str.Length > 0 && prev != null && prev is ItemReference)
  159. // non-empty, non-semicolon string after item ref
  160. ThrowCantConcatError (prev, str);
  161. if (trimmed_str.Length == 0 && prev is string && IsSemicolon ((string) prev)) {
  162. // empty string after a ';', ignore it
  163. continue;
  164. }
  165. // empty string _after_ a itemref, not an error
  166. prev_can_concat = !(str.Length > 0 && str [str.Length - 1] == ';') && trimmed_str.Length > 0;
  167. AddItemsToArray (finalItems,
  168. ConvertToITaskItemArrayFromString (str),
  169. can_concat);
  170. prev = o;
  171. continue;
  172. }
  173. IReference br = o as IReference;
  174. if (br == null)
  175. throw new Exception ("BUG: Invalid type in objects collection.");
  176. if (o is ItemReference) {
  177. if (prev != null && !(prev is string && (string)prev == ";"))
  178. ThrowCantConcatError (prev, br);
  179. prev_can_concat = true;
  180. } else if (o is MetadataReference) {
  181. if (prev != null && prev is ItemReference)
  182. ThrowCantConcatError (prev, br);
  183. prev_can_concat = true;
  184. } else if (o is PropertyReference) {
  185. if (prev != null && prev is ItemReference)
  186. ThrowCantConcatError (prev, br);
  187. string value = ((PropertyReference) o).GetValue (project);
  188. prev_can_concat = !(value.Length > 0 && value [value.Length - 1] == ';');
  189. }
  190. AddItemsToArray (finalItems, br.ConvertToITaskItemArray (project, options), can_concat);
  191. prev = o;
  192. }
  193. // Trim and Remove empty items
  194. List<ITaskItem> toRemove = new List<ITaskItem> ();
  195. for (int i = 0; i < finalItems.Count; i ++) {
  196. string s = finalItems [i].ItemSpec.Trim ();
  197. if (s.Length == 0)
  198. toRemove.Add (finalItems [i]);
  199. else
  200. finalItems [i].ItemSpec = s;
  201. }
  202. foreach (ITaskItem ti in toRemove)
  203. finalItems.Remove (ti);
  204. return finalItems.ToArray ();
  205. }
  206. // concat's first item in @items to last item in @list if @concat is true
  207. // else just adds all @items to @list
  208. void AddItemsToArray (List<ITaskItem> list, ITaskItem[] items, bool concat)
  209. {
  210. if (items == null || items.Length == 0)
  211. return;
  212. int start_index = 1;
  213. if (concat && list.Count > 0)
  214. list [list.Count - 1].ItemSpec += items [0].ItemSpec;
  215. else
  216. start_index = 0;
  217. for (int i = start_index; i < items.Length; i ++)
  218. list.Add (items [i]);
  219. }
  220. ITaskItem [] ConvertToITaskItemArrayFromString (string source)
  221. {
  222. List <ITaskItem> items = new List <ITaskItem> ();
  223. string [] splitSource = source.Split (new char [] {';'},
  224. StringSplitOptions.RemoveEmptyEntries);
  225. foreach (string s in splitSource)
  226. items.Add (new TaskItem (s));
  227. return items.ToArray ();
  228. }
  229. bool IsSemicolon (string str)
  230. {
  231. return str != null && str.Length == 1 && str [0] == ';';
  232. }
  233. void ThrowCantConcatError (object first, object second)
  234. {
  235. throw new Exception (String.Format (
  236. "Can't concatenate Item list with other strings where an item list is " +
  237. "expected ('{0}', '{1}'). Use semi colon to separate items.",
  238. first.ToString (), second.ToString ()));
  239. }
  240. }
  241. }