#region Copyright
// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (C) 2015 Ian Horswill
//
// 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.
//
// --------------------------------------------------------------------------------------------------------------------
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace Prolog
{
///
/// A node in the trie representing a collection of exclusion logic assertions
///
[DebuggerDisplay("{Name}")]
public class ELNode
{
#region Printing
///
/// Unparses the entry into key+key+key format
///
/// Name in key+key+key format
public override string ToString()
{
return Name;
}
///
/// Unparses the entry into key+key+key format
///
public string Name
{
get
{
if (Parent == null)
return "/";
var b = new StringBuilder();
this.BuildName(b);
return b.ToString();
}
}
void BuildName(StringBuilder b)
{
if (Parent != null && Parent.Parent != null)
{
Parent.BuildName(b);
b.Append(Parent.mode == ExclusionMode.Exclusive ? ELProlog.ExclusiveOperator : ELProlog.NonExclusiveOperator);
}
b.Append(Key);
}
#endregion
internal ELNode(ELNode parent, object key)
{
Key = key;
Parent = parent;
Children = EmptyChildren;
}
#region Fields and properties
///
/// Parent node of this KB node. Used primarily for printing.
///
public ELNode Parent { get; private set; }
///
/// The value associated with this node.
///
public object Key;
///
/// List of the child nodes of this node.
///
public List Children { get; private set; }
///
/// An empty list. Cached so there need only be one lying around.
///
static readonly List EmptyChildren = new List();
private enum ExclusionMode { Empty, Exclusive, NonExclusive };
private ExclusionMode mode;
public bool IsExclusive
{
get
{
return mode == ExclusionMode.Exclusive;
}
}
public bool IsNonExclusive
{
get
{
return mode == ExclusionMode.NonExclusive;
}
}
public string ModeString
{
get
{
switch (mode)
{
case ExclusionMode.Empty:
return "";
case ExclusionMode.Exclusive:
return ":";
case ExclusionMode.NonExclusive:
return "/";
default:
return "";
}
}
}
#endregion
#region Accessors
///
/// Test if this node contains a child with a given key.
///
/// Key to search for
/// True if there is a child with the specified key.
public bool ContainsKey(object key)
{
foreach (var n in Children)
if (Equals(key, n.Key))
return true;
return false;
}
///
/// Attempts to look up a child with the specified key.
///
/// Key to search for
/// Child with that key, if found, else null.
/// True if child was found, else false and child is null.
public bool TryLookup(object key, out ELNode child)
{
foreach (var c in Children)
{
if (Equals(c.Key, key))
{
child = c;
return true;
}
}
child = null;
return false;
}
///
/// Returns the child with the specified key or null is no such child.
///
/// Key to search for
/// The child, if there is a child with that key, otherwise null.
public ELNode ChildWithKey(object key)
{
foreach (var c in Children)
if (Equals(c.Key, key))
{
return c;
}
return null;
}
///
/// Return the value of this node's exclusive child's key.
/// Throw exception if its not exclusive, doesn't have a child, or the key has the wrong type.
///
/// Type expected for the key
/// Value of the child key.
public T ExclusiveKeyValue()
{
if (!IsExclusive)
throw new ArgumentException("ExclusiveKeyValue called on non-exclusive or empty node: "+this);
var child = Children[0];
if (!(child.Key is T))
throw new ArgumentException(string.Format("Node {0} has wrong type; should be {1}.", child, typeof(T).Name));
return (T)child.Key;
}
#endregion
#region Mutators
///
/// A do-nothing procedure used to make clear that a / % expression in C# is really intended to do a store.
///
/// The ELNode that got stored.
public static void Store(ELNode ignore)
{
// Does nothing
}
/////
///// Store an exclusive child inside this node.
/////
///// Bound variable holding the key for the child
///// If true, this will overwrite any existing child with a different value.
///// The child node
///// If a non-exclusive child has already been written.
//public ELNode StoreExclusive(Variable v, bool overwrite)
//{
// return StoreExclusive(v.Value, overwrite);
//}
///
/// Store an exclusive child inside this node.
///
/// Key for the child
/// If true, this will overwrite any existing child with a different value.
/// The child node
/// If a non-exclusive child has already been written.
public ELNode StoreExclusive(object key, bool overwrite)
{
switch (mode)
{
case ExclusionMode.Empty:
{
mode = ExclusionMode.Exclusive;
var result = new ELNode(this, key);
if (this.Children.Count==0)
this.Children = new List { result };
else
this.Children.Add(result);
return result;
}
case ExclusionMode.NonExclusive:
throw new ELNodeExclusionException("Exclusive store on non-exclusive node.", this, key);
case ExclusionMode.Exclusive:
if (overwrite)
{
if (this.Children.Count>0)
this.Children[0].OverwriteExclusive(key);
else
{
this.Children.Add(new ELNode(this, key));
}
}
else if (key != this.Children[0].Key)
throw new ELNodeExclusionException("Exclusive store doesn't match previous store.", this, key);
return this.Children[0];
default:
throw new InvalidOperationException("Invalid exclusion mode");
}
}
/////
///// Store a non-exclusive child inside this node.
/////
///// Bound variable holding the key for the child
///// The child node
///// If an exclusive child has already been written.
//public ELNode StoreNonExclusive(Variable v)
//{
// return StoreNonExclusive(v.Value);
//}
///
/// Store a non-exclusive child inside this node.
///
/// Key for the new child
/// The child node
/// If an exclusive child has already been written.
public ELNode StoreNonExclusive(object key)
{
ELNode result =null;
switch (mode)
{
case ExclusionMode.Empty:
{
mode = ExclusionMode.NonExclusive;
result = new ELNode(this, key);
if (this.Children == EmptyChildren)
this.Children = new List { result };
else
this.Children.Add(result);
}
break;
case ExclusionMode.Exclusive:
throw new ELNodeExclusionException("Non-exclusive store on exclusive node.", this, key);
case ExclusionMode.NonExclusive:
foreach (var c in this.Children)
{
if (c.Key == key)
return c;
}
this.Children.Add(result = new ELNode(this, key));
break;
}
return result;
}
private void OverwriteExclusive(object key)
{
if (key != Key)
{
Key = key;
this.Children.Clear();
mode = ExclusionMode.Empty;
}
}
///
/// Deletes this node from its parent.
///
public void DeleteSelf()
{
Parent.Children.Remove(this);
}
///
/// Deletes the first child matching KEY.
///
/// Key to search for
public void DeleteKey(object key)
{
for (int i = 0; i
/// Removes all the nodes satisfying the specified predicate
///
///
public void DeleteAll(Predicate predicate)
{
Children.RemoveAll(predicate);
}
#endregion
#region Tree walk
///
/// Run VISITOR on all the nodes in a subtree rooted at this node and whose
/// children are stored in nodes with the key CHILDKEY
///
/// Key to look up to get node containing children of this node
/// Procedure to call on each node.
public void WalkTree(object childKey, Action visitor)
{
visitor(this);
ELNode childrenNode;
if (this.TryLookup(childKey, out childrenNode))
{
// It has children
foreach (var child in childrenNode.Children)
child.WalkTree(childKey, visitor);
}
}
#endregion
#region Mutators overloaded as C# operators
/////
///// Write the specified key as a non-exclusive child. If key is already a child, this has no effect
/////
///// KB node
///// Bound variable holding the key for the child
///// The child node containing key.
///// If an exclusive child has already been written in this node.
//public static ELNode operator +(ELNode e, Variable v)
//{
// return e.StoreNonExclusive(v);
//}
///
/// Write the specified key as a non-exclusive child. If key is already a child, this has no effect
///
/// KB node
/// Key to write
/// The child node containing key.
/// If an exclusive child has already been written in this node.
public static ELNode operator /(ELNode e, object key)
{
return e.StoreNonExclusive(key);
}
/////
///// Write the specified key as an exclusive child.
///// If key is already the child, this has no effect.
///// Otherwise, the current child is replaced with this key.
/////
///// KB node
///// Bound variable holding the key for the child
///// The child node containing key.
///// If a non-exclusive child has already been written in this node.
//public static ELNode operator -(ELNode e, Variable v)
//{
// return e.StoreExclusive(v, true);
//}
///
/// Write the specified key as an exclusive child.
/// If key is already the child, this has no effect.
/// Otherwise, the current child is replaced with this key.
///
/// KB node
/// Key to write
/// The child node containing key.
/// If a non-exclusive child has already been written in this node.
public static ELNode operator %(ELNode e, object key)
{
return e.StoreExclusive(key, true);
}
#endregion
}
}