|
//*****************************************************************************/
// Copyright (c) 2010 Luigi Grilli
//
// 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.Generic;
using System.Text;
using System.ComponentModel;
namespace Odbms.SmartLists
{
/// <summary>
/// EventList with named items
/// </summary>
/// <remarks>In this kind of list is not allowed to add items with the same name</remarks>
/// <typeparam name="T">Type of the items contained in the list</typeparam>
public class NamedList<T> : EventList<T> where T : class, INamed
{
private bool _caseSensitive = true;
private NamedList<T> _readOnlyNamedList = null;
#region Constructors
/// <summary>
/// Creates a new NamedList
/// </summary>
public NamedList()
{
}
/// <summary>
/// Creates a new readonly NamedList referencing the specified list
/// </summary>
/// <param name="list">Base list</param>
protected NamedList(IList<T> list, bool readOnly)
: base(list, readOnly)
{
}
#endregion
#region Public Members
/// <summary>
/// Gets the element with the specified name
/// </summary>
/// <param name="name">Name of the element</param>
/// <returns>The element with the specified name</returns>
public T this[string name]
{
get
{
T item = this.FindName(name);
if (item == null)
throw new ArgumentException("There are no objects with '" + name + "' name in the collection");
return item;
}
}
/// <summary>
/// Check if there is an elment with the specified name
/// </summary>
/// <param name="name">Name to look for in the list items</param>
/// <returns>True if there is an item with the specified name, false if it doesn't exists</returns>
public bool ExistsName(string name)
{
return (this.FindName(name) != null);
}
/// <summary>
/// Get or set the way the Items name are checked for duplicated entries
/// </summary>
public bool CaseSensitive
{
get { return _caseSensitive; }
set
{
if (this.IsReadOnly)
throw new ReadOnlyListException();
//check if you can change the value to case insensitive
if(!value)
foreach (T item in this)
if (this.FindName(item.Name, item, value) != null)
throw new InvalidOperationException("Cannot set CaseSensitive to false because there would be duplicated items names");
_caseSensitive = value;
}
}
/// <summary>
/// Creates a readonly reference to this list
/// </summary>
/// <returns></returns>
public override EventList<T> AsReadOnly()
{
return this.AsReadOnlyNamedList();
}
/// <summary>
/// Creates a readonly reference to this list
/// </summary>
/// <returns></returns>
public NamedList<T> AsReadOnlyNamedList()
{
if (this.IsReadOnly)
return this;
if (_readOnlyNamedList == null)
_readOnlyNamedList = new NamedList<T>(this, true);
return _readOnlyNamedList;
}
#endregion
#region Protected and overriden members
/// <summary>
/// Checks if there is already an item with the same Name on the list
/// </summary>
/// <param name="item">Item to be added</param>
/// <returns>True if the operation must be cancelled</returns>
/// <exception cref="DuplicatedNameException"></exception>
protected override bool OnBeforeAdd(T item)
{
if (this.FindName(item.Name) != null)
throw new DuplicatedNameException(item.Name);
return base.OnBeforeAdd(item);
}
/// <summary>
/// After adding an item we register for change Name events
/// </summary>
/// <param name="item">Item added to the list</param>
protected override void OnAfterAdd(T item)
{
item.BeforeNameChanged += new CancelRenameEventHandler(this.ItemBeforeNameChanged);
base.OnAfterAdd(item);
}
/// <summary>
/// Check if the item can change it's name
/// </summary>
/// <param name="item">Item that will change it's name</param>
/// <param name="newName">The name that will be set</param>
protected virtual void OnItemNameChanged(T item, string newName)
{
if (this.FindName(item.Name, item) != null)
throw new DuplicatedNameException(item.Name);
}
/// <summary>
/// After removing an item removes the event handler for change Name events
/// </summary>
/// <param name="item">Item that has been removed</param>
protected override void OnAfterRemove(T item)
{
item.BeforeNameChanged -= new CancelRenameEventHandler(this.ItemBeforeNameChanged);
base.OnAfterRemove(item);
}
/// <summary>
/// Checks if there is already an item with the same Name on the list
/// </summary>
/// <param name="index">Index where the item will be inserted</param>
/// <param name="item">Item to be inserted</param>
/// <returns>True if the operation must be cancelled</returns>
/// <exception cref="DuplicatedNameException"></exception>
protected override bool OnBeforeInsert(int index, T item)
{
if (index < this.Count)
if (this.FindName(item.Name, this[index]) != null)
throw new DuplicatedNameException(item.Name);
return base.OnBeforeInsert(index, item);
}
/// <summary>
/// Removes the event handler for change Name events of all the items removed
/// </summary>
/// <param name="items">Items removed from the list</param>
protected override void OnAfterClear(T[] items)
{
foreach (T item in items)
item.BeforeNameChanged -= new CancelRenameEventHandler(this.ItemBeforeNameChanged);
base.OnAfterClear(items);
}
/// <summary>
/// Event handler for the items BeforeNameChanged event
/// </summary>
/// <param name="sender">Item</param>
/// <param name="args">Event arguments</param>
private void ItemBeforeNameChanged(object sender, CancelRenameEventArgs args)
{
this.OnItemNameChanged((T)sender, args.NewName);
}
#endregion
#region Private methods
private T FindName(string name)
{
return this.FindName(name, null);
}
private T FindName(string name, INamed exclude)
{
return this.FindName(name, exclude, this.CaseSensitive);
}
private T FindName(string name, INamed exclude, bool caseSensitive)
{
if (String.IsNullOrEmpty(name))
throw new ArgumentNullException("Object name can't be empty");
if (caseSensitive)
{
foreach (T obj in this)
if (obj != exclude)
if (obj.Name.Equals(name))
return obj;
}
else
{
name = name.ToLower();
foreach (T obj in this)
if (obj != exclude)
if (obj.Name.ToLower().Equals(name))
return obj;
}
return null;
}
#endregion
}
#region Interfaces
/// <summary>
/// Object that have a Name (property)
/// </summary>
public interface INamed
{
/// <summary>
/// Event fired before the Name of the object is changed
/// Operation can be canceled
/// </summary>
event CancelRenameEventHandler BeforeNameChanged;
/// <summary>
/// Event fired when the object has changed it's Name
/// </summary>
event EventHandler NameChanged;
/// <summary>
/// Name of the object
/// </summary>
string Name { get; }
}
#endregion
#region Event arguments and delagates
/// <summary>
/// Event handler for a rename operation
/// </summary>
/// <param name="sender">Item that will be renaimed</param>
/// <param name="args">Event data</param>
public delegate void CancelRenameEventHandler(object sender, CancelRenameEventArgs args);
/// <summary>
/// Event arguments data of a Rename operation
/// </summary>
public class CancelRenameEventArgs : CancelEventArgs
{
string _newName = String.Empty;
/// <summary>
/// Event arguments data of a Rename operation
/// </summary>
/// <param name="newName">New Name that will be set</param>
public CancelRenameEventArgs(string newName)
{
_newName = newName;
}
/// <summary>
/// Get the new name that will be set
/// </summary>
public string NewName
{
get { return _newName; }
}
}
#endregion
#region Exceptions
/// <summary>
/// Represent an error that the list has already an item with the same name
/// </summary>
[global::System.Serializable]
public class DuplicatedNameException : Exception
{
/// <summary>
/// Represent an error that the list has already an item with the same name
/// </summary>
/// <param name="name"></param>
public DuplicatedNameException(string name) : base("An element with the name '" + name + "' is already present on the list") { }
/// <summary>
/// Initializes a new instance of the System.Exception class with serialized data
/// </summary>
/// <param name="info"></param>
/// <param name="context"></param>
protected DuplicatedNameException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
: base(info, context) { }
}
#endregion
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.