using System;
using System.Collections.Generic;
using System.Text;
namespace OSGeo.MapGuide.MaestroAPI
{
///
/// A wrapper class to help deal with the selection xml format
///
public class Selection : IList
{
private RuntimeClasses.RuntimeMap m_map;
private List m_layers;
///
/// Constructs a new selection helper
///
/// The map that the selection belongs to
public Selection(RuntimeClasses.RuntimeMap map)
{
m_layers = new List();
m_map = map;
}
///
/// Constructs a new selection helper
///
/// The map that the selection belongs to
/// A selection xml to initialize the selection with
public Selection(RuntimeClasses.RuntimeMap map, string xml)
: this(map)
{
FromXml(xml);
}
///
/// Resets the selectionhelper to match the provided xml
///
/// The selection xml
public void FromXml(string xml)
{
m_layers.Clear();
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
if (!string.IsNullOrEmpty(xml))
doc.LoadXml(xml);
//There are two variations
System.Xml.XmlNodeList lst = doc.SelectNodes("FeatureSet/Layer");
if (lst.Count == 0)
lst = doc.SelectNodes("FeatureInformation/FeatureSet/Layer");
foreach (System.Xml.XmlNode n in lst)
{
if (n.Attributes["id"] != null)
{
string guid = n.Attributes["id"].Value;
RuntimeClasses.RuntimeMapLayer l = m_map.Layers.FindByGuid(guid);
if (l != null)
{
foreach (System.Xml.XmlNode c in n.SelectNodes("Class"))
{
if (c.Attributes["id"] != null)
if (c.Attributes["id"].Value == l.SchemaName + ":" + l.FeatureName)
m_layers.Add(new LayerSelection(l, c.SelectNodes("ID")));
}
}
}
}
}
///
/// Returns an xml document that represents the current map selection
///
/// An xml document that represents the current map selection
public string ToXml()
{
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
System.Xml.XmlNode root = doc.AppendChild(doc.CreateElement("FeatureSet"));
foreach (LayerSelection layer in m_layers)
{
System.Xml.XmlNode ln = root.AppendChild(doc.CreateElement("Layer"));
ln.Attributes.Append(doc.CreateAttribute("id")).Value = layer.Layer.Guid;
System.Xml.XmlNode cn = ln.AppendChild(doc.CreateElement("Class"));
cn.Attributes.Append(doc.CreateAttribute("id")).Value = layer.Layer.SchemaName + ":" + layer.Layer.FeatureName;
for (int i = 0; i < layer.Count; i++)
cn.AppendChild(doc.CreateElement("ID")).InnerText = layer.EncodeIDString(layer[i]);
}
return doc.OuterXml;
}
///
/// Returns the selection Xml, the same as ToXml()
///
/// The selection xml
public override string ToString()
{
return ToXml();
}
///
/// Helper class to represent a layer with selected objects
///
public class LayerSelection : IList
{
private RuntimeClasses.RuntimeMapLayer m_layer;
private List m_list = new List();
///
/// Gets the layer that contains the selected objects
///
public RuntimeClasses.RuntimeMapLayer Layer { get { return m_layer; } }
///
/// Internal helper to construct a LayerSelection
///
/// The layer that the selection belongs to
/// A list of xml <ID> nodes
internal LayerSelection(RuntimeClasses.RuntimeMapLayer layer, System.Xml.XmlNodeList ids)
: this(layer)
{
foreach (System.Xml.XmlNode n in ids)
Add(ParseIDString(n.InnerXml));
}
///
/// Encodes the given combined keyset into an ID string for use in the Xml
///
/// The combined key
/// A base64 encoded ID string
public string EncodeIDString(object[] values)
{
object[] tmp = NormalizeAndValidate(values);
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
for (int i = 0; i < m_layer.IDs.Count; i++)
{
Type type = m_layer.IDs[i].Value;
if (type == typeof(short))
{
byte[] x = BitConverter.GetBytes((short)tmp[i]);
ms.Write(x, 0, x.Length);
}
else if (type == typeof(int))
{
byte[] x = BitConverter.GetBytes((int)tmp[i]);
ms.Write(x, 0, x.Length);
}
else if (type == typeof(long))
{
byte[] x = BitConverter.GetBytes((long)tmp[i]);
ms.Write(x, 0, x.Length);
}
else if (type == typeof(string))
{
byte[] x = System.Text.Encoding.UTF8.GetBytes((string)tmp[i]);
ms.Write(x, 0, x.Length);
ms.WriteByte(0);
}
else
throw new Exception(string.Format("The type {0} is not supported for primary keys", type.ToString()));
}
return Convert.ToBase64String(ms.ToArray());
}
}
///
/// Parses a base64 encoded string with key values
///
/// The base64 encoded ID string
/// The composite value key
public object[] ParseIDString(string id)
{
int index = 0;
byte[] data = Convert.FromBase64String(id);
object[] tmp = new object[m_layer.IDs.Count];
for (int i = 0; i < m_layer.IDs.Count; i++)
{
Type type = m_layer.IDs[i].Value;
if (type == typeof(short))
{
tmp[i] = BitConverter.ToInt16(data, index);
index += BinarySerializer.MgBinarySerializer.UInt16Len;
}
else if (type == typeof(int))
{
tmp[i] = BitConverter.ToInt32(data, index);
index += BinarySerializer.MgBinarySerializer.UInt32Len;
}
else if (type == typeof(long))
{
tmp[i] = BitConverter.ToInt64(data, index);
index += BinarySerializer.MgBinarySerializer.UInt64Len;
}
else if (type == typeof(string))
{
int pos = index;
while (pos < data.Length && data[pos] != 0)
pos++;
if (pos >= data.Length)
throw new Exception("Bad null encoded string");
tmp[i] = System.Text.Encoding.UTF8.GetString(data, index, pos - index);
index = pos + 1;
}
else
throw new Exception(string.Format("The type {0} is not supported for primary keys", type.ToString()));
}
return tmp;
}
///
/// Constructs a new LayerSelection with a number of selected featured
///
/// The layer to represent
/// The list of composite IDs that the layer supports
public LayerSelection(RuntimeClasses.RuntimeMapLayer layer, IEnumerable ids)
: this(layer)
{
AddRange(ids);
}
///
/// Constructs a new LayerSelection with a number of selected featured
///
/// The layer to represent
public LayerSelection(RuntimeClasses.RuntimeMapLayer layer)
{
if (layer == null)
throw new ArgumentNullException("layer");
if (layer.IDs == null || layer.IDs.Count == 0)
throw new Exception("The layer does not have a primary key, and cannot be used for selection");
m_layer = layer;
}
///
/// Adds a composite key to the selection
///
/// The composite key
public void Add(object[] values)
{
object[] tmp = NormalizeAndValidate(values);
if (!this.Contains(tmp))
m_list.Add(tmp);
}
///
/// Ensures that the composite key types match the layers ID column types.
/// The returned array is a copy of the one passed in
///
/// The composite key
/// A copy of the composite key
private object[] NormalizeAndValidate(object[] values)
{
if (values == null)
throw new ArgumentNullException("values");
if (values.Length != m_layer.IDs.Count)
throw new Exception(string.Format("The layers key consists of {0} columns, but only {1} columns were given", m_layer.IDs.Count, values.Length));
object[] tmp = new object[values.Length];
for (int i = 0; i < values.Length; i++)
{
if (values[i] == null)
throw new Exception(string.Format("The value for {0} is null, which is not supported as a key", m_layer.IDs[i].Key));
if (values[i].GetType() != m_layer.IDs[i].Value)
try { tmp[i] = Convert.ChangeType(values[i], m_layer.IDs[i].Value); }
catch (Exception ex) { throw new Exception(string.Format("Failed to convert value for {0} from {1} to {2}", m_layer.IDs[i].Key, values[i].GetType(), m_layer.IDs[i].Value), ex); }
else
tmp[i] = values[i];
}
return tmp;
}
///
/// Adds a number of composite keys
///
/// A list of composite keys
public void AddRange(IEnumerable lst)
{
foreach (object[] x in lst)
Add(x);
}
#region IList Members
///
/// Returns the index of the given composite key
///
/// The composite key to look for
/// The index of the composite key or -1 if the key is not found
public int IndexOf(object[] item)
{
object[] tmp = NormalizeAndValidate(item);
for (int i = 0; i < m_list.Count; i++)
{
object[] tmpx = m_list[i];
bool matches = true;
for (int j = 0; j < tmpx.Length; j++)
if (tmpx[j] != item[j])
{
matches = false;
break;
}
if (matches)
return i;
}
return -1;
}
///
/// Inserts a key at the specified location
///
/// The index to insert the key at
/// The key to insert
public void Insert(int index, object[] item)
{
object[] tmp = NormalizeAndValidate(item);
int ix = IndexOf(tmp);
if (ix >= 0)
RemoveAt(ix);
Insert(index, item);
}
///
/// Removes the element at the specified location
///
/// The index of the item to remove
public void RemoveAt(int index)
{
m_list.RemoveAt(index);
}
///
/// Gets or sets the composite key for the specified index
///
/// The index for the composite key
/// The composite key
public object[] this[int index]
{
get
{
object[] tmp = new object[m_layer.IDs.Count];
Array.Copy(m_list[index], tmp, tmp.Length);
return tmp;
}
set
{
m_list[index] = NormalizeAndValidate(value);
}
}
#endregion
#region ICollection Members
///
/// Removes all composite keys from the collection
///
public void Clear()
{
m_list.Clear();
}
///
/// Returns a value indicating if the composite key is contained in the collection
///
/// The composite key to look for
/// True if the collection contains the composite key, false otherwise
public bool Contains(object[] item)
{
return IndexOf(item) >= 0;
}
///
/// Not implemented
///
///
///
public void CopyTo(object[][] array, int arrayIndex)
{
throw new NotImplementedException();
}
///
/// Returns the number of composite keys (and thus selected objects)
///
public int Count
{
get { return m_list.Count; }
}
///
/// Gets a value indicating if the collection is read-only
///
public bool IsReadOnly
{
get { return false; }
}
///
/// Removes the given composite key from the collection
///
/// The composite key to remove
/// True if the composite key was found and removed, false otherwise
public bool Remove(object[] item)
{
int ix = IndexOf(item);
if (ix < 0)
return false;
m_list.RemoveAt(ix);
return true;
}
#endregion
#region IEnumerable Members
///
/// Returns an enumerator for the collection
///
/// An enumerator for the collection
public IEnumerator GetEnumerator()
{
return m_list.GetEnumerator();
}
#endregion
#region IEnumerable Members
///
/// Returns an enumerator for the collection
///
/// An enumerator for the collection
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((System.Collections.IEnumerable)m_list).GetEnumerator();
}
#endregion
}
#region IList Members
///
/// Returns the index of the given layer
///
/// The layer to look for
/// The index of the layer, or -1 if the layer is not in the collection
public int IndexOf(Selection.LayerSelection item)
{
return IndexOf(item.Layer);
}
///
/// Returns the index of the given layer
///
/// The layer to look for
/// The index of the layer, or -1 if the layer is not in the collection
public int IndexOf(RuntimeClasses.RuntimeMapLayer layer)
{
for (int i = 0; i < m_layers.Count; i++)
if (m_layers[i].Layer.Guid == layer.Guid)
return i;
return 1;
}
///
/// Inserts a selection layer into the collection
///
/// The index to place the item at
/// The item to insert
public void Insert(int index, Selection.LayerSelection item)
{
m_layers.Insert(index, item);
}
///
/// Inserts a selection layer into the collection
///
/// The index to place the item at
/// The item to insert
public void Insert(int index, RuntimeClasses.RuntimeMapLayer layer)
{
m_layers.Insert(index, new LayerSelection(layer));
}
///
/// Removes the item at the given index
///
/// The index to remove the item at
public void RemoveAt(int index)
{
m_layers.RemoveAt(index);
}
///
/// Gets or sets the selection layer at a given index
///
/// The index to get or set the item for
/// The item at the given index
public Selection.LayerSelection this[int index]
{
get
{
return m_layers[index];
}
set
{
if (value == null)
throw new ArgumentNullException();
m_layers[index] = value;
}
}
///
/// Gets or sets the selection layer at a given index
///
/// The index to get or set the item for
/// The item at the given index
public Selection.LayerSelection this[RuntimeClasses.RuntimeMapLayer index]
{
get
{
return m_layers[IndexOf(index)];
}
set
{
if (value == null)
throw new ArgumentNullException();
m_layers[IndexOf(index)] = value;
}
}
#endregion
#region ICollection Members
public void Add(Selection.LayerSelection item)
{
if (item == null)
throw new ArgumentNullException();
m_layers.Add(item);
}
public void Add(RuntimeClasses.RuntimeMapLayer layer)
{
if (!Contains(layer))
Add(new LayerSelection(layer));
}
public void Clear()
{
m_layers.Clear();
}
public bool Contains(RuntimeClasses.RuntimeMapLayer item)
{
return IndexOf(item) >= 0;
}
public bool Contains(Selection.LayerSelection item)
{
return IndexOf(item) >= 0;
}
public void CopyTo(Selection.LayerSelection[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public int Count
{
get { return m_layers.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(Selection.LayerSelection item)
{
int ix = IndexOf(item);
if (ix < 0)
return false;
m_layers.RemoveAt(ix);
return true;
}
#endregion
#region IEnumerable Members
public IEnumerator GetEnumerator()
{
return m_layers.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((System.Collections.IEnumerable)m_layers).GetEnumerator();
}
#endregion
}
}