309 lines
9 KiB
C#
309 lines
9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using JD.Collections.Extensions;
|
|
using JD.Collections.Interfaces;
|
|
using JD.Kernel.Structs;
|
|
using JD.TeaBoxAddinSystem.Addins.Description;
|
|
using JD.TeaBoxAddinSystem.Addins.Description.Conditions;
|
|
using JD.TeaBoxAddinSystem.Addins.Internal;
|
|
|
|
namespace JD.TeaBoxAddinSystem.Addins
|
|
{
|
|
/// <summary>
|
|
/// Extension Nodes are the objects that are created and placed in the extension tree.
|
|
/// </summary>
|
|
public class ExtensionNode : Node
|
|
{
|
|
#region Inner Types
|
|
|
|
private class ExtensionNodeCollection : System.Collections.ObjectModel.ReadOnlyCollection<ExtensionNode>, IKeyedList<string, ExtensionNode>
|
|
{
|
|
#region Constructor
|
|
|
|
internal ExtensionNodeCollection(IList<ExtensionNode> list)
|
|
: base(list)
|
|
{
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
public ExtensionNode this[string key]
|
|
{
|
|
get
|
|
{
|
|
foreach (ExtensionNode node in this)
|
|
{
|
|
if (node.Id == key)
|
|
{
|
|
return node;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
public bool ContainsKey(string key)
|
|
{
|
|
return (this[key] != null);
|
|
}
|
|
|
|
public bool Remove(string key)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
public bool TryGetValue(string key, out ExtensionNode value)
|
|
{
|
|
value = this[key];
|
|
return value != null;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Variables
|
|
|
|
private object m_cachedInstance;
|
|
|
|
// Set through Initialize
|
|
private TreeNode m_node;
|
|
|
|
// Lazy-constructed
|
|
private List<ExtensionNode> m_childNodes;
|
|
|
|
private object m_lock = new object();
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Gets the file location of the extension node.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override sealed FileLocation FileLocation
|
|
{
|
|
get
|
|
{
|
|
if (m_node != null)
|
|
{
|
|
return m_node.ExtensionNodeDescription.FileLocation.GetValueOrDefault();
|
|
}
|
|
|
|
return FileLocation.Empty;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Type of this extension node.
|
|
/// </summary>
|
|
public new ExtensionType Type
|
|
{
|
|
get
|
|
{
|
|
return (ExtensionType)base.Type;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Id of the extension node.
|
|
/// </summary>
|
|
public string Id
|
|
{
|
|
get
|
|
{
|
|
return m_node.Id;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets whether the <see cref="Id"/> of this <see cref="ExtensionNode"/> was defined by the user or automatically generated.
|
|
/// </summary>
|
|
public bool HasIdDefined
|
|
{
|
|
get
|
|
{
|
|
return m_node.HasIdDefined;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Full path of the extension node.
|
|
/// </summary>
|
|
public string FullPath
|
|
{
|
|
get
|
|
{
|
|
return m_node.FullPath;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Child nodes of this extension node.
|
|
/// </summary>
|
|
public IKeyedList<string, ExtensionNode> ChildNodes
|
|
{
|
|
get
|
|
{
|
|
if (m_childNodes == null)
|
|
{
|
|
lock (m_lock)
|
|
{
|
|
if (m_childNodes == null)
|
|
{
|
|
List<ExtensionNode> nodes = new List<ExtensionNode>(m_node.ChildNodes.Count);
|
|
|
|
foreach (TreeNode childNode in m_node.ChildNodes)
|
|
{
|
|
ExtensionNode childExtensionNode = childNode.ExtensionNode;
|
|
|
|
if (childExtensionNode != null)
|
|
{
|
|
nodes.Add(childExtensionNode);
|
|
}
|
|
}
|
|
|
|
m_childNodes = nodes;
|
|
}
|
|
}
|
|
}
|
|
|
|
System.Diagnostics.Debug.Assert(m_childNodes != null);
|
|
return new ExtensionNodeCollection(m_childNodes);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension Object of this node.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This method is only valid on <see cref="TypeExtensionNode"/>s
|
|
/// </remarks>
|
|
/// <exception cref="InvalidOperationException">The node is not a <see cref="TypeExtensionNode"/>.</exception>
|
|
public virtual object ExtensionObject
|
|
{
|
|
get
|
|
{
|
|
if (m_cachedInstance == null)
|
|
{
|
|
m_cachedInstance = CreateExtensionObject();
|
|
}
|
|
|
|
return m_cachedInstance;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
/// <summary>
|
|
/// Creates an extension object for this node.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This method is only valid on certain extension node types like <see cref="TypeExtensionNode"/>.
|
|
/// </remarks>
|
|
/// <returns></returns>
|
|
/// <exception cref="InvalidOperationException">This extension node doesn't support extension objects.</exception>
|
|
public virtual object CreateExtensionObject()
|
|
{
|
|
throw new InvalidOperationException("ExtensionObjects are only available on TypeExtensionNodes");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a collection of extension objects contained in this extension node.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// All objects created by child nodes MUST be of type <typeparamref name="T"/>.
|
|
/// </remarks>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <returns></returns>
|
|
public IList<T> GetExtensionObjects<T>()
|
|
{
|
|
return ChildNodes.Project(node => (T)node.ExtensionObject);
|
|
}
|
|
|
|
internal T GetExtensionObject<T>()
|
|
{
|
|
return (T)ExtensionObject;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by a <see cref="TreeNode"/> when its collection of child nodes is modified.
|
|
/// </summary>
|
|
internal void NotifyChildNodesChanged(ExtensionNodeChangedEventArgs e)
|
|
{
|
|
// This will trigger a recreation of the nodes on demand.
|
|
m_childNodes = null;
|
|
OnChanged(e);
|
|
}
|
|
|
|
internal bool Initialize(TreeNode node, Module module, ExtensionNodeDescription description, ExtensionType type)
|
|
{
|
|
System.Diagnostics.Debug.Assert(node != null);
|
|
m_node = node;
|
|
|
|
if (!base.Initialize(module, description, type))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Validate conditional targets
|
|
if (description != null) // Description can be null in case of extension point's fake extension nodes.
|
|
{
|
|
foreach (ConditionInfo conditionInfo in description.Conditions)
|
|
{
|
|
if (!string.IsNullOrEmpty(conditionInfo.Target) && !type.ConditionAttributes.Contains(conditionInfo.Target))
|
|
{
|
|
description.LogWarning("Cannot apply condition {0} on target {1} of node {2} because it is not supported on extension nodes of type {3}",
|
|
conditionInfo.Condition,
|
|
conditionInfo.Target,
|
|
description.DisplayId,
|
|
type.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Events
|
|
|
|
/// <summary>
|
|
/// Triggered when this extension node changes.
|
|
/// </summary>
|
|
public event EventHandler<ExtensionNodeChangedEventArgs> Changed
|
|
{
|
|
add
|
|
{
|
|
AddinManager.SessionService.AddExtensionChangedHandler(FullPath, value);
|
|
}
|
|
remove
|
|
{
|
|
AddinManager.SessionService.RemoveExtensionChangedHandler(FullPath, value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Triggers a <see cref="Changed"/> event.
|
|
/// </summary>
|
|
/// <param name="e">Event Args.</param>
|
|
protected virtual void OnChanged(ExtensionNodeChangedEventArgs e)
|
|
{
|
|
AddinManager.SessionService.ChangedHandlerManager.OnChanged(e);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|