#region Disclaimer / License
// Copyright (C) 2011, Jackie Ng
// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie@gmail.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using OSGeo.MapGuide.MaestroAPI.Exceptions;
namespace OSGeo.MapGuide.MaestroAPI.Mapping
{
///
/// A generic key/value collection
///
/// The type of the key.
/// The type of the value.
public abstract class KeyValueCollection : IList where TVal : class
{
///
/// The internal list of value
///
protected List _values;
///
/// The internal dictionary of values keyed by its key
///
protected Dictionary _valuesByKey;
///
/// Initializes a new instance of the class.
///
protected KeyValueCollection()
{
_values = new List();
_valuesByKey = new Dictionary();
}
///
/// Determines the index of a specific item in the .
///
/// The object to locate in the .
///
/// The index of if found in the list; otherwise, -1.
///
public int IndexOf(TVal item)
{
return _values.IndexOf(item);
}
///
/// Determines the index of a specific key in the collection
///
/// The key of the object to locate in the collection
///
/// The index of if found in the list; otherwise, -1.
///
public int IndexOf(TKey key)
{
var item = this[key];
return IndexOf(item);
}
///
/// Inserts an item to the at the specified index.
///
/// The zero-based index at which should be inserted.
/// The object to insert into the .
///
/// is not a valid index in the .
///
///
///
/// The is read-only.
///
public virtual void Insert(int index, TVal item)
{
var key = SelectKey(item);
if (_valuesByKey.ContainsKey(key))
throw new DuplicateKeyException(string.Format(Properties.Resources.DuplicateKeyExceptionMessage, key));
OnBeforeItemAdded(item);
_values.Insert(index, item);
_valuesByKey.Add(key, item);
OnItemAdded(item);
}
///
/// Removes the item at the specified index.
///
/// The zero-based index of the item to remove.
///
/// is not a valid index in the .
///
///
///
/// The is read-only.
///
public void RemoveAt(int index)
{
var item = this[index];
OnBeforeItemRemove(item);
_values.RemoveAt(index);
if (item != null)
{
var key = SelectKey(item);
_valuesByKey.Remove(key);
OnItemRemoved(item);
}
}
///
/// Gets or sets the element at the specified index.
///
///
/// The element at the specified index.
///
///
///
/// is not a valid index in the .
///
///
///
/// The property is set and the is read-only.
///
public virtual TVal this[int index]
{
get
{
return _values[index];
}
set
{
if (_values[index] != null)
RemoveAt(index);
_values[index] = value;
}
}
///
/// Adds an item to the .
///
/// The object to add to the .
///
/// The is read-only.
///
public virtual void Add(TVal item)
{
var key = SelectKey(item);
if (_valuesByKey.ContainsKey(key))
throw new DuplicateKeyException(string.Format(Properties.Resources.DuplicateKeyExceptionMessage, key));
OnBeforeItemAdded(item);
_values.Add(item);
_valuesByKey.Add(key, item);
OnItemAdded(item);
}
///
/// Removes all items from the .
///
///
/// The is read-only.
///
public virtual void Clear()
{
//We don't call Clear() directly because we need to propagate removal of each
//item back to the map
var items = new List(this);
foreach (var item in items)
{
Remove(item);
}
//This shouldn't happen
if (_values.Count > 0)
{
System.Diagnostics.Trace.TraceWarning("Expected empty values collection!");
_values.Clear();
}
if (_valuesByKey.Count > 0)
{
System.Diagnostics.Trace.TraceWarning("Expected empty values collection!");
_valuesByKey.Clear();
}
}
///
/// Determines whether the contains a specific value.
///
/// The object to locate in the .
///
/// true if is found in the ; otherwise, false.
///
public bool Contains(TVal item)
{
return _values.Contains(item);
}
///
/// Copies the elements of the to an , starting at a particular index.
///
/// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing.
/// The zero-based index in at which copying begins.
///
/// is null.
///
///
///
/// is less than 0.
///
///
///
/// is multidimensional.
/// -or-
/// is equal to or greater than the length of .
/// -or-
/// The number of elements in the source is greater than the available space from to the end of the destination .
/// -or-
/// Type cannot be cast automatically to the type of the destination .
///
public void CopyTo(TVal[] array, int arrayIndex)
{
_values.CopyTo(array, arrayIndex);
}
///
/// Gets the number of elements contained in the .
///
///
/// The number of elements contained in the .
///
public int Count
{
get { return _values.Count; }
}
///
/// Gets a value indicating whether the is read-only.
///
/// true if the is read-only; otherwise, false.
///
public bool IsReadOnly
{
get { return false; }
}
///
/// Removes the first occurrence of a specific object from the .
///
/// The object to remove from the .
///
/// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original .
///
///
/// The is read-only.
///
public bool Remove(TVal item)
{
OnBeforeItemRemove(item);
var ret = _values.Remove(item);
if (ret)
{
var key = SelectKey(item);
_valuesByKey.Remove(key);
OnItemRemoved(item);
return ret;
}
return ret;
}
///
/// Returns an enumerator that iterates through the collection.
///
///
/// A that can be used to iterate through the collection.
///
public IEnumerator GetEnumerator()
{
return _values.GetEnumerator();
}
///
/// Returns an enumerator that iterates through a collection.
///
///
/// An object that can be used to iterate through the collection.
///
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _values.GetEnumerator();
}
///
/// Gets or sets the element at the specified index.
///
///
/// The element at the specified index.
///
///
///
/// is not a valid index in the .
///
///
///
/// The property is set and the is read-only.
///
public TVal this[TKey key]
{
get { return _valuesByKey.ContainsKey(key) ? _valuesByKey[key] : null; }
set { _valuesByKey[key] = value; }
}
///
/// Called before an item is added
///
/// The item.
protected virtual void OnBeforeItemAdded(TVal item) { }
///
/// Called before an item is removed
///
/// The item.
protected virtual void OnBeforeItemRemove(TVal item) { }
///
/// Called after an item has been added
///
/// The item.
protected abstract void OnItemAdded(TVal item);
///
/// Called after an item has been removed. Note this is only called if the remove
/// operation removed the item in question
///
/// The value.
protected abstract void OnItemRemoved(TVal value);
///
/// Selects the key given the value.
///
/// The value.
///
protected abstract TKey SelectKey(TVal value);
}
///
/// A collection of runtime map layers
///
public class RuntimeMapLayerCollection : KeyValueCollection
{
private RuntimeMap _parent;
private Dictionary _layerIdMap;
internal RuntimeMapLayerCollection(RuntimeMap parent)
{
_parent = parent;
_layerIdMap = new Dictionary();
}
///
/// Adds the specified layer.
///
/// The layer.
public override void Add(RuntimeMapLayer layer)
{
//calculate and set the zorder for the new layer
RuntimeMapLayer prevLayer = (this.Count == 0) ? null : this[this.Count - 1];
double zOrder = prevLayer == null ? RuntimeMap.Z_ORDER_TOP : prevLayer.DisplayOrder + RuntimeMap.Z_ORDER_INCREMENT;
layer.DisplayOrder = zOrder;
base.Add(layer);
}
///
/// Inserts an item to the at the specified index.
///
/// The zero-based index at which should be inserted.
/// The object to insert into the .
///
/// is not a valid index in the .
///
///
///
/// The is read-only.
///
public override void Insert(int index, RuntimeMapLayer item)
{
CalculateDisplayOrder(index, item);
base.Insert(index, item);
}
///
/// Called when [before item added].
///
/// The layer.
protected override void OnBeforeItemAdded(RuntimeMapLayer layer)
{
if (_layerIdMap.ContainsKey(layer.ObjectId))
throw new DuplicateKeyException(string.Format(Properties.Resources.DuplicateKeyExceptionMessage, layer.ObjectId));
}
///
/// Called when [item added].
///
/// The layer.
protected override void OnItemAdded(RuntimeMapLayer layer)
{
_layerIdMap[layer.ObjectId] = layer;
_parent.OnLayerAdded(layer);
}
///
/// Called when [item removed].
///
/// The layer.
protected override void OnItemRemoved(RuntimeMapLayer layer)
{
if (_layerIdMap.ContainsKey(layer.ObjectId))
_layerIdMap.Remove(layer.ObjectId);
_parent.OnLayerRemoved(layer);
}
///
/// Selects the key given the value.
///
/// The value.
///
protected override string SelectKey(RuntimeMapLayer value)
{
return value.Name;
}
///
/// Gets a runtime map layer by its object id.
///
/// The object id.
///
public RuntimeMapLayer GetByObjectId(string id)
{
return _layerIdMap.ContainsKey(id) ? _layerIdMap[id] : null;
}
///
/// Gets or sets the element at the specified index.
///
///
/// The element at the specified index.
///
///
///
/// is not a valid index in the .
///
///
///
/// The property is set and the is read-only.
///
public override RuntimeMapLayer this[int index]
{
get
{
return base[index];
}
set
{
CalculateDisplayOrder(index, value);
base[index] = value;
}
}
///
/// Removes the specified layer by its name.
///
/// The name.
public void Remove(string name)
{
var layer = this[name];
if (layer != null)
Remove(layer);
}
private void CalculateDisplayOrder(int index, RuntimeMapLayer value)
{
//calculate zorder for the new layer
double zOrderLow, zOrderHigh;
RuntimeMapLayer layer;
if (index == 0)
{
zOrderLow = 0;
layer = base.Count > 0 ? base[index] : null;
if (layer != null)
zOrderHigh = layer.DisplayOrder;
else
zOrderHigh = 2.0 * RuntimeMap.Z_ORDER_INCREMENT;
}
else
{
layer = base[index - 1];
zOrderLow = layer.DisplayOrder;
layer = base.Count > index ? base[index] : null;
zOrderHigh = layer != null ? layer.DisplayOrder : zOrderLow + 2.0 * RuntimeMap.Z_ORDER_INCREMENT;
}
value.DisplayOrder = (zOrderLow + (zOrderHigh - zOrderLow) / 2.0);
}
}
///
/// A collection of runtime map groups
///
public class RuntimeMapGroupCollection : KeyValueCollection
{
private RuntimeMap _parent;
internal RuntimeMapGroupCollection(RuntimeMap parent)
{
_parent = parent;
}
///
/// Called after an item has been added
///
/// The item.
protected override void OnItemAdded(RuntimeMapGroup item)
{
_parent.OnGroupAdded(item);
}
///
/// Called after an item has been removed. Note this is only called if the remove
/// operation removed the item in question
///
/// The value.
protected override void OnItemRemoved(RuntimeMapGroup value)
{
_parent.OnGroupRemoved(value);
}
///
/// Selects the key given the value.
///
/// The value.
///
protected override string SelectKey(RuntimeMapGroup value)
{
return value.Name;
}
///
/// Removes the specified group by its name.
///
/// Name of the group.
public void Remove(string groupName)
{
var group = this[groupName];
if (group != null)
Remove(group);
}
}
}