JD2022-TU1/main/tools/framework/JD.TeaBoxAddinSystem/Addins/ExtensionNode.cs

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
}
}