/AttackTrees/Model/Trees/Node.cs
C# | 387 lines | 250 code | 46 blank | 91 comment | 26 complexity | a5c351af2992247a357ad65e2a997268 MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Diagnostics.Contracts;
- using System.ComponentModel;
- using Model.Functions;
- using System.Collections.ObjectModel;
-
- namespace Model.Trees
- {
- /// <summary>
- /// Defines a single node in an attack tree. Connections are
- /// handled by the tree data structure and not stored in the
- /// nodes, so the node is only a plain data holder.
- /// </summary>
- public class Node : IEquatable<Node>, INotifyPropertyChanged, ICloneable
- {
-
- #region Fields
-
- private Guid _id;
- private string _name;
- private bool _isResult;
- private bool _isExpanded;
- private bool _isSelected;
- private bool _showsAttributes;
- private Dictionary<string, NodeAttribute> _attributes;
- private ObservableCollection<NodeAttribute> _nodeAttributes;
-
- #endregion
-
- #region Constructors & Finalizers
-
- /// <summary>
- /// Initializes a new instance of a node. Generates a new unique id for the
- /// node.
- /// </summary>
- /// <param name="name">The name of the node.</param>
- public Node(string name)
- : this(name, Guid.NewGuid())
- {
- }
-
- public Node(string name, Guid id)
- {
- // Name is required
- if (name == null)
- {
- throw new ArgumentNullException("name", "Name is required");
- }
- if (string.IsNullOrEmpty(name))
- {
- throw new ArgumentException("name", "Name can't be empty");
- }
- this.Name = name;
- // Guid is required
- if (id == null)
- {
- throw new ArgumentNullException("id", "Guid is required");
- }
- this.Id = id;
- this.Attributes = new Dictionary<string, NodeAttribute>();
- this.IsExpanded = true;
- this.IsResult = false;
- this.IsSelected = false;
- this.ShowsAttributes = true;
- }
-
- #endregion
-
- #region Events
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- #endregion
-
- #region Properties
-
- /// <summary>
- /// Gets or sets the unique iderntifier of the node.
- /// </summary>
- public Guid Id
- {
- get { return _id; }
- set
- {
- _id = value;
- OnPropertyChanged(new PropertyChangedEventArgs("Id"));
- }
- }
-
- /// <summary>
- /// Gets or sets the nodes name.
- /// </summary>
- public string Name
- {
- get { return _name; }
- set
- {
- _name = value;
- OnPropertyChanged(new PropertyChangedEventArgs("Name"));
- }
- }
-
- /// <summary>
- /// Gets a dictionary of attributes of this node.
- /// </summary>
- public Dictionary<string, NodeAttribute> Attributes
- {
- get
- {
- if (this._attributes == null)
- {
- this._attributes = new Dictionary<string, NodeAttribute>();
- }
- return this._attributes;
- }
-
- private set
- {
- this._attributes = value;
- }
-
- }
-
- /// <summary>
- /// UI usage | Get a string within all Dictionary keys
- /// </summary>
- public string AttributesKeys { get; private set; }
-
- /// <summary>
- /// UI usage | Get a string within all Dictionary values
- /// </summary>
- public string AttributesValues { get; private set; }
-
- /// <summary>
- /// UI usage | Get NodeAttributes only from Dictionary
- /// </summary>
- public ObservableCollection<NodeAttribute> NodeAttributes
- {
- get
- {
- return this._nodeAttributes;
- }
- set
- {
- this._nodeAttributes = value;
- OnPropertyChanged(new PropertyChangedEventArgs("NodeAttributes"));
- }
- }
-
- /// <summary>
- /// Gets or sets if the node is part of the last
- /// function runs result.
- /// </summary>
- public bool IsResult
- {
- get { return _isResult; }
- set
- {
- _isResult = value;
- OnPropertyChanged(new PropertyChangedEventArgs("IsResult"));
- }
- }
-
- /// <summary>
- /// Gets or sets if this node should be shown as expanded or not (collapsed).
- /// </summary>
- public bool IsExpanded
- {
- get { return _isExpanded; }
- set
- {
- _isExpanded = value;
- OnPropertyChanged(new PropertyChangedEventArgs("IsExpanded"));
- }
- }
-
- /// <summary>
- /// Gets or sets if this node should be highlighted as selected.
- /// </summary>
- public bool IsSelected
- {
- get { return _isSelected; }
- set
- {
- _isSelected = value;
- OnPropertyChanged(new PropertyChangedEventArgs("IsSelected"));
- }
- }
-
- /// <summary>
- /// Gets or sets if this node should show its Attributes in UI
- /// </summary>
- public bool ShowsAttributes
- {
- get { return _showsAttributes; }
- set
- {
- _showsAttributes = value;
- OnPropertyChanged(new PropertyChangedEventArgs("ShowsAttributes"));
- }
- }
-
- #endregion
-
- #region Methods
-
- /// <summary>
- /// Adds a new numeric attribute to the node.
- /// </summary>
- /// <param name="name">Name of the attribute to add.</param>
- /// <param name="value">Value of the attribute to add.</param>
- public void AddAttribute(string name, Decimal value)
- {
- this.Attributes.Add(name, new NodeAttribute(name, value));
- this.AttributesToList();
- this.OnPropertyChanged(new PropertyChangedEventArgs("Attributes"));
- }
-
- /// <summary>
- /// Adds a new boolean attribute to the node.
- /// </summary>
- /// <param name="name">Name of the attribute to add.</param>
- /// <param name="value">Value of the attribute to add.</param>
- public void AddAttribute(string name, bool value)
- {
- this.Attributes.Add(name, new NodeAttribute(name, value));
- this.AttributesToList();
- this.OnPropertyChanged(new PropertyChangedEventArgs("Attributes"));
- }
-
- /// <summary>
- /// Adds a new attribute to the node.
- /// </summary>
- /// <param name="attribute">Attribute to add.</param>
- public void AddAttribute(NodeAttribute attribute)
- {
- this.Attributes.Add(attribute.Name, attribute);
- this.AttributesToList();
- this.OnPropertyChanged(new PropertyChangedEventArgs("Attributes"));
- }
-
- /// <summary>
- /// Changes the value of an already defined attribute.
- /// </summary>
- /// <param name="name">Name of attribute to change.</param>
- /// <param name="value">New value for attribute.</param>
- /// <exception cref="KeyNotFoundException">When the attribute is not defined.</exception>
- /// <exception cref="InvalidOperationException">When the attribute type is not boolean.</exception>
- public void SetAttributeValue(string name, bool value)
- {
- NodeAttribute attribute;
- if (Attributes.TryGetValue(name, out attribute))
- {
- if (attribute.Type != Types.Number)
- throw new InvalidOperationException("Attribute '" + name + "' is not boolean");
- attribute.SetValue(value);
- }
- else
- {
- throw new KeyNotFoundException("Property '" + name + "' is not defined");
- }
- }
-
-
- /// <summary>
- /// Changes the value of an already defined attribute.
- /// </summary>
- /// <param name="name">Name of attribute to change.</param>
- /// <param name="value">New value for attribute.</param>
- /// <exception cref="KeyNotFoundException">When the attribute is not defined.</exception>
- /// <exception cref="InvalidOperationException">When the attribute type is not numeric.</exception>
- public void SetAttributeValue(string name, decimal value)
- {
- NodeAttribute attribute;
- if (Attributes.TryGetValue(name, out attribute))
- {
- if (attribute.Type != Types.Number)
- throw new InvalidOperationException("Attribute '" + name + "' is not numeric");
- attribute.SetValue(value);
- }
- else
- {
- throw new KeyNotFoundException("Property '" + name + "' is not defined");
- }
- }
-
- /// <summary>
- /// Removes all attributes from this node.
- /// </summary>
- public void ClearAttributes()
- {
- this.Attributes.Clear();
- if (this.NodeAttributes != null)
- {
- this.NodeAttributes.Clear();
- }
- this.OnPropertyChanged(new PropertyChangedEventArgs("Attributes"));
- this.OnPropertyChanged(new PropertyChangedEventArgs("NodeAttributes"));
- }
-
- /// <summary>
- /// Updates all attributes of this node and makes sure
- /// PropertyChanged events are fired.
- /// </summary>
- public void UpdateAttributes()
- {
- this.AttributesToList();
- this.OnPropertyChanged(new PropertyChangedEventArgs("Attributes"));
- this.OnPropertyChanged(new PropertyChangedEventArgs("NodeAttributes"));
- }
-
- /// <summary>
- /// UI usage | generate List with NodeAttributes for UI nodes
- /// </summary>
- private void AttributesToList()
- {
- if (Attributes.Any())
- {
- if (this.NodeAttributes == null)
- this.NodeAttributes = new ObservableCollection<NodeAttribute>();
-
- this.NodeAttributes.Clear();
- foreach (var nodeAttribute in Attributes.Values)
- {
- this.NodeAttributes.Add(nodeAttribute);
- }
- }
- else if (this.NodeAttributes != null)
- {
- this.NodeAttributes.Clear();
- }
- }
-
- /// <summary>
- /// Fires the PropertyChanged event.
- /// </summary>
- /// <param name="args">Event arguments.</param>
- protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
- {
- if (PropertyChanged != null)
- PropertyChanged(this, args);
- }
-
- public bool Equals(Node other)
- {
- return this.Id.Equals(other.Id) && this.Name.Equals(other.Name);
- }
-
- public override string ToString()
- {
- return Name;
- }
-
-
- /// <summary>
- /// Creates a deep clone of this node.
- /// </summary>
- /// <returns>A deep clone of this node.</returns>
- public object Clone()
- {
- var clone = new Node(this.Name);
-
- // Don't copy flags
- clone.IsExpanded = true;
- clone.IsResult = false;
- clone.IsSelected = false;
-
- // Copy attributes
- clone.Attributes = new Dictionary<string, NodeAttribute>(this.Attributes.Count);
- foreach (var attribute in this.Attributes)
- {
- if (attribute.Value.Type == Types.Number)
- clone.AddAttribute(attribute.Key, attribute.Value.AsNumber());
- else
- clone.AddAttribute(attribute.Key, attribute.Value.AsBool());
- }
-
- return clone;
- }
-
- #endregion
-
- }
- }