/class/System.Windows/Mono.Xaml/MarkupExpressionParser.cs

https://github.com/andreiagaita/moon · C# · 457 lines · 343 code · 80 blank · 34 comment · 98 complexity · 7444c9de69d39f47b68010b8542250f1 MD5 · raw file

  1. //
  2. // MarkupExpressionParser.cs
  3. //
  4. // Contact:
  5. // Moonlight List (moonlight-list@lists.ximian.com)
  6. //
  7. // Copyright 2009 Novell, Inc.
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. using System;
  29. using System.Linq;
  30. using System.Text;
  31. using System.Windows;
  32. using System.Windows.Data;
  33. using System.Collections.Generic;
  34. using System.Windows.Markup;
  35. namespace Mono.Xaml {
  36. internal class SL3MarkupExpressionParser : MarkupExpressionParser {
  37. private IntPtr parser;
  38. private IntPtr target_data;
  39. public SL3MarkupExpressionParser (object target, string attribute_name, IntPtr parser, IntPtr target_data) : base (target, attribute_name)
  40. {
  41. this.parser = parser;
  42. this.target_data = target_data;
  43. }
  44. protected override object LookupNamedResource (DependencyObject dob, string name)
  45. {
  46. if (name == null)
  47. throw new XamlParseException ("you must specify a key in {StaticResource}");
  48. IntPtr value_ptr = NativeMethods.xaml_lookup_named_item (parser, target_data, name);
  49. object o = Value.ToObject (null, value_ptr);
  50. if (value_ptr != IntPtr.Zero)
  51. NativeMethods.value_delete_value2 (value_ptr);
  52. if (o == null && !parsingBinding)
  53. throw new XamlParseException (String.Format ("Resource '{0}' must be available as a static resource", name));
  54. return o;
  55. }
  56. protected override FrameworkTemplate GetParentTemplate ()
  57. {
  58. IntPtr template = NativeMethods.xaml_get_template_parent (parser, target_data);
  59. if (template == IntPtr.Zero)
  60. return null;
  61. INativeEventObjectWrapper dob = NativeDependencyObjectHelper.FromIntPtr (template);
  62. return dob as FrameworkTemplate;
  63. }
  64. }
  65. internal class SL4MarkupExpressionParser : MarkupExpressionParser {
  66. private XamlParser parser;
  67. private XamlObjectElement target_element;
  68. public SL4MarkupExpressionParser (object target, string attribute_name, XamlParser parser, XamlObjectElement target_element) : base (target, attribute_name)
  69. {
  70. this.parser = parser;
  71. this.target_element = target_element;
  72. }
  73. protected override object LookupNamedResource (DependencyObject dob, string name)
  74. {
  75. if (name == null)
  76. throw new XamlParseException ("you must specify a key in {StaticResource}");
  77. object o = parser.LookupNamedItem (target_element, name);
  78. if (o == null && !parsingBinding)
  79. throw new XamlParseException (String.Format ("Resource '{0}' must be available as a static resource", name));
  80. return o;
  81. }
  82. protected override FrameworkTemplate GetParentTemplate ()
  83. {
  84. return parser.GetParentTemplate (target_element);
  85. }
  86. }
  87. internal abstract class MarkupExpressionParser {
  88. private StringBuilder piece;
  89. private object target;
  90. private string attribute_name;
  91. protected bool parsingBinding;
  92. public MarkupExpressionParser ()
  93. {
  94. }
  95. public MarkupExpressionParser (object target, string attribute_name)
  96. {
  97. this.target = target;
  98. this.attribute_name = attribute_name;
  99. }
  100. public static bool IsTemplateBinding (string expression)
  101. {
  102. return MatchExpression ("TemplateBinding", expression);
  103. }
  104. public static bool IsStaticResource (string expression)
  105. {
  106. return MatchExpression ("StaticResource", expression);
  107. }
  108. public static bool IsBinding (string expression)
  109. {
  110. return MatchExpression ("Binding", expression);
  111. }
  112. public delegate object ExpressionHandler (ref string expression);
  113. public object ParseExpression (ref string expression)
  114. {
  115. if (expression.StartsWith ("{}"))
  116. return expression.Substring (2);
  117. object result = null;
  118. bool rv = false;
  119. if (!rv)
  120. rv = TryHandler ("Binding", ParseBinding, ref expression, out result);
  121. if (!rv)
  122. rv = TryHandler ("StaticResource", ParseStaticResource, ref expression, out result);
  123. if (!rv)
  124. rv = TryHandler ("TemplateBinding", ParseTemplateBinding, ref expression, out result);
  125. if (!rv)
  126. rv = TryHandler ("RelativeSource", ParseRelativeSource, ref expression, out result);
  127. return result;
  128. }
  129. private static bool MatchExpression (string match, string expression)
  130. {
  131. int dummy;
  132. return MatchExpression (match, expression, out dummy);
  133. }
  134. private static bool MatchExpression (string match, string expression, out int end)
  135. {
  136. if (expression.Length < 2) {
  137. end = 1;
  138. return false;
  139. }
  140. if (expression [0] != '{') {
  141. end = 2;
  142. return false;
  143. }
  144. int i;
  145. bool found = false;
  146. for (i = 1; i < expression.Length; i++) {
  147. if (expression [i] == ' ')
  148. continue;
  149. found = true;
  150. break;
  151. }
  152. if (!found) {
  153. end = 3;
  154. return false;
  155. }
  156. if (i + match.Length > expression.Length) {
  157. end = 4;
  158. return false;
  159. }
  160. int c;
  161. for (c = 0; c < match.Length; c++) {
  162. if (expression [i+c] == match [c])
  163. continue;
  164. end = 5;
  165. return false;
  166. }
  167. if (c != match.Length) {
  168. end = 6;
  169. return false;
  170. }
  171. end = i + c;
  172. return true;
  173. }
  174. private bool TryHandler (string match, ExpressionHandler handler, ref string expression, out object result)
  175. {
  176. int len;
  177. if (!MatchExpression (match, expression, out len)) {
  178. result = null;
  179. return false;
  180. }
  181. expression = expression.Substring (len).TrimStart ();
  182. if (expression.Length == 0)
  183. throw new Exception ("Expression did not end in '}'");
  184. result = handler (ref expression);
  185. return true;
  186. }
  187. public Binding ParseBinding (ref string expression)
  188. {
  189. Binding binding = new Binding ();
  190. parsingBinding = true;
  191. char next;
  192. if (expression [0] == '}')
  193. return binding;
  194. string remaining = expression;
  195. string piece = GetNextPiece (ref remaining, out next);
  196. if (next == '=')
  197. HandleProperty (binding, piece, ref remaining);
  198. else
  199. binding.Path = new PropertyPath (piece);
  200. while ((piece = GetNextPiece (ref remaining, out next)) != null) {
  201. HandleProperty (binding, piece, ref remaining);
  202. };
  203. parsingBinding = false;
  204. return binding;
  205. }
  206. public object ParseStaticResource (ref string expression)
  207. {
  208. if (!expression.EndsWith ("}"))
  209. throw new Exception ("Whitespace is not allowed after the end of the expression");
  210. char next;
  211. string name = GetNextPiece (ref expression, out next);
  212. object o = LookupNamedResource (null, name);
  213. #if !__TESTING
  214. if (o == null)
  215. o = Application.Current.Resources [name];
  216. #endif
  217. return o;
  218. }
  219. public object ParseTemplateBinding (ref string expression)
  220. {
  221. TemplateBindingExpression tb = new TemplateBindingExpression ();
  222. char next;
  223. string prop = GetNextPiece (ref expression, out next);
  224. /*FrameworkTemplate template = */GetParentTemplate ();
  225. tb.TargetPropertyName = attribute_name;
  226. tb.SourcePropertyName = prop;
  227. // tb.Source will be filled in elsewhere between attaching the change handler.
  228. return tb;
  229. }
  230. public object ParseRelativeSource (ref string expression)
  231. {
  232. char next;
  233. string mode_str = GetNextPiece (ref expression, out next);
  234. try {
  235. return new RelativeSource ((RelativeSourceMode) Enum.Parse (typeof (RelativeSourceMode), mode_str, true));
  236. } catch {
  237. throw new XamlParseException (String.Format ("MarkupExpressionParser: Error parsing RelativeSource, unknown mode: {0}", mode_str));
  238. }
  239. }
  240. private void HandleProperty (Binding b, string prop, ref string remaining)
  241. {
  242. char next;
  243. object value = null;
  244. string str_value = null;
  245. if (remaining.StartsWith ("{")) {
  246. value = ParseExpression (ref remaining);
  247. remaining = remaining.TrimStart ();
  248. if (remaining.Length > 0 && remaining[0] == ',')
  249. remaining = remaining.Substring (1);
  250. if (value is string)
  251. str_value = (string) value;
  252. }
  253. else {
  254. str_value = GetNextPiece (ref remaining, out next);
  255. }
  256. switch (prop) {
  257. case "FallbackValue":
  258. b.FallbackValue = value ?? str_value;
  259. break;
  260. case "Mode":
  261. if (str_value == null)
  262. throw new XamlParseException (String.Format ("Invalid type '{0}' for Mode.", value == null ? "null" : value.GetType ().ToString ()));
  263. b.Mode = (BindingMode) Enum.Parse (typeof (BindingMode), str_value, true);
  264. break;
  265. case "Path":
  266. if (str_value == null)
  267. throw new XamlParseException (String.Format ("Invalid type '{0}' for Path.", value == null ? "null" : value.GetType ().ToString ()));
  268. b.Path = new PropertyPath (str_value);
  269. break;
  270. case "Source":
  271. // if the expression was: Source="{StaticResource xxx}" then 'value' will be populated
  272. // If the expression was Source="5" then 'str_value' will be populated.
  273. b.Source = value ?? str_value;
  274. break;
  275. case "StringFormat":
  276. b.StringFormat = (string) value ?? str_value;
  277. break;
  278. case "Converter":
  279. IValueConverter value_converter = value as IValueConverter;
  280. if (value_converter == null && value != null)
  281. throw new Exception ("A Binding Converter must be of type IValueConverter.");
  282. b.Converter = value_converter;
  283. break;
  284. case "ConverterParameter":
  285. b.ConverterParameter = value ?? str_value;
  286. break;
  287. case "NotifyOnValidationError":
  288. bool bl;
  289. if (!Boolean.TryParse (str_value, out bl))
  290. throw new Exception (String.Format ("Invalid value {0} for NotifyValidationOnError.", str_value));
  291. b.NotifyOnValidationError = bl;
  292. break;
  293. case "TargetNullValue":
  294. b.TargetNullValue = value ?? str_value;
  295. break;
  296. case "ValidatesOnExceptions":
  297. if (!Boolean.TryParse (str_value, out bl))
  298. throw new Exception (String.Format ("Invalid value {0} for ValidatesOnExceptions.", str_value));
  299. b.ValidatesOnExceptions = bl;
  300. break;
  301. case "ValidatesOnDataErrors":
  302. if (!bool.TryParse (str_value, out bl))
  303. throw new Exception (string.Format ("Invalid value {0} for ValidatesOnDataErrors", str_value));
  304. b.ValidatesOnDataErrors = bl;
  305. break;
  306. case "ValidatesOnNotifyDataErrors":
  307. if (!bool.TryParse (str_value, out bl))
  308. throw new Exception (string.Format ("Invalid value {0} for ValidatesOnNotifyDataErrors", str_value));
  309. b.ValidatesOnNotifyDataErrors = bl;
  310. break;
  311. case "RelativeSource":
  312. RelativeSource rs = value as RelativeSource;
  313. if (rs == null)
  314. throw new Exception (String.Format ("Invalid value {0} for RelativeSource.", value));
  315. b.RelativeSource = rs;
  316. break;
  317. case "ElementName":
  318. b.ElementName = (string) value ?? str_value;
  319. break;
  320. case "UpdateSourceTrigger":
  321. b.UpdateSourceTrigger = (UpdateSourceTrigger) Enum.Parse (typeof (UpdateSourceTrigger), str_value, true);
  322. break;
  323. default:
  324. Console.Error.WriteLine ("Unhandled Binding Property: '{0}' value: {1}", prop, value != null ? value.ToString () : str_value);
  325. break;
  326. }
  327. }
  328. private string GetNextPiece (ref string remaining, out char next)
  329. {
  330. bool inString = false;
  331. int end = 0;
  332. remaining = remaining.TrimStart ();
  333. if (remaining.Length == 0) {
  334. next = Char.MaxValue;
  335. return null;
  336. }
  337. piece = piece ?? new StringBuilder ();
  338. piece.Length = 0;
  339. // If we're inside a quoted string we append all chars to our piece until we hit the ending quote.
  340. while (end < remaining.Length && (inString || (remaining [end] != '}' && remaining [end] != ',' && remaining [end] != '='))) {
  341. if (remaining [end] == '\'')
  342. inString = !inString;
  343. // If this is an escape char, consume it and append the next char to our piece.
  344. if (remaining [end] == '\\') {
  345. end ++;
  346. if (end == remaining.Length)
  347. break;;
  348. }
  349. piece.Append (remaining [end]);
  350. end++;
  351. }
  352. if (end == remaining.Length && !remaining.EndsWith ("}"))
  353. throw new Exception ("Binding did not end with '}'");
  354. if (end == 0) {
  355. next = Char.MaxValue;
  356. return null;
  357. }
  358. next = remaining [end];
  359. remaining = remaining.Substring (end + 1);
  360. // Whitespace is trimmed from the end of the piece before stripping
  361. // quote chars from the start/end of the string.
  362. while (piece.Length > 0 && char.IsWhiteSpace (piece [piece.Length - 1]))
  363. piece.Length --;
  364. if (piece.Length >= 2) {
  365. char first = piece [0];
  366. char last = piece [piece.Length - 1];
  367. if ((first == '\'' && last == '\'') || (first == '"' && last == '"')) {
  368. piece.Remove (piece.Length - 1, 1);
  369. piece.Remove (0, 1);
  370. }
  371. }
  372. return piece.ToString ();
  373. }
  374. protected abstract object LookupNamedResource (DependencyObject dob, string name);
  375. protected abstract FrameworkTemplate GetParentTemplate ();
  376. }
  377. }