using System;
using System.Collections.Generic;
using System.Text;
using OSGeo.MapGuide.MaestroAPI.Serialization;
using System.Xml;
namespace OSGeo.MapGuide.MaestroAPI.Mapping
{
///
/// Represents a map selection
///
public class MapSelection : IBinarySerializable, IList
{
private RuntimeMap _map;
private List _layers;
///
/// Constructor
///
///
public MapSelection(RuntimeMap map)
{
_map = map;
_layers = new List();
}
///
/// Constructor
///
///
///
public MapSelection(RuntimeMap map, string xml)
: this(map)
{
LoadXml(xml);
}
///
/// Initialize this selection from the specified xml string
///
///
public void LoadXml(string xml)
{
_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;
var l = _map.GetLayerByObjectId(guid);
if (l != null)
{
foreach (System.Xml.XmlNode c in n.SelectNodes("Class"))
{
if (c.Attributes["id"] != null)
if (c.Attributes["id"].Value == l.QualifiedClassName)
_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 _layers)
{
System.Xml.XmlNode ln = root.AppendChild(doc.CreateElement("Layer"));
ln.Attributes.Append(doc.CreateAttribute("id")).Value = layer.Layer.ObjectId;
System.Xml.XmlNode cn = ln.AppendChild(doc.CreateElement("Class"));
cn.Attributes.Append(doc.CreateAttribute("id")).Value = layer.Layer.QualifiedClassName;
for (int i = 0; i < layer.Count; i++)
cn.AppendChild(doc.CreateElement("ID")).InnerText = layer.EncodeIDString(layer[i]);
}
return doc.OuterXml;
}
///
/// Represents a layer selection
///
public class LayerSelection : IList
{
private RuntimeMapLayer m_layer;
private List m_list = new List();
///
/// Gets the layer that contains the selected objects
///
public 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(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.IdentityProperties.Length; i++)
{
Type type = m_layer.IdentityProperties[i].Type;
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.IdentityProperties.Length];
for (int i = 0; i < m_layer.IdentityProperties.Length; i++)
{
Type type = m_layer.IdentityProperties[i].Type;
if (type == typeof(short))
{
tmp[i] = BitConverter.ToInt16(data, index);
index += MgBinarySerializer.UInt16Len;
}
else if (type == typeof(int))
{
tmp[i] = BitConverter.ToInt32(data, index);
index += MgBinarySerializer.UInt32Len;
}
else if (type == typeof(long))
{
tmp[i] = BitConverter.ToInt64(data, index);
index += 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(RuntimeMapLayer layer, IEnumerable ids)
: this(layer)
{
AddRange(ids);
}
///
/// Constructs a new LayerSelection with a number of selected featured
///
/// The layer to represent
public LayerSelection(RuntimeMapLayer layer)
{
if (layer == null)
throw new ArgumentNullException("layer");
if (layer.IdentityProperties.Length == 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.IdentityProperties.Length)
throw new Exception(string.Format("The layers key consists of {0} columns, but only {1} columns were given", m_layer.IdentityProperties.Length, 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.IdentityProperties[i].Name));
if (values[i].GetType() != m_layer.IdentityProperties[i].Type)
try { tmp[i] = Convert.ChangeType(values[i], m_layer.IdentityProperties[i].Type); }
catch (Exception ex) { throw new Exception(string.Format("Failed to convert value for {0} from {1} to {2}", m_layer.IdentityProperties[i].Name, values[i].GetType(), m_layer.IdentityProperties[i].Type), 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.IdentityProperties.Length];
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
}
public void Serialize(MgBinarySerializer s)
{
var m_selection = new XmlDocument();
m_selection.LoadXml(ToXml());
if (m_selection["FeatureSet"] == null)
{
s.Write((int)0);
return;
}
XmlNodeList lst = m_selection["FeatureSet"].SelectNodes("Layer");
s.Write(lst.Count);
foreach (XmlNode n in lst)
{
if (n.Attributes["id"] == null)
throw new Exception("A layer in selection had no id");
s.Write(n.Attributes["id"].Value);
XmlNodeList cls = n.SelectNodes("Class");
s.Write(cls.Count);
foreach (XmlNode c in cls)
{
s.Write(c.Attributes["id"].Value);
XmlNodeList ids = c.SelectNodes("ID");
s.Write(ids.Count);
foreach (XmlNode id in ids)
s.Write(id.InnerText);
}
}
}
public void Deserialize(MgBinaryDeserializer d)
{
XmlDocument doc = new XmlDocument();
XmlNode root = doc.AppendChild(doc.CreateElement("FeatureSet"));
int layerCount = d.ReadInt32();
for (int i = 0; i < layerCount; i++)
{
XmlNode layer = root.AppendChild(doc.CreateElement("Layer"));
layer.Attributes.Append(doc.CreateAttribute("id")).Value = d.ReadString();
int classCount = d.ReadInt32();
for (int j = 0; j < classCount; j++)
{
XmlNode @class = layer.AppendChild(doc.CreateElement("Class"));
@class.Attributes.Append(doc.CreateAttribute("id")).Value = d.ReadString();
int idCount = d.ReadInt32();
for (int k = 0; k < idCount; k++)
@class.AppendChild(doc.CreateElement("ID")).InnerText = d.ReadString();
}
}
LoadXml(doc.OuterXml);
}
#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(MapSelection.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(RuntimeMapLayer layer)
{
for (int i = 0; i < _layers.Count; i++)
if (_layers[i].Layer.ObjectId == layer.ObjectId)
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, MapSelection.LayerSelection item)
{
_layers.Insert(index, item);
}
///
/// Inserts a selection layer into the collection
///
/// The index to place the item at
/// The layer.
public void Insert(int index, RuntimeMapLayer layer)
{
_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)
{
_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 MapSelection.LayerSelection this[int index]
{
get
{
return _layers[index];
}
set
{
if (value == null)
throw new ArgumentNullException();
_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 MapSelection.LayerSelection this[RuntimeMapLayer index]
{
get
{
return _layers[IndexOf(index)];
}
set
{
if (value == null)
throw new ArgumentNullException();
_layers[IndexOf(index)] = value;
}
}
#endregion
#region ICollection Members
///
/// Adds the specified layer selection
///
///
public void Add(MapSelection.LayerSelection item)
{
if (item == null)
throw new ArgumentNullException();
_layers.Add(item);
}
///
/// Adds the specified layer
///
///
public void Add(RuntimeMapLayer layer)
{
if (!Contains(layer))
Add(new LayerSelection(layer));
}
///
/// Clears this selction
///
public void Clear()
{
_layers.Clear();
}
///
/// Gets whether this selection contains the specified layer
///
///
///
public bool Contains(RuntimeMapLayer item)
{
return IndexOf(item) >= 0;
}
///
/// Gets whether this selection contains the specified layer selection
///
///
///
public bool Contains(MapSelection.LayerSelection item)
{
return IndexOf(item) >= 0;
}
public void CopyTo(MapSelection.LayerSelection[] array, int arrayIndex)
{
throw new NotImplementedException();
}
///
/// Gets the number of layers in this selection
///
public int Count
{
get { return _layers.Count; }
}
///
/// Gets whether this is read only
///
public bool IsReadOnly
{
get { return false; }
}
///
/// Removes the specified layer selection
///
///
///
public bool Remove(MapSelection.LayerSelection item)
{
int ix = IndexOf(item);
if (ix < 0)
return false;
_layers.RemoveAt(ix);
return true;
}
#endregion
#region IEnumerable Members
///
/// Gets the layer selection enumerator
///
///
public IEnumerator GetEnumerator()
{
return _layers.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((System.Collections.IEnumerable)_layers).GetEnumerator();
}
#endregion
}
}