PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/AttackTrees/Model/Trees/Node.cs

https://bitbucket.org/sebastian_ebert/attacktrees
C# | 387 lines | 250 code | 46 blank | 91 comment | 26 complexity | a5c351af2992247a357ad65e2a997268 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Diagnostics.Contracts;
  6. using System.ComponentModel;
  7. using Model.Functions;
  8. using System.Collections.ObjectModel;
  9. namespace Model.Trees
  10. {
  11. /// <summary>
  12. /// Defines a single node in an attack tree. Connections are
  13. /// handled by the tree data structure and not stored in the
  14. /// nodes, so the node is only a plain data holder.
  15. /// </summary>
  16. public class Node : IEquatable<Node>, INotifyPropertyChanged, ICloneable
  17. {
  18. #region Fields
  19. private Guid _id;
  20. private string _name;
  21. private bool _isResult;
  22. private bool _isExpanded;
  23. private bool _isSelected;
  24. private bool _showsAttributes;
  25. private Dictionary<string, NodeAttribute> _attributes;
  26. private ObservableCollection<NodeAttribute> _nodeAttributes;
  27. #endregion
  28. #region Constructors & Finalizers
  29. /// <summary>
  30. /// Initializes a new instance of a node. Generates a new unique id for the
  31. /// node.
  32. /// </summary>
  33. /// <param name="name">The name of the node.</param>
  34. public Node(string name)
  35. : this(name, Guid.NewGuid())
  36. {
  37. }
  38. public Node(string name, Guid id)
  39. {
  40. // Name is required
  41. if (name == null)
  42. {
  43. throw new ArgumentNullException("name", "Name is required");
  44. }
  45. if (string.IsNullOrEmpty(name))
  46. {
  47. throw new ArgumentException("name", "Name can't be empty");
  48. }
  49. this.Name = name;
  50. // Guid is required
  51. if (id == null)
  52. {
  53. throw new ArgumentNullException("id", "Guid is required");
  54. }
  55. this.Id = id;
  56. this.Attributes = new Dictionary<string, NodeAttribute>();
  57. this.IsExpanded = true;
  58. this.IsResult = false;
  59. this.IsSelected = false;
  60. this.ShowsAttributes = true;
  61. }
  62. #endregion
  63. #region Events
  64. public event PropertyChangedEventHandler PropertyChanged;
  65. #endregion
  66. #region Properties
  67. /// <summary>
  68. /// Gets or sets the unique iderntifier of the node.
  69. /// </summary>
  70. public Guid Id
  71. {
  72. get { return _id; }
  73. set
  74. {
  75. _id = value;
  76. OnPropertyChanged(new PropertyChangedEventArgs("Id"));
  77. }
  78. }
  79. /// <summary>
  80. /// Gets or sets the nodes name.
  81. /// </summary>
  82. public string Name
  83. {
  84. get { return _name; }
  85. set
  86. {
  87. _name = value;
  88. OnPropertyChanged(new PropertyChangedEventArgs("Name"));
  89. }
  90. }
  91. /// <summary>
  92. /// Gets a dictionary of attributes of this node.
  93. /// </summary>
  94. public Dictionary<string, NodeAttribute> Attributes
  95. {
  96. get
  97. {
  98. if (this._attributes == null)
  99. {
  100. this._attributes = new Dictionary<string, NodeAttribute>();
  101. }
  102. return this._attributes;
  103. }
  104. private set
  105. {
  106. this._attributes = value;
  107. }
  108. }
  109. /// <summary>
  110. /// UI usage | Get a string within all Dictionary keys
  111. /// </summary>
  112. public string AttributesKeys { get; private set; }
  113. /// <summary>
  114. /// UI usage | Get a string within all Dictionary values
  115. /// </summary>
  116. public string AttributesValues { get; private set; }
  117. /// <summary>
  118. /// UI usage | Get NodeAttributes only from Dictionary
  119. /// </summary>
  120. public ObservableCollection<NodeAttribute> NodeAttributes
  121. {
  122. get
  123. {
  124. return this._nodeAttributes;
  125. }
  126. set
  127. {
  128. this._nodeAttributes = value;
  129. OnPropertyChanged(new PropertyChangedEventArgs("NodeAttributes"));
  130. }
  131. }
  132. /// <summary>
  133. /// Gets or sets if the node is part of the last
  134. /// function runs result.
  135. /// </summary>
  136. public bool IsResult
  137. {
  138. get { return _isResult; }
  139. set
  140. {
  141. _isResult = value;
  142. OnPropertyChanged(new PropertyChangedEventArgs("IsResult"));
  143. }
  144. }
  145. /// <summary>
  146. /// Gets or sets if this node should be shown as expanded or not (collapsed).
  147. /// </summary>
  148. public bool IsExpanded
  149. {
  150. get { return _isExpanded; }
  151. set
  152. {
  153. _isExpanded = value;
  154. OnPropertyChanged(new PropertyChangedEventArgs("IsExpanded"));
  155. }
  156. }
  157. /// <summary>
  158. /// Gets or sets if this node should be highlighted as selected.
  159. /// </summary>
  160. public bool IsSelected
  161. {
  162. get { return _isSelected; }
  163. set
  164. {
  165. _isSelected = value;
  166. OnPropertyChanged(new PropertyChangedEventArgs("IsSelected"));
  167. }
  168. }
  169. /// <summary>
  170. /// Gets or sets if this node should show its Attributes in UI
  171. /// </summary>
  172. public bool ShowsAttributes
  173. {
  174. get { return _showsAttributes; }
  175. set
  176. {
  177. _showsAttributes = value;
  178. OnPropertyChanged(new PropertyChangedEventArgs("ShowsAttributes"));
  179. }
  180. }
  181. #endregion
  182. #region Methods
  183. /// <summary>
  184. /// Adds a new numeric attribute to the node.
  185. /// </summary>
  186. /// <param name="name">Name of the attribute to add.</param>
  187. /// <param name="value">Value of the attribute to add.</param>
  188. public void AddAttribute(string name, Decimal value)
  189. {
  190. this.Attributes.Add(name, new NodeAttribute(name, value));
  191. this.AttributesToList();
  192. this.OnPropertyChanged(new PropertyChangedEventArgs("Attributes"));
  193. }
  194. /// <summary>
  195. /// Adds a new boolean attribute to the node.
  196. /// </summary>
  197. /// <param name="name">Name of the attribute to add.</param>
  198. /// <param name="value">Value of the attribute to add.</param>
  199. public void AddAttribute(string name, bool value)
  200. {
  201. this.Attributes.Add(name, new NodeAttribute(name, value));
  202. this.AttributesToList();
  203. this.OnPropertyChanged(new PropertyChangedEventArgs("Attributes"));
  204. }
  205. /// <summary>
  206. /// Adds a new attribute to the node.
  207. /// </summary>
  208. /// <param name="attribute">Attribute to add.</param>
  209. public void AddAttribute(NodeAttribute attribute)
  210. {
  211. this.Attributes.Add(attribute.Name, attribute);
  212. this.AttributesToList();
  213. this.OnPropertyChanged(new PropertyChangedEventArgs("Attributes"));
  214. }
  215. /// <summary>
  216. /// Changes the value of an already defined attribute.
  217. /// </summary>
  218. /// <param name="name">Name of attribute to change.</param>
  219. /// <param name="value">New value for attribute.</param>
  220. /// <exception cref="KeyNotFoundException">When the attribute is not defined.</exception>
  221. /// <exception cref="InvalidOperationException">When the attribute type is not boolean.</exception>
  222. public void SetAttributeValue(string name, bool value)
  223. {
  224. NodeAttribute attribute;
  225. if (Attributes.TryGetValue(name, out attribute))
  226. {
  227. if (attribute.Type != Types.Number)
  228. throw new InvalidOperationException("Attribute '" + name + "' is not boolean");
  229. attribute.SetValue(value);
  230. }
  231. else
  232. {
  233. throw new KeyNotFoundException("Property '" + name + "' is not defined");
  234. }
  235. }
  236. /// <summary>
  237. /// Changes the value of an already defined attribute.
  238. /// </summary>
  239. /// <param name="name">Name of attribute to change.</param>
  240. /// <param name="value">New value for attribute.</param>
  241. /// <exception cref="KeyNotFoundException">When the attribute is not defined.</exception>
  242. /// <exception cref="InvalidOperationException">When the attribute type is not numeric.</exception>
  243. public void SetAttributeValue(string name, decimal value)
  244. {
  245. NodeAttribute attribute;
  246. if (Attributes.TryGetValue(name, out attribute))
  247. {
  248. if (attribute.Type != Types.Number)
  249. throw new InvalidOperationException("Attribute '" + name + "' is not numeric");
  250. attribute.SetValue(value);
  251. }
  252. else
  253. {
  254. throw new KeyNotFoundException("Property '" + name + "' is not defined");
  255. }
  256. }
  257. /// <summary>
  258. /// Removes all attributes from this node.
  259. /// </summary>
  260. public void ClearAttributes()
  261. {
  262. this.Attributes.Clear();
  263. if (this.NodeAttributes != null)
  264. {
  265. this.NodeAttributes.Clear();
  266. }
  267. this.OnPropertyChanged(new PropertyChangedEventArgs("Attributes"));
  268. this.OnPropertyChanged(new PropertyChangedEventArgs("NodeAttributes"));
  269. }
  270. /// <summary>
  271. /// Updates all attributes of this node and makes sure
  272. /// PropertyChanged events are fired.
  273. /// </summary>
  274. public void UpdateAttributes()
  275. {
  276. this.AttributesToList();
  277. this.OnPropertyChanged(new PropertyChangedEventArgs("Attributes"));
  278. this.OnPropertyChanged(new PropertyChangedEventArgs("NodeAttributes"));
  279. }
  280. /// <summary>
  281. /// UI usage | generate List with NodeAttributes for UI nodes
  282. /// </summary>
  283. private void AttributesToList()
  284. {
  285. if (Attributes.Any())
  286. {
  287. if (this.NodeAttributes == null)
  288. this.NodeAttributes = new ObservableCollection<NodeAttribute>();
  289. this.NodeAttributes.Clear();
  290. foreach (var nodeAttribute in Attributes.Values)
  291. {
  292. this.NodeAttributes.Add(nodeAttribute);
  293. }
  294. }
  295. else if (this.NodeAttributes != null)
  296. {
  297. this.NodeAttributes.Clear();
  298. }
  299. }
  300. /// <summary>
  301. /// Fires the PropertyChanged event.
  302. /// </summary>
  303. /// <param name="args">Event arguments.</param>
  304. protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
  305. {
  306. if (PropertyChanged != null)
  307. PropertyChanged(this, args);
  308. }
  309. public bool Equals(Node other)
  310. {
  311. return this.Id.Equals(other.Id) && this.Name.Equals(other.Name);
  312. }
  313. public override string ToString()
  314. {
  315. return Name;
  316. }
  317. /// <summary>
  318. /// Creates a deep clone of this node.
  319. /// </summary>
  320. /// <returns>A deep clone of this node.</returns>
  321. public object Clone()
  322. {
  323. var clone = new Node(this.Name);
  324. // Don't copy flags
  325. clone.IsExpanded = true;
  326. clone.IsResult = false;
  327. clone.IsSelected = false;
  328. // Copy attributes
  329. clone.Attributes = new Dictionary<string, NodeAttribute>(this.Attributes.Count);
  330. foreach (var attribute in this.Attributes)
  331. {
  332. if (attribute.Value.Type == Types.Number)
  333. clone.AddAttribute(attribute.Key, attribute.Value.AsNumber());
  334. else
  335. clone.AddAttribute(attribute.Key, attribute.Value.AsBool());
  336. }
  337. return clone;
  338. }
  339. #endregion
  340. }
  341. }