using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using RainmeterStudio.Core.Utils;
namespace RainmeterStudio.Core.Model
{
///
/// Reference to a file or folder
///
[DebuggerDisplay("QualifiedName = {QualifiedName}, StoragePath = {StoragePath}")]
public class Reference : INotifyCollectionChanged
{
private Dictionary _children;
#region Properties
///
/// Gets or sets the parent of this reference
///
[XmlIgnore]
public Reference Parent { get; set; }
///
/// Gets the children references
///
[XmlIgnore]
public ReadOnlyDictionary ChildrenDictionary
{
get
{
return new ReadOnlyDictionary(_children);
}
}
///
/// Gets or sets children
///
[XmlArray("children")]
public Reference[] Children
{
get
{
return _children.Values.ToArray();
}
set
{
Clear();
value.ForEach(Add);
}
}
///
/// Gets the name of the reference
///
[XmlAttribute("name")]
public string Name
{
get; set;
}
///
/// Gets the full qualified name of this reference
///
[XmlIgnore]
public string QualifiedName
{
get
{
if (Parent == null)
{
// Return name
return Name;
}
else
{
// If it has a parent, get the parent's name
return Parent.QualifiedName + '/' + Name;
}
}
}
///
/// Gets the parts of the full qualified name of this reference
///
[XmlIgnore]
public IEnumerable QualifiedParts
{
get
{
if (Parent == null)
{
return Enumerable.Repeat(Name, 1);
}
else
{
return Parent.QualifiedParts.Append(Name);
}
}
}
///
/// Gets the path to the file on the disk. If reference is in a project, the path should be relative.
///
[XmlAttribute("storagePath")]
public string StoragePath
{
get;
set;
}
#endregion
#region Constructors
///
/// Initializes the reference
///
public Reference()
: this(null, null)
{
}
///
/// Initializes the reference
///
/// Project path to item referenced
public Reference(string name)
: this(name, null)
{
}
///
/// Initializes the reference
///
/// Name of reference
/// Path to item on disk
public Reference(string name, string storagePath)
{
StoragePath = storagePath;
Name = name;
_children = new Dictionary();
}
#endregion
///
/// Checks if the reference has a file on disk
///
///
public bool IsOnStorage()
{
return (StoragePath != null);
}
///
/// Adds a child reference
///
///
public void Add(Reference reference)
{
// Make sure object is not parented yet
if (reference.Parent != null)
throw new ArgumentException("Reference must be removed from its current parent first.");
// Add and parent
reference.Parent = this;
_children.Add(reference.Name, reference);
// Trigger event
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, reference));
}
///
/// Removes a reference
///
/// Reference to remove
/// True if removed successfully
public bool Remove(Reference reference)
{
// Make sure we are the parent
if (reference.Parent != this)
return false;
// Remove
reference.Parent = null;
bool res = _children.Remove(reference.Name);
// Trigger event
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, reference));
return res;
}
///
/// Removes this reference from its parent
///
/// True if unparented successfully
public bool Unparent()
{
if (Parent != null)
return Parent.Remove(this);
return false;
}
///
/// Removes all children
///
public void Clear()
{
// Unparent
foreach (var pair in _children)
{
pair.Value.Parent = null;
}
// Clear
_children.Clear();
// Trigger event
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
///
/// Gets the number of children
///
public int Count
{
get
{
return _children.Count;
}
}
///
/// Compares a reference to another objects
///
/// Another object
/// True if objects are equal
public override bool Equals(object obj)
{
if (obj is Reference)
{
Reference other = (Reference)obj;
return (String.Equals(QualifiedName, other.QualifiedName));
}
return false;
}
///
/// Obtains the hash code of this reference
///
/// Hash code
public override int GetHashCode()
{
return QualifiedName.GetHashCode();
}
///
/// Gets the string representation of this reference
///
/// String representation
public override string ToString()
{
return QualifiedName;
}
///
/// Triggered when children are added or removed.
///
public event NotifyCollectionChangedEventHandler CollectionChanged;
}
///
/// Provides useful methods for references
///
public static class ReferenceExtensions
{
///
/// Tries to get a reference from the same tree having specified qualified name
///
/// Reference contained in the tree
/// Full qualified name
/// Found reference
/// True if succeeded to find the reference
public static bool TryGetReference(this Reference @this, string qualifiedName, out Reference output)
{
var thisQualifiedName = @this.QualifiedName;
// Am I the reference? return myself
if (qualifiedName.Equals(thisQualifiedName))
{
output = @this;
return true;
}
// Qualified name is a child, look child up
else if (qualifiedName.StartsWith(thisQualifiedName))
{
int startIndex = thisQualifiedName.Length + 1;
int endIndex = qualifiedName.IndexOf('/', startIndex);
string child;
Reference childRef;
if (endIndex < 0)
{
child = qualifiedName.Substring(startIndex);
}
else
{
child = qualifiedName.Substring(startIndex, endIndex - startIndex);
}
// Try to get child
if (@this.ChildrenDictionary.TryGetValue(child, out childRef))
{
return childRef.TryGetReference(qualifiedName, out output);
}
}
// Qualified name is not a child and not 'this', so ask parent to find it
else if (@this.Parent != null)
{
return @this.Parent.TryGetReference(qualifiedName, out output);
}
// Failed to find child
output = null;
return false;
}
///
/// Gets a reference from the same tree having specified qualified name
///
/// Reference contained in the tree
/// Full qualified name
/// Found reference
/// If qualified name not found
public static Reference GetReference(this Reference @this, string qualifiedName)
{
Reference res;
if (TryGetReference(@this, qualifiedName, out res))
{
return res;
}
else
{
throw new ArgumentException("Could not find reference.");
}
}
///
/// Checks if a reference is in the same tree as this
///
/// Reference that is in the tree we search in
/// Reference to search
/// True if the tree contains the reference.
public static bool TreeContains(this Reference @this, Reference reference)
{
Reference temp;
return TryGetReference(@this, reference.QualifiedName, out temp);
}
}
}