//
// ExtensionNodeSet.cs
//
// Author:
// Lluis Sanchez Gual
//
// Copyright (C) 2007 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.Xml;
using Mono.Addins.Serialization;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
namespace Mono.Addins.Description
{
///
/// An extension node set definition.
///
///
/// Node sets allow grouping a set of extension node declarations and give an identifier to that group
/// (the node set). Once a node set is declared, it can be referenced from several extension points
/// which use the same extension node structure. Extension node sets also allow declaring recursive
/// extension nodes, that is, extension nodes with a tree structure.
///
public class ExtensionNodeSet: ObjectDescription
{
string id;
ExtensionNodeTypeCollection nodeTypes;
NodeSetIdCollection nodeSets;
bool missingNodeSetId;
ExtensionNodeTypeCollection cachedAllowedTypes;
internal string SourceAddinId { get; set; }
internal ExtensionNodeSet (XmlElement element)
{
Element = element;
id = element.GetAttribute (IdAttribute);
}
///
/// Copies data from another node set
///
///
/// Node set from which to copy
///
public void CopyFrom (ExtensionNodeSet nset)
{
id = nset.id;
NodeTypes.Clear ();
foreach (ExtensionNodeType nt in nset.NodeTypes) {
ExtensionNodeType cnt = new ExtensionNodeType ();
cnt.CopyFrom (nt);
NodeTypes.Add (cnt);
}
NodeSets.Clear ();
foreach (string ns in nset.NodeSets)
NodeSets.Add (ns);
missingNodeSetId = nset.missingNodeSetId;
}
internal override void Verify (string location, StringCollection errors)
{
if (missingNodeSetId)
errors.Add (location + "Missing id attribute in extension set reference");
NodeTypes.Verify (location + "ExtensionNodeSet (" + Id + ")/", errors);
}
internal override void SaveXml (XmlElement parent)
{
SaveXml (parent, "ExtensionNodeSet");
}
internal virtual void SaveXml (XmlElement parent, string nodeName)
{
if (Element == null) {
Element = parent.OwnerDocument.CreateElement (nodeName);
parent.AppendChild (Element);
}
if (Id.Length > 0)
Element.SetAttribute (IdAttribute, Id);
if (nodeTypes != null)
nodeTypes.SaveXml (Element);
if (nodeSets != null) {
foreach (string s in nodeSets) {
if (Element.SelectSingleNode ("ExtensionNodeSet[@id='" + s + "']") == null) {
XmlElement e = Element.OwnerDocument.CreateElement ("ExtensionNodeSet");
e.SetAttribute ("id", s);
Element.AppendChild (e);
}
}
var list = new List ();
foreach (XmlElement e in Element.SelectNodes ("ExtensionNodeSet")) {
if (!nodeSets.Contains (e.GetAttribute ("id")))
list.Add (e);
}
foreach (XmlElement e in list)
Element.RemoveChild (e);
}
}
///
/// Initializes a new instance of the class.
///
public ExtensionNodeSet ()
{
}
///
/// Gets or sets the identifier of the node set.
///
///
/// The identifier.
///
public string Id {
get { return id != null ? id : string.Empty; }
set { id = value; }
}
internal virtual string IdAttribute {
get { return "id"; }
}
///
/// Gets the node types allowed in this node set.
///
///
/// The node types.
///
public ExtensionNodeTypeCollection NodeTypes {
get {
if (nodeTypes == null) {
if (Element != null)
InitCollections ();
else
nodeTypes = new ExtensionNodeTypeCollection (this);
}
return nodeTypes;
}
}
///
/// Gets a list of other node sets included in this node set.
///
///
/// The node sets.
///
public NodeSetIdCollection NodeSets {
get {
if (nodeSets == null) {
if (Element != null)
InitCollections ();
else
nodeSets = new NodeSetIdCollection ();
}
return nodeSets;
}
}
///
/// Gets all the allowed node types.
///
///
/// The allowed node types.
///
///
/// Gets all allowed node types, including those defined in included node sets.
/// This method only works for descriptions loaded from a registry.
///
public ExtensionNodeTypeCollection GetAllowedNodeTypes ()
{
if (cachedAllowedTypes == null) {
cachedAllowedTypes = new ExtensionNodeTypeCollection ();
GetAllowedNodeTypes (new Hashtable (), cachedAllowedTypes);
}
return cachedAllowedTypes;
}
void GetAllowedNodeTypes (Hashtable visitedSets, ExtensionNodeTypeCollection col)
{
if (Id.Length > 0) {
if (visitedSets.Contains (Id))
return;
visitedSets [Id] = Id;
}
// Gets all allowed node types, including those defined in node sets
// It only works for descriptions generated from a registry
foreach (ExtensionNodeType nt in NodeTypes)
col.Add (nt);
AddinDescription desc = ParentAddinDescription;
if (desc == null || desc.OwnerDatabase == null)
return;
foreach (string[] ns in NodeSets.InternalList) {
string startAddin = ns [1];
if (startAddin == null || startAddin.Length == 0)
startAddin = desc.AddinId;
ExtensionNodeSet nset = desc.OwnerDatabase.FindNodeSet (ParentAddinDescription.Domain, startAddin, ns[0]);
if (nset != null)
nset.GetAllowedNodeTypes (visitedSets, col);
}
}
internal void Clear ()
{
Element = null;
nodeSets = null;
nodeTypes = null;
}
internal void SetExtensionsAddinId (string addinId)
{
foreach (ExtensionNodeType nt in NodeTypes) {
nt.AddinId = addinId;
nt.SetExtensionsAddinId (addinId);
}
NodeSets.SetExtensionsAddinId (addinId);
}
internal void MergeWith (string thisAddinId, ExtensionNodeSet other)
{
foreach (ExtensionNodeType nt in other.NodeTypes) {
if (nt.AddinId != thisAddinId && !NodeTypes.Contains (nt))
NodeTypes.Add (nt);
}
NodeSets.MergeWith (thisAddinId, other.NodeSets);
}
internal void UnmergeExternalData (string thisAddinId, Hashtable addinsToUnmerge)
{
// Removes extension types and extension sets coming from other add-ins.
var todelete = new List ();
foreach (ExtensionNodeType nt in NodeTypes) {
if (nt.AddinId != thisAddinId && (addinsToUnmerge == null || addinsToUnmerge.Contains (nt.AddinId)))
todelete.Add (nt);
}
foreach (ExtensionNodeType nt in todelete)
NodeTypes.Remove (nt);
NodeSets.UnmergeExternalData (thisAddinId, addinsToUnmerge);
}
void InitCollections ()
{
nodeTypes = new ExtensionNodeTypeCollection (this);
nodeSets = new NodeSetIdCollection ();
foreach (XmlNode n in Element.ChildNodes) {
XmlElement nt = n as XmlElement;
if (nt == null)
continue;
if (nt.LocalName == "ExtensionNode") {
ExtensionNodeType etype = new ExtensionNodeType (nt);
nodeTypes.Add (etype);
}
else if (nt.LocalName == "ExtensionNodeSet") {
string id = nt.GetAttribute ("id");
if (id.Length > 0)
nodeSets.Add (id);
else
missingNodeSetId = true;
}
}
}
internal override void Write (BinaryXmlWriter writer)
{
writer.WriteValue ("Id", id);
writer.WriteValue ("NodeTypes", NodeTypes);
writer.WriteValue ("NodeSets", NodeSets.InternalList);
}
internal override void Read (BinaryXmlReader reader)
{
id = reader.ReadStringValue ("Id");
nodeTypes = (ExtensionNodeTypeCollection) reader.ReadValue ("NodeTypes", new ExtensionNodeTypeCollection (this));
reader.ReadValue ("NodeSets", NodeSets.InternalList);
}
}
///
/// A collection of node set identifiers
///
public class NodeSetIdCollection: IEnumerable
{
// A list of string[2]. Item 0 is the node set id, item 1 is the addin that defines it.
List list = new List ();
///
/// Gets the node set identifier at the specified index.
///
///
/// An index.
///
public string this [int n] {
get { return list [n][0]; }
}
///
/// Gets the item count.
///
///
/// The count.
///
public int Count {
get { return list.Count; }
}
///
/// Gets the collection enumerator.
///
///
/// The enumerator.
///
public IEnumerator GetEnumerator ()
{
return list.Select (x => x [0]).GetEnumerator ();
}
///
/// Add the specified node set identifier.
///
///
/// Node set identifier.
///
public void Add (string nodeSetId)
{
if (!Contains (nodeSetId))
list.Add (new string [] { nodeSetId, null });
}
///
/// Remove a node set identifier
///
///
/// Node set identifier.
///
public void Remove (string nodeSetId)
{
int i = IndexOf (nodeSetId);
if (i != -1)
list.RemoveAt (i);
}
///
/// Clears the collection
///
public void Clear ()
{
list.Clear ();
}
///
/// Checks if the specified identifier is present in the collection
///
///
/// true if the node set identifier is present.
///
public bool Contains (string nodeSetId)
{
return IndexOf (nodeSetId) != -1;
}
///
/// Returns the index of the specified node set identifier
///
///
/// The index.
///
///
/// A node set identifier.
///
public int IndexOf (string nodeSetId)
{
for (int n=0; n InternalList {
get { return list; }
set { list = value; }
}
internal void MergeWith (string thisAddinId, NodeSetIdCollection other)
{
foreach (string[] ns in other.list) {
if (ns [1] != thisAddinId && !list.Contains (ns))
list.Add (ns);
}
}
internal void UnmergeExternalData (string thisAddinId, Hashtable addinsToUnmerge)
{
List newList = new List ();
foreach (string[] ns in list) {
if (ns[1] == thisAddinId || (addinsToUnmerge != null && !addinsToUnmerge.Contains (ns[1])))
newList.Add (ns);
}
list = newList;
}
}
}