PageRenderTime 55ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/Faxback/Interface/Interfaces.cs

https://bitbucket.org/docustream/newfaxback
C# | 232 lines | 101 code | 30 blank | 101 comment | 5 complexity | 268e3c78a4aa864cb42deffd16d1df48 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using FaxbackModel.Implementation.Logging;
  6. namespace Faxback.Interface
  7. {
  8. /// <summary>
  9. /// An interface for starting/stopping the emission of faxes.
  10. /// </summary>
  11. public interface IObservableFaxProcess
  12. {
  13. /// <summary>
  14. /// Initiates a process/thread.
  15. /// </summary>
  16. void Execute();
  17. void EnableProcess();
  18. /// <summary>
  19. /// Interrupts a process/thread.
  20. /// </summary>
  21. void InterruptProcess();
  22. /// <summary>
  23. /// Instance of a subject delegate in the subject/observer pattern. The implementer of this interface is the subject, those who call 'Subscribe' on this IReporter are the observers.
  24. /// </summary>
  25. IReporter Reporter { get; set; }
  26. /// <summary>
  27. /// Whether the observable process is still running.
  28. /// </summary>
  29. bool IsRunning { get; set; }
  30. }
  31. public class Message
  32. {
  33. public string Name { get; set; }
  34. public string Content { get; set; }
  35. public DateTime TimeStamp { get; } = DateTime.UtcNow;
  36. public static Func<Message, bool> MessageWithName(string name) => (m) => m.Name == name;
  37. public static Func<Message, bool> MessageWithNames(params string[] names) => (m) => names.Contains(m.Name);
  38. public override string ToString() => $"({Name}, {Content ?? "NULL"} at {TimeStamp.ToString()})";
  39. public static implicit operator Message((string Name, string Content) rhs)
  40. {
  41. return new Message { Name = rhs.Name, Content = rhs.Content };
  42. }
  43. }
  44. /// <summary>
  45. /// Subject delegate in the subject/observer pattern.
  46. /// </summary>
  47. public interface IReporter : IObservable<Message>
  48. {
  49. /// <summary>
  50. /// Update all observers with a new array of keys, so that they can act accordingly.
  51. /// </summary>
  52. /// <param name="messages"></param>
  53. void Update(params Message[] messages);
  54. }
  55. public class MultiChannelReporter : IReporter
  56. {
  57. /// <summary>
  58. /// Dictionary with the following hierarchy:
  59. /// Name of channel : {
  60. /// List of channel observers,
  61. /// Last refreshed message on channel
  62. /// }
  63. /// </summary>
  64. List<(string name, (List<(IObserver<Message> observer, bool picky)> pickyObservers, Message state) channel)> _namedChannels;
  65. public MultiChannelReporter(params string[] channelNames)
  66. {
  67. this._namedChannels =
  68. channelNames.Select(
  69. _ =>
  70. (
  71. // key is simply the string name itself
  72. name: _,
  73. // value is a tuple of message-observers list and last known message state.
  74. channel : (
  75. pickyObservers: new List<(IObserver<Message> observer, bool picky)>(),
  76. state: new Message { Name = _ }
  77. )
  78. )
  79. ).ToList();
  80. }
  81. public IDisposable Subscribe(IObserver<Message> observer) => Subscribe(observer, false, ((m) => true));
  82. public IDisposable Subscribe(IObserver<Message> observer, bool isPicky, Func<Message, bool> observationCriteria)
  83. {
  84. var subChannels = this._namedChannels
  85. .Where(k => observationCriteria(k.channel.state))
  86. .Select(k => k.channel)
  87. .ToList();
  88. subChannels.ForEach(s => s.pickyObservers.Add((observer, isPicky)));
  89. return new Unsubscriber<Message>(subChannels.Select(s => s.pickyObservers.Select(p => p.observer).ToList()), observer);
  90. }
  91. public void Update(params Message[] messages)
  92. {
  93. // update the necessary messages
  94. messages.ToList().ForEach(msg => {
  95. this._namedChannels
  96. .ToList()
  97. .ForEach(nch =>
  98. {
  99. if (msg.Name == nch.name)
  100. {
  101. var messageChanged = nch.channel.state.Content != msg.Content;
  102. nch.channel.state.Content = msg.Content;
  103. notifyNonPickys(nch, messageChanged);
  104. }
  105. });
  106. });
  107. }
  108. private static void notifyNonPickys((string name, (List<(IObserver<Message> observer, bool picky)> observers, Message state) channel) nch, bool messageChanged) =>
  109. nch.channel.observers
  110. .Where(obs => !obs.picky || messageChanged)
  111. .ToList()
  112. .ForEach(obs =>
  113. obs.observer.OnNext(nch.channel.state));
  114. }
  115. /// <summary>
  116. /// A client interface for emitting faxes, and updating state of a generic fax type with information on the state of the fax job.
  117. /// </summary>
  118. /// <typeparam name="TFax">A generic fax type.</typeparam>
  119. public interface IFaxClient<TFax>
  120. {
  121. /// <summary>
  122. /// Attempts to emit a generic fax.
  123. /// </summary>
  124. /// <param name="fax">A generic fax type.</param>
  125. void Send(TFax fax);
  126. /// <summary>
  127. /// Attempts to update the status regarding a generic fax.
  128. /// </summary>
  129. /// <param name="fax">A generic fax type.</param>
  130. void UpdateStatus(TFax fax);
  131. }
  132. /// <summary>
  133. /// Serves up a stream of generic objects types matching true for some a boolean query criterion (optional, results to (e) => true otherwise).
  134. /// </summary>
  135. /// <typeparam name="TQueryable">A generic queryable type.</typeparam>
  136. public interface IQueryableStream<TQueryable>
  137. {
  138. /// <summary>
  139. /// Retrieves the next generic queryable type from the stream matching the input expression.
  140. /// </summary>
  141. /// <param name="expression">A generic queryable type => boolean expression explaining which queryables may be picked up through this interface.</param>
  142. /// <returns>The next available generic queryable matching the input expression.</returns>
  143. TQueryable Next(Expression<Func<TQueryable, bool>> expression = null);
  144. /// <summary>
  145. /// Checks if there are any available generic queryables in the stream.
  146. /// </summary>
  147. /// <param name="expression">A generic queryable type => boolean expression explaining which queryables may be picked up through this interface.</param>
  148. /// <returns>True if there is an available generic queryable in the stream, false otherwise.</returns>
  149. bool HasNext(Expression<Func<TQueryable, bool>> expression = null);
  150. /// <summary>
  151. /// Throws away changes to the currently checked out record.
  152. /// </summary>
  153. void DiscardChanges();
  154. /// <summary>
  155. /// Number of elements in the current queryable stream
  156. /// </summary>
  157. int Count { get; }
  158. }
  159. /// <summary>
  160. /// A generic data-service for interfacing with a Data Access Layer to get lists of objects.
  161. /// It takes into account that new objects may be added to the Data Access Layer by other processes over
  162. /// time, and provides a means of refreshing the list accordingly. It does not however, provide any
  163. /// means of collision control between two entities trying to write to the DAL simultaneously.
  164. /// </summary>
  165. /// <typeparam name="TData">The generic type to be buffered by this data service.</typeparam>
  166. public interface IBufferedList<TData> : IList<TData>
  167. {
  168. /// <summary>
  169. /// Saves the data currently stored in the object back to the data access layer.
  170. /// </summary>
  171. void SaveBuffer();
  172. /// <summary>
  173. /// Saves objects in the buffered list which meet the predicate
  174. /// </summary>
  175. /// <param name="predicate">A control for whether an object should be saved or not.</param>
  176. void SaveBuffer(Func<TData, bool> predicate);
  177. /// <summary>
  178. /// Introduces elements into this BufferedList meeting the predicate criterion, And returns the subset of elements that matched, but not necessarily are "new" to the list itself.
  179. /// </summary>
  180. /// <param name="predicate">The filtering expression for objects that should </param>
  181. /// <returns></returns>
  182. void Refresh(Func<TData, bool> predicate);
  183. }
  184. //public interface IBufferedList<TData> : IList<TData>
  185. //{
  186. // /// <summary>
  187. // /// Saves the data currently stored in the object back to the data access layer.
  188. // /// </summary>
  189. // void SaveBuffer();
  190. // /// <summary>
  191. // /// Saves objects in the buffered list which meet the predicate
  192. // /// </summary>
  193. // /// <param name="predicate">A control for whether an object should be saved or not.</param>
  194. // void SaveBuffer(string predicate);
  195. // /// <summary>
  196. // /// Introduces elements into this BufferedList meeting the predicate criterion, And returns the subset of elements that matched, but not necessarily are "new" to the list itself.
  197. // /// </summary>
  198. // /// <param name="predicate">The filtering expression for objects that should </param>
  199. // /// <returns></returns>
  200. // void Refresh(string predicate);
  201. //}
  202. }