/src/Caliburn.PresentationFramework/RoutedMessaging/Parsers/MessageParserBase.cs

http://caliburn.codeplex.com · C# · 197 lines · 130 code · 33 blank · 34 comment · 20 complexity · 3bf7f2564bfc2315334d4913b4ba0804 MD5 · raw file

  1. using System.Text.RegularExpressions;
  2. namespace Caliburn.PresentationFramework.RoutedMessaging.Parsers
  3. {
  4. using System;
  5. using System.Linq;
  6. using System.Windows;
  7. using System.Windows.Data;
  8. using Conventions;
  9. using RoutedMessaging;
  10. using Views;
  11. /// <summary>
  12. /// An base implementation of <see cref="IMessageParser"/>.
  13. /// </summary>
  14. public abstract class MessageParserBase<T> : IMessageParser
  15. where T : IRoutedMessage, new()
  16. {
  17. static readonly Regex Regex = new Regex(@",(?=(?:[^']*'[^']*')*(?![^']*'))");
  18. readonly IConventionManager conventionManager;
  19. readonly IMessageBinder messageBinder;
  20. #if !SILVERLIGHT
  21. private readonly UpdateSourceTrigger defaultTrigger;
  22. /// <summary>
  23. /// Initializes a new instance of the <see cref="MessageParserBase{T}"/> class.
  24. /// </summary>
  25. protected MessageParserBase(IConventionManager conventionManager, IMessageBinder messageBinder)
  26. : this(conventionManager, messageBinder, UpdateSourceTrigger.PropertyChanged) { }
  27. /// <summary>
  28. /// Initializes a new instance of the <see cref="MessageParserBase{T}"/> class.
  29. /// </summary>
  30. /// <param name="conventionManager">The convention manager.</param>
  31. /// <param name="messageBinder">The message binder.</param>
  32. /// <param name="defaultTrigger">The default trigger.</param>
  33. protected MessageParserBase(IConventionManager conventionManager, IMessageBinder messageBinder, UpdateSourceTrigger defaultTrigger)
  34. {
  35. this.conventionManager = conventionManager;
  36. this.messageBinder = messageBinder;
  37. this.defaultTrigger = defaultTrigger;
  38. }
  39. #else
  40. /// <summary>
  41. /// Initializes a new instance of the <see cref="MessageParserBase{T}"/> class.
  42. /// </summary>
  43. /// <param name="messageBinder">The message binder.</param>
  44. protected MessageParserBase(IConventionManager conventionManager, IMessageBinder messageBinder)
  45. {
  46. this.conventionManager = conventionManager;
  47. this.messageBinder = messageBinder;
  48. }
  49. #endif
  50. /// <summary>
  51. /// Parses the specified message text.
  52. /// </summary>
  53. /// <param name="target">The targeted UI element.</param>
  54. /// <param name="messageText">The message text.</param>
  55. /// <returns></returns>
  56. public IRoutedMessage Parse(DependencyObject target, string messageText)
  57. {
  58. var message = new T();
  59. var headAndTail = ParseHeadAndTail(messageText, message);
  60. var openingParenthesisIndex = headAndTail[0].IndexOf('(');
  61. if (openingParenthesisIndex < 0) openingParenthesisIndex = headAndTail[0].Length;
  62. var closingParenthesisIndex = headAndTail[0].LastIndexOf(')');
  63. if (closingParenthesisIndex < 0) closingParenthesisIndex = headAndTail[0].Length;
  64. var core = headAndTail[0].Substring(0, openingParenthesisIndex).Trim();
  65. SetCore(message, target, core);
  66. if (closingParenthesisIndex - openingParenthesisIndex > 1)
  67. {
  68. var paramString = headAndTail[0].Substring(openingParenthesisIndex + 1,
  69. closingParenthesisIndex - openingParenthesisIndex - 1);
  70. var parameters = Regex.Split(paramString);
  71. foreach (var parameter in parameters)
  72. {
  73. message.Parameters.Add(
  74. CreateParameter(target, parameter.Trim())
  75. );
  76. }
  77. }
  78. return message;
  79. }
  80. /// <summary>
  81. /// Sets the core value of the message.
  82. /// </summary>
  83. /// <param name="message">The message.</param>
  84. /// <param name="target">The target.</param>
  85. /// <param name="coreOfMessage">The core representation of the message.</param>
  86. protected abstract void SetCore(T message, DependencyObject target, string coreOfMessage);
  87. /// <summary>
  88. /// Parses the root portion of the message.
  89. /// </summary>
  90. /// <param name="messageText">The message text.</param>
  91. /// <param name="message">The message.</param>
  92. /// <returns></returns>
  93. protected virtual string[] ParseHeadAndTail(string messageText, T message)
  94. {
  95. int indexOfColon = messageText.LastIndexOf(':');
  96. if (indexOfColon == -1)
  97. return new[] { messageText.Trim() }; //no tail
  98. int indexOfParen = messageText.LastIndexOf(')');
  99. if (indexOfParen == -1)
  100. return messageText.Split(':').Select(x => x.Trim()).ToArray(); //no parameters
  101. if (indexOfColon < indexOfParen)
  102. return new[] { messageText.Trim() }; //no tail
  103. return new[]
  104. {
  105. messageText.Substring(0, indexOfColon).Trim(),
  106. messageText.Substring(indexOfColon + 1).Trim()
  107. };
  108. }
  109. Parameter CreateParameter(DependencyObject target, string parameter)
  110. {
  111. var actualParameter = new Parameter();
  112. if (parameter.StartsWith("'") && parameter.EndsWith("'"))
  113. actualParameter.Value = parameter.Substring(1, parameter.Length - 2);
  114. else if (messageBinder.IsSpecialValue(parameter) || char.IsNumber(parameter[0]))
  115. actualParameter.Value = parameter;
  116. else
  117. {
  118. View.ExecuteOnLoad(target, delegate {
  119. BindParameter(target, parameter, actualParameter);
  120. });
  121. }
  122. return actualParameter;
  123. }
  124. void BindParameter(DependencyObject target, string parameter, Parameter actualParameter)
  125. {
  126. #if !SILVERLIGHT
  127. var nameAndBindingMode = parameter.Split(':')
  128. .Select(x => x.Trim()).ToArray();
  129. var index = nameAndBindingMode[0].IndexOf('.');
  130. if (index == -1)
  131. actualParameter.Bind(target, parameter, null);
  132. else
  133. {
  134. var elementName = nameAndBindingMode[0].Substring(0, index);
  135. var path = new PropertyPath(nameAndBindingMode[0].Substring(index + 1));
  136. var binding = elementName == "$this"
  137. ? new Binding
  138. {
  139. Path = path,
  140. Source = target,
  141. UpdateSourceTrigger = defaultTrigger
  142. }
  143. : new Binding
  144. {
  145. Path = path,
  146. ElementName = elementName,
  147. UpdateSourceTrigger = defaultTrigger
  148. };
  149. if (nameAndBindingMode.Length == 2)
  150. binding.Mode = (BindingMode)Enum.Parse(typeof(BindingMode), nameAndBindingMode[1]);
  151. BindingOperations.SetBinding(actualParameter, Parameter.ValueProperty, binding);
  152. }
  153. #else
  154. var index = parameter.IndexOf('.');
  155. if(index > 0)
  156. {
  157. actualParameter.ElementName = parameter.Substring(0, index);
  158. actualParameter.Path = parameter.Substring(index + 1);
  159. }
  160. else actualParameter.ElementName = parameter;
  161. #endif
  162. }
  163. }
  164. }