885 lines
26 KiB
C#
885 lines
26 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq;
|
|
using JD.Collections;
|
|
using JD.Kernel.Structs;
|
|
using JD.LogService;
|
|
using JD.Reflection.MefImplementations.Hosting.Sorting;
|
|
using JD.TeaBoxAddinSystem.Addins.Description;
|
|
using JD.TeaBoxAddinSystem.Addins.Description.Conditions;
|
|
|
|
namespace JD.TeaBoxAddinSystem.Addins.Internal
|
|
{
|
|
/// <summary>
|
|
/// A node in the extension tree.
|
|
/// </summary>
|
|
public class TreeNode : IEnumerable<TreeNode>
|
|
{
|
|
#region Inner Types
|
|
|
|
private class TreeNodeCollection : Collection<TreeNode>
|
|
{
|
|
#region Inner Types
|
|
|
|
private class TreeNodeTopologicalSort : TopologicalSort<string, TreeNode>
|
|
{
|
|
#region Constructor
|
|
|
|
public TreeNodeTopologicalSort(IEnumerable<TreeNode> originalList)
|
|
: base(originalList)
|
|
{
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
protected override bool TryGetKeyForItem(TreeNode item, out string key)
|
|
{
|
|
System.Diagnostics.Debug.Assert(item != null);
|
|
key = item.Id;
|
|
return true;
|
|
}
|
|
|
|
protected override string GetDisplayNameForItem(TreeNode item)
|
|
{
|
|
return item.Id;
|
|
}
|
|
|
|
protected override IEnumerable<string> GetItemsAfterForItem(TreeNode item)
|
|
{
|
|
System.Diagnostics.Debug.Assert(item != null);
|
|
|
|
if (item.m_nodesAfter != null)
|
|
{
|
|
return item.m_nodesAfter;
|
|
}
|
|
|
|
return new string[0];
|
|
}
|
|
|
|
protected override IEnumerable<string> GetItemsBeforeForItem(TreeNode item)
|
|
{
|
|
System.Diagnostics.Debug.Assert(item != null);
|
|
|
|
if (item.m_nodesBefore != null)
|
|
{
|
|
return item.m_nodesBefore;
|
|
}
|
|
|
|
return new string[0];
|
|
}
|
|
|
|
private static bool IsAll(IEnumerable<string> nodes)
|
|
{
|
|
if (nodes == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const string AllString = "*";
|
|
return nodes.Any(node => node == AllString);
|
|
}
|
|
|
|
protected override TopologicalSortItemPositioningType GetItemPositioningType(TreeNode item)
|
|
{
|
|
System.Diagnostics.Debug.Assert(item != null);
|
|
|
|
bool beforeAll = IsAll(item.m_nodesAfter);
|
|
bool afterAll = IsAll(item.m_nodesBefore);
|
|
|
|
if (afterAll && beforeAll)
|
|
{
|
|
Log(new Error(LogCategory.Warning, "Cannot have both insertafter and insertbefore set to '*'") { Origin = item.FileLocation });
|
|
return TopologicalSortItemPositioningType.AfterAll;
|
|
}
|
|
|
|
if (afterAll)
|
|
{
|
|
return TopologicalSortItemPositioningType.AfterAll;
|
|
}
|
|
|
|
if (beforeAll)
|
|
{
|
|
return TopologicalSortItemPositioningType.BeforeAll;
|
|
}
|
|
|
|
return base.GetItemPositioningType(item);
|
|
}
|
|
|
|
protected override void Log(LogEvent @event)
|
|
{
|
|
AddinManager.Log(@event);
|
|
}
|
|
|
|
protected override FileLocation GetFileLocation(TreeNode item)
|
|
{
|
|
return item.FileLocation;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Variables
|
|
|
|
private readonly TreeNode m_parent;
|
|
private bool m_dirty = true; // Dirty flag.
|
|
private readonly object m_lock = new object();
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
public TreeNodeCollection(TreeNode parent)
|
|
{
|
|
System.Diagnostics.Debug.Assert(parent != null);
|
|
m_parent = parent;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
private void SetDirty()
|
|
{
|
|
m_dirty = true;
|
|
}
|
|
|
|
internal void Sort()
|
|
{
|
|
if (m_dirty)
|
|
{
|
|
lock (m_lock)
|
|
{
|
|
if (m_dirty)
|
|
{
|
|
List<TreeNode> originalList = new List<TreeNode>(this);
|
|
base.ClearItems();
|
|
foreach (TreeNode node in new TreeNodeTopologicalSort(originalList).Sort())
|
|
{
|
|
base.InsertItem(Count, node);
|
|
}
|
|
m_dirty = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void InsertItem(int index, TreeNode item)
|
|
{
|
|
item.Parent = m_parent;
|
|
base.InsertItem(index, item);
|
|
SetDirty();
|
|
}
|
|
|
|
protected override void SetItem(int index, TreeNode item)
|
|
{
|
|
item.Parent = m_parent;
|
|
base.SetItem(index, item);
|
|
SetDirty();
|
|
}
|
|
|
|
protected override void RemoveItem(int index)
|
|
{
|
|
base.RemoveItem(index);
|
|
SetDirty();
|
|
}
|
|
|
|
protected override void ClearItems()
|
|
{
|
|
base.ClearItems();
|
|
SetDirty();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper class, used for tests.
|
|
/// </summary>
|
|
public class TreeNodeBuilder
|
|
{
|
|
#region Variables
|
|
|
|
private readonly TreeNode m_treeNode;
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
/// <summary>
|
|
/// Constructor.
|
|
/// </summary>
|
|
public TreeNodeBuilder()
|
|
{
|
|
m_treeNode = new TreeNode();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor.
|
|
/// </summary>
|
|
/// <param name="id"></param>
|
|
public TreeNodeBuilder(string id)
|
|
{
|
|
m_treeNode = new TreeNode(id);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// TreeNode.
|
|
/// </summary>
|
|
public TreeNode TreeNode
|
|
{
|
|
get
|
|
{
|
|
return m_treeNode;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Child Nodes.
|
|
/// </summary>
|
|
public IList<TreeNode> ChildNodes
|
|
{
|
|
get
|
|
{
|
|
return TreeNode.ChildNodesInternal;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Variables
|
|
|
|
/// <summary>
|
|
/// Prefix used to auto-generate ids for tree nodes.
|
|
/// </summary>
|
|
internal const string AutoIdPrefix = "__auto_node_id__";
|
|
private static int m_autoIdGenerator;
|
|
private static readonly ExtensionNode m_dummyNode = new ExtensionNode();
|
|
|
|
private readonly string m_id;
|
|
private readonly TreeNodeCollection m_childNodes;
|
|
|
|
private TreeNode m_parent;
|
|
private ExtensionNodeDescription m_extensionNodeDescription;
|
|
private IExtensionTypeSetContainer m_allowedTypes;
|
|
private bool? m_isValid; // Whether the conditions evaluated on this node are all true.
|
|
|
|
// Only valid if there is an extension node description for this node.
|
|
// Used for sorting
|
|
private readonly List<string> m_nodesBefore = new List<string>();
|
|
private readonly List<string> m_nodesAfter = new List<string>();
|
|
|
|
private ExtensionNode m_extensionNode;
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
/// <summary>
|
|
/// Constructor.
|
|
/// </summary>
|
|
public TreeNode()
|
|
{
|
|
m_id = string.Empty;
|
|
m_childNodes = new TreeNodeCollection(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor.
|
|
/// </summary>
|
|
/// <param name="id"></param>
|
|
public TreeNode(string id)
|
|
{
|
|
if (string.IsNullOrEmpty(id))
|
|
{
|
|
id = GenerateAutoId();
|
|
}
|
|
else if (id.IndexOfAny(ExtensionPoint.GetPathSeparators()) != -1)
|
|
{
|
|
throw new ArgumentException("Id cannot contain path separator chars.");
|
|
}
|
|
|
|
m_id = id;
|
|
m_childNodes = new TreeNodeCollection(this);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Id of the node.
|
|
/// </summary>
|
|
public string Id
|
|
{
|
|
get
|
|
{
|
|
return m_id;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets whether the <see cref="Id"/> of this tree node was defined by the user or was automatically generated.
|
|
/// </summary>
|
|
public bool HasIdDefined
|
|
{
|
|
get
|
|
{
|
|
return !m_id.StartsWith(AutoIdPrefix);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Child Nodes.
|
|
/// </summary>
|
|
public IList<TreeNode> ChildNodes
|
|
{
|
|
get
|
|
{
|
|
// Make sure the nodes are sorted
|
|
m_childNodes.Sort();
|
|
return new ReadOnlyCollection<TreeNode>(m_childNodes);
|
|
}
|
|
}
|
|
|
|
internal IList<TreeNode> ChildNodesInternal
|
|
{
|
|
get
|
|
{
|
|
return m_childNodes;
|
|
}
|
|
}
|
|
|
|
internal FileLocation FileLocation
|
|
{
|
|
get
|
|
{
|
|
if (ExtensionNodeDescription != null)
|
|
{
|
|
return ExtensionNodeDescription.FileLocation.GetValueOrDefault();
|
|
}
|
|
else
|
|
{
|
|
ExtensionPoint extensionPoint = ExtensionPoint;
|
|
if (extensionPoint != null)
|
|
{
|
|
return extensionPoint.FileLocation.GetValueOrDefault();
|
|
}
|
|
else
|
|
{
|
|
return FileLocation.Empty;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a child tree node by its index.
|
|
/// </summary>
|
|
/// <param name="index"></param>
|
|
/// <returns>The node at the index</returns>
|
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when the index is not in the node bounds.</exception>
|
|
public TreeNode this[int index]
|
|
{
|
|
get
|
|
{
|
|
return ChildNodesInternal[index];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a child tree node by its id.
|
|
/// </summary>
|
|
/// <param name="id"></param>
|
|
/// <returns>The node if existing, null otherwise</returns>
|
|
public TreeNode this[string id]
|
|
{
|
|
get
|
|
{
|
|
return GetNode(ExtensionPoint.NormalizePath(id), false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parent Tree node, if any.
|
|
/// </summary>
|
|
public TreeNode Parent
|
|
{
|
|
get
|
|
{
|
|
return m_parent;
|
|
}
|
|
internal set
|
|
{
|
|
System.Diagnostics.Debug.Assert(m_parent == null || value == null);
|
|
m_parent = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Full path of the tree node.
|
|
/// </summary>
|
|
public string FullPath
|
|
{
|
|
get
|
|
{
|
|
int num = 0;
|
|
TreeNode node = this;
|
|
while (node != null)
|
|
{
|
|
if (!string.IsNullOrEmpty(node.Id))
|
|
{
|
|
num++;
|
|
}
|
|
node = node.Parent;
|
|
}
|
|
|
|
string[] ids = new string[num];
|
|
|
|
node = this;
|
|
while (node != null)
|
|
{
|
|
if (!string.IsNullOrEmpty(node.Id))
|
|
{
|
|
ids[--num] = node.Id;
|
|
}
|
|
node = node.Parent;
|
|
}
|
|
|
|
return string.Join(ExtensionPoint.PathSeparator.ToString(), ids);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension Point. Mutually exclusive with <see cref="ExtensionNodeDescription"/>.
|
|
/// </summary>
|
|
public ExtensionPoint ExtensionPoint
|
|
{
|
|
get
|
|
{
|
|
return AllowedTypes as ExtensionPoint;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension node description. Mutually exclusive with <see cref="ExtensionPoint"/>.
|
|
/// </summary>
|
|
public ExtensionNodeDescription ExtensionNodeDescription
|
|
{
|
|
get
|
|
{
|
|
return m_extensionNodeDescription;
|
|
}
|
|
internal set
|
|
{
|
|
System.Diagnostics.Debug.Assert(m_extensionNodeDescription == null ^ value == null);
|
|
System.Diagnostics.Debug.Assert(ExtensionPoint == null || value == null);
|
|
m_extensionNodeDescription = value;
|
|
}
|
|
}
|
|
|
|
internal IExtensionTypeSetContainer AllowedTypes
|
|
{
|
|
get
|
|
{
|
|
return m_allowedTypes;
|
|
}
|
|
set
|
|
{
|
|
System.Diagnostics.Debug.Assert(m_allowedTypes == null ^ value == null);
|
|
m_allowedTypes = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the extension node corresponding to this tree node. Creates it if necessary.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Returns null if a condition on this node evaluates to false.
|
|
/// </remarks>
|
|
public ExtensionNode ExtensionNode
|
|
{
|
|
get
|
|
{
|
|
if (!IsValid)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (m_extensionNode == null)
|
|
{
|
|
if (m_extensionNodeDescription != null)
|
|
{
|
|
// Semi-hack
|
|
System.Diagnostics.Debug.Assert(AllowedTypes is ExtensionType);
|
|
m_extensionNode = CreateExtensionNode(m_extensionNodeDescription, (ExtensionType)AllowedTypes);
|
|
|
|
if (m_extensionNode == null)
|
|
{
|
|
m_extensionNode = m_dummyNode;
|
|
}
|
|
else
|
|
{
|
|
ApplyAllConditionTarget();
|
|
}
|
|
}
|
|
else if (ExtensionPoint != null)
|
|
{
|
|
// Create a dummy node that represents the extension point.
|
|
var tmp = new ExtensionNode();
|
|
tmp.Initialize(this, ExtensionPoint.Module, null, null);
|
|
m_extensionNode = tmp;
|
|
}
|
|
}
|
|
|
|
if (m_extensionNode == m_dummyNode)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return m_extensionNode;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether the <see cref="ExtensionNode"/> on this tree node has been loaded.
|
|
/// </summary>
|
|
public bool IsLoaded
|
|
{
|
|
get
|
|
{
|
|
return (m_extensionNode != null);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets whether the conditions on this node evaluate to true.
|
|
/// </summary>
|
|
private bool IsValid
|
|
{
|
|
get
|
|
{
|
|
// Check parent
|
|
if (Parent != null && !Parent.IsValid)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!m_isValid.HasValue)
|
|
{
|
|
m_isValid = ComputeIsValid(null);
|
|
}
|
|
|
|
System.Diagnostics.Debug.Assert(m_isValid.HasValue);
|
|
return m_isValid.Value;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
internal void Clear()
|
|
{
|
|
ChildNodesInternal.Clear();
|
|
m_extensionNodeDescription = null;
|
|
m_allowedTypes = null;
|
|
m_nodesBefore.Clear();
|
|
m_nodesAfter.Clear();
|
|
m_extensionNode = null;
|
|
}
|
|
|
|
internal void LoadExtension(Extension extension)
|
|
{
|
|
System.Diagnostics.Debug.Assert(extension != null);
|
|
if (LoadExtensionNodeDescriptions(extension.ExtensionNodes))
|
|
{
|
|
// Tell the extension node if it is created
|
|
OnChanged();
|
|
}
|
|
}
|
|
|
|
private bool LoadExtensionNodeDescriptions(IEnumerable<ExtensionNodeDescription> nodes)
|
|
{
|
|
System.Diagnostics.Debug.Assert(nodes != null);
|
|
|
|
TreeNode lastNode = null;
|
|
bool changed = false;
|
|
foreach (ExtensionNodeDescription description in nodes)
|
|
{
|
|
TreeNode currentNode = LoadExtensionNodeDescription(description);
|
|
|
|
if (currentNode != null)
|
|
{
|
|
changed = true;
|
|
|
|
if (lastNode != null)
|
|
{
|
|
currentNode.m_nodesBefore.Add(lastNode.Id);
|
|
}
|
|
|
|
lastNode = currentNode;
|
|
}
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
private TreeNode LoadExtensionNodeDescription(ExtensionNodeDescription description)
|
|
{
|
|
// Make sure this node type is valid here
|
|
ExtensionType type = AddinManager.SessionService.FindExtensionType(AllowedTypes.ExtensionTypeSet, description.Name);
|
|
|
|
if (type == null)
|
|
{
|
|
description.LogWarning("Cannot load extension node {0} because the type {1} is not valid at path {2}",
|
|
description.DisplayId,
|
|
description.Name,
|
|
FullPath);
|
|
|
|
return null;
|
|
}
|
|
|
|
TreeNode childNode = new TreeNode(description.Id);
|
|
childNode.ExtensionNodeDescription = description;
|
|
childNode.AllowedTypes = type;
|
|
|
|
// Set insert before and insert after
|
|
const char InsertionSeparator = ',';
|
|
if (!string.IsNullOrEmpty(description.InsertAfter))
|
|
{
|
|
childNode.m_nodesBefore.AddRange(description.InsertAfter.Split(new char[] { InsertionSeparator }, StringSplitOptions.RemoveEmptyEntries));
|
|
}
|
|
if (!string.IsNullOrEmpty(description.InsertBefore))
|
|
{
|
|
childNode.m_nodesAfter.AddRange(description.InsertBefore.Split(new char[] { InsertionSeparator }, StringSplitOptions.RemoveEmptyEntries));
|
|
}
|
|
|
|
// Register conditions
|
|
AddinManager.SessionService.ConditionManager.RegisterTreeNode(childNode);
|
|
|
|
ChildNodesInternal.Add(childNode);
|
|
|
|
// Load child nodes
|
|
childNode.LoadExtensionNodeDescriptions(description.ChildNodes);
|
|
|
|
return childNode;
|
|
}
|
|
|
|
internal void UnloadExtension(Extension extension)
|
|
{
|
|
System.Diagnostics.Debug.Assert(extension != null);
|
|
|
|
bool changed = false;
|
|
foreach (ExtensionNodeDescription description in extension.ExtensionNodes)
|
|
{
|
|
changed |= UnloadExtensionNodeDescription(description);
|
|
}
|
|
|
|
if (changed)
|
|
{
|
|
// Tell the extension node if it is created
|
|
OnChanged();
|
|
}
|
|
}
|
|
|
|
private bool UnloadExtensionNodeDescription(ExtensionNodeDescription description)
|
|
{
|
|
bool changed = false;
|
|
foreach (TreeNode childNode in new List<TreeNode>(m_childNodes))
|
|
{
|
|
// No assert here, extension node is not necessarily loaded because of type errors, etc.
|
|
if (childNode.ExtensionNodeDescription == description)
|
|
{
|
|
AddinManager.SessionService.ConditionManager.UnregisterTreeNode(childNode);
|
|
|
|
foreach (ExtensionNodeDescription childDescription in description.ChildNodes)
|
|
{
|
|
childNode.UnloadExtensionNodeDescription(childDescription);
|
|
}
|
|
|
|
ChildNodesInternal.Remove(childNode);
|
|
changed = true;
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
internal TreeNode GetNode(string id, bool createIfNotFound)
|
|
{
|
|
System.Diagnostics.Debug.Assert(ExtensionPoint.NormalizePath(id) == id, "Path must be normalized");
|
|
|
|
TreeNode currentNode = this;
|
|
if (!string.IsNullOrEmpty(id))
|
|
{
|
|
string[] pathTokens = id.Split(ExtensionPoint.GetPathSeparators());
|
|
|
|
foreach (string token in pathTokens)
|
|
{
|
|
TreeNode childNode = currentNode.GetNodeInternal(token);
|
|
|
|
if (childNode == null)
|
|
{
|
|
if (createIfNotFound)
|
|
{
|
|
childNode = new TreeNode(token);
|
|
currentNode.ChildNodesInternal.Add(childNode);
|
|
}
|
|
else
|
|
{
|
|
currentNode = null;
|
|
break;
|
|
}
|
|
}
|
|
|
|
System.Diagnostics.Debug.Assert(childNode != null);
|
|
currentNode = childNode;
|
|
}
|
|
}
|
|
|
|
return currentNode;
|
|
}
|
|
|
|
private TreeNode GetNodeInternal(string id)
|
|
{
|
|
foreach (TreeNode node in this)
|
|
{
|
|
if (id == node.Id)
|
|
{
|
|
return node;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private ExtensionNode CreateExtensionNode(ExtensionNodeDescription description, ExtensionType type)
|
|
{
|
|
ExtensionNode node = Node.CreateNode<ExtensionNode>("extension", type);
|
|
|
|
if (node == null || !node.Initialize(this, description.Module, description, type))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
private bool ComputeIsValid(string target)
|
|
{
|
|
bool isTargetEmpty = string.IsNullOrEmpty(target);
|
|
|
|
if (ExtensionNodeDescription != null)
|
|
{
|
|
// Check conditions
|
|
foreach (ConditionInfo conditionInfo in ExtensionNodeDescription.Conditions)
|
|
{
|
|
if ((isTargetEmpty && string.IsNullOrEmpty(conditionInfo.Target)) || string.Equals(target, conditionInfo.Target))
|
|
{
|
|
if (!conditionInfo.Condition.Evaluate())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
internal void OnChanged()
|
|
{
|
|
// Notify our extension node
|
|
if (IsLoaded)
|
|
{
|
|
ExtensionNode.NotifyChildNodesChanged(new ExtensionNodeChangedEventArgs(this));
|
|
}
|
|
}
|
|
|
|
internal void OnConditionChanged()
|
|
{
|
|
m_isValid = null; // Reset m_isValid so that it will be recomputed next time.
|
|
}
|
|
|
|
private void ApplyAllConditionTarget()
|
|
{
|
|
if (ExtensionNodeDescription != null && IsLoaded)
|
|
{
|
|
List<string> doneTargets = new List<string>();
|
|
foreach (ConditionInfo conditionInfo in ExtensionNodeDescription.Conditions)
|
|
{
|
|
if (!string.IsNullOrEmpty(conditionInfo.Target) && !doneTargets.Contains(conditionInfo.Target))
|
|
{
|
|
ApplyCondition(conditionInfo.Target);
|
|
doneTargets.Add(conditionInfo.Target);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void ApplyCondition(string target)
|
|
{
|
|
System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(target));
|
|
|
|
if (IsLoaded && ExtensionNode.Type.ConditionAttributes.Contains(target))
|
|
{
|
|
StringPropertyDescriptor conditionalTargetInfo = ExtensionNode.Type.ConditionAttributes[target];
|
|
bool result = ComputeIsValid(target);
|
|
|
|
try
|
|
{
|
|
conditionalTargetInfo.MemberInfo.SetValue(ExtensionNode, result);
|
|
}
|
|
catch (System.Reflection.TargetInvocationException e)
|
|
{
|
|
AddinManager.Log(new Error(LogCategory.Error, "Unexpected exception while setting conditional target {0} on node of type {1}: {2}", target, ExtensionNode.GetType().FullName, e.InnerException.Message));
|
|
}
|
|
}
|
|
}
|
|
|
|
private static string GenerateAutoId()
|
|
{
|
|
return AutoIdPrefix + m_autoIdGenerator++;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Overriden.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override string ToString()
|
|
{
|
|
return $"[TreeNode: Id={Id}]";
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerable<TreeNode> Members
|
|
|
|
/// <summary>
|
|
/// Get enumerator.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public IEnumerator<TreeNode> GetEnumerator()
|
|
{
|
|
return ChildNodes.GetEnumerator();
|
|
}
|
|
|
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|