Skip to content
Browse files

added CQL3 collections support, unit tests, and integration tests.

upped the version number to 1.2.1
  • Loading branch information...
1 parent 8664730 commit 504a4f9d41802f00e8f3fdc59805d265f6a7da1c @Aaronontheweb Aaronontheweb committed Oct 30, 2013
Showing with 2,177 additions and 25 deletions.
  1. +7 −0 src/FluentCassandra.csproj
  2. +2 −2 src/Properties/AssemblyInfo.cs
  3. +10 −0 src/Types/AsciiType.cs
  4. +9 −0 src/Types/BooleanType.cs
  5. +10 −0 src/Types/BytesType.cs
  6. +4 −0 src/Types/CassandraObject.cs
  7. +37 −1 src/Types/CassandraType.cs
  8. +10 −0 src/Types/CompositeType.cs
  9. +10 −0 src/Types/DateType.cs
  10. +10 −0 src/Types/DecimalType.cs
  11. +10 −0 src/Types/DoubleType.cs
  12. +10 −0 src/Types/DynamicCompositeType.cs
  13. +15 −5 src/Types/EmptyType.cs
  14. +10 −0 src/Types/FloatType.cs
  15. +11 −0 src/Types/InetAddressType.cs
  16. +10 −0 src/Types/Int32Type.cs
  17. +9 −0 src/Types/IntegerType.cs
  18. +10 −0 src/Types/LexicalUUIDType.cs
  19. +268 −0 src/Types/ListType.cs
  20. +183 −0 src/Types/ListTypeConverter.cs
  21. +10 −0 src/Types/LongType.cs
  22. +275 −0 src/Types/MapType.cs
  23. +235 −0 src/Types/MapTypeConverter.cs
  24. +11 −1 src/Types/NullType.cs
  25. +11 −1 src/Types/ReversedType.cs
  26. +306 −0 src/Types/SetType.cs
  27. +15 −0 src/Types/SetTypeConverter.cs
  28. +10 −0 src/Types/TimeUUIDType.cs
  29. +107 −0 src/Types/TypeHelper.cs
  30. +12 −1 src/Types/UTF8Type.cs
  31. +11 −1 src/Types/UUIDType.cs
  32. +11 −1 src/Types/VoidType.cs
  33. +24 −12 test/FluentCassandra.Integration.Tests/CassandraDatabaseSetup.cs
  34. +4 −0 test/FluentCassandra.Integration.Tests/FluentCassandra.Integration.Tests.csproj
  35. +108 −0 test/FluentCassandra.Integration.Tests/Operations/Cql3CollectionsTest.cs
  36. +6 −0 test/FluentCassandra.Tests/FluentCassandra.Tests.csproj
  37. +120 −0 test/FluentCassandra.Tests/Types/ListTypeTest.cs
  38. +136 −0 test/FluentCassandra.Tests/Types/MapTypeTest.cs
  39. +120 −0 test/FluentCassandra.Tests/Types/SetTypeTest.cs
View
7 src/FluentCassandra.csproj
@@ -295,10 +295,17 @@
<Compile Include="Types\IntegerTypeConverter.cs" />
<Compile Include="Types\IntegerType.cs" />
<Compile Include="Types\LexicalUUIDTypeConverter.cs" />
+ <Compile Include="Types\ListType.cs" />
+ <Compile Include="Types\ListTypeConverter.cs" />
<Compile Include="Types\LongTypeConverter.cs" />
+ <Compile Include="Types\MapType.cs" />
+ <Compile Include="Types\MapTypeConverter.cs" />
<Compile Include="Types\NullType.cs" />
<Compile Include="Types\ReversedType.cs" />
+ <Compile Include="Types\SetType.cs" />
+ <Compile Include="Types\SetTypeConverter.cs" />
<Compile Include="Types\TimeUUIDTypeConverter.cs" />
+ <Compile Include="Types\TypeHelper.cs" />
<Compile Include="Types\UTF8TypeConverter.cs" />
<Compile Include="Types\LexicalUUIDType.cs" />
<Compile Include="IFluentMutationTracker.cs" />
View
4 src/Properties/AssemblyInfo.cs
@@ -31,5 +31,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.2.0.0")]
-[assembly: AssemblyFileVersion("1.2.0.0")]
+[assembly: AssemblyVersion("1.2.1.0")]
+[assembly: AssemblyFileVersion("1.2.1.0")]
View
10 src/Types/AsciiType.cs
@@ -95,6 +95,16 @@ private static AsciiType ConvertFrom(object o)
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
}
}
View
9 src/Types/BooleanType.cs
@@ -87,6 +87,15 @@ private static BooleanType ConvertFrom(object o)
type.SetValue(o);
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
#endregion
}
View
10 src/Types/BytesType.cs
@@ -194,6 +194,16 @@ private static BytesType ConvertFrom(object o)
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
}
}
View
4 src/Types/CassandraObject.cs
@@ -34,6 +34,10 @@ public object GetValue(Type type)
return GetValueInternal(type);
}
+ public abstract bool CanConvertFrom(Type sourceType);
+
+ public abstract bool CanConvertTo(Type destinationType);
+
public abstract object GetValue();
public abstract void SetValue(object obj);
View
38 src/Types/CassandraType.cs
@@ -27,6 +27,9 @@ public sealed class CassandraType
public static readonly CassandraType EmptyType = new CassandraType("org.apache.cassandra.db.marshal.EmptyType");
public static readonly CassandraType InetAddressType = new CassandraType("org.apache.cassandra.db.marshal.InetAddressType");
+ private static readonly CassandraType _MapType = new CassandraType("org.apache.cassandra.db.marshal.MapType");
+ private static readonly CassandraType _ListType = new CassandraType("org.apache.cassandra.db.marshal.ListType");
+ private static readonly CassandraType _SetType = new CassandraType("org.apache.cassandra.db.marshal.SetType");
private static readonly CassandraType _CompositeType = new CassandraType("org.apache.cassandra.db.marshal.CompositeType");
private static readonly CassandraType _DynamicCompositeType = new CassandraType("org.apache.cassandra.db.marshal.DynamicCompositeType");
@@ -118,11 +121,41 @@ private void Parse(string dbType)
ParseDynamicCompositeType(part2);
else if (_type == typeof(ReversedType))
ParseReversedType(part2);
+ else if (_type == typeof (ListType<>))
+ ParseListType(part2);
+ else if (_type == typeof(SetType<>))
+ ParseSetType(part2);
+ else if (_type == typeof(MapType<,>))
+ ParseMapType(part2);
else
throw new CassandraException("Type '" + dbType + "' not found.");
}
- private void ParseReversedType(string part)
+ private void ParseMapType(string part)
+ {
+ part = part.Trim('(', ')');
+ var parts = part.Split(',');
+
+ var keyType = GetSystemType(parts[0]);
+ var valueType = GetSystemType(parts[1]);
+ _type = _type.MakeGenericType(keyType, valueType);
+ }
+
+ private void ParseSetType(string part)
+ {
+ //construct the generic SetType (has an indentical implmentation to ListType)
+ ParseListType(part);
+ }
+
+ private void ParseListType(string part)
+ {
+ //construct the generic ListType
+ part = part.Trim('(', ')');
+ var listType = GetSystemType(part);
+ _type = _type.MakeGenericType(listType);
+ }
+
+ private void ParseReversedType(string part)
{
part = part.Trim('(', ')');
_typeReversed = true;
@@ -353,6 +386,9 @@ public static Type GetSystemType(string dbType)
case "reversedtype": type = typeof(ReversedType); break;
case "emptytype": type = typeof(EmptyType); break;
case "inetaddresstype": type = typeof(InetAddressType); break;
+ case "listtype": type = typeof (ListType<>); break;
+ case "settype": type = typeof (SetType<>); break;
+ case "maptype": type = typeof(MapType<,>); break;
default: throw new CassandraException("Type '" + dbType + "' not found.");
}
View
10 src/Types/CompositeType.cs
@@ -216,6 +216,16 @@ private static CompositeType ConvertFrom(object o)
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
#region IList<CassandraType> Members
View
10 src/Types/DateType.cs
@@ -96,6 +96,16 @@ private static DateType ConvertFrom(object o)
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
}
}
View
10 src/Types/DecimalType.cs
@@ -102,6 +102,16 @@ private static DecimalType ConvertFrom(object o)
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
}
}
View
10 src/Types/DoubleType.cs
@@ -88,6 +88,16 @@ private static DoubleType ConvertFrom(object o)
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
}
}
View
10 src/Types/DynamicCompositeType.cs
@@ -159,6 +159,16 @@ private static DynamicCompositeType ConvertFrom(object o)
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
#region IList<CassandraType> Members
View
20 src/Types/EmptyType.cs
@@ -54,12 +54,12 @@ public override void SetValueFromBigEndian(byte[] value)
#endregion
- public override object GetValue()
- {
- return _value;
- }
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return true;
+ }
- private readonly byte[] _value = new byte[0];
+ private readonly byte[] _value = new byte[0];
#region Equality
@@ -178,6 +178,16 @@ private static EmptyType ConvertFrom(object o)
return type;
}
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return true;
+ }
+
+ public override object GetValue()
+ {
+ return _value;
+ }
+
#endregion
}
}
View
10 src/Types/FloatType.cs
@@ -88,6 +88,16 @@ private static FloatType ConvertFrom(object o)
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
}
}
View
11 src/Types/InetAddressType.cs
@@ -81,6 +81,17 @@ private static InetAddressType ConvertFrom(object o)
type.SetValue(o);
return type;
}
+
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
}
}
View
10 src/Types/Int32Type.cs
@@ -88,6 +88,16 @@ private static Int32Type ConvertFrom(object o)
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
}
}
View
9 src/Types/IntegerType.cs
@@ -118,6 +118,15 @@ private static IntegerType ConvertFrom(object o)
type.SetValue(o);
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
#endregion
}
View
10 src/Types/LexicalUUIDType.cs
@@ -91,6 +91,16 @@ private static LexicalUUIDType ConvertFrom(object o)
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
}
}
View
268 src/Types/ListType.cs
@@ -0,0 +1,268 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace FluentCassandra.Types
+{
+ public class ListType<T> : CassandraObject, IList<T> where T : CassandraObject
+ {
+ private static readonly ListTypeConverter<T> Converter = new ListTypeConverter<T>();
+
+ #region Create
+
+ public ListType() : this(new List<T>())
+ {
+ }
+
+ public ListType(IEnumerable<T> objects)
+ {
+ _value = new List<T>(objects);
+ }
+
+ #endregion
+
+ private List<T> _value;
+
+ public static CassandraType ComponentType {
+ get { return (CassandraType)typeof(T); }
+ }
+
+ #region Implementation
+
+ public override object GetValue()
+ {
+ return _value;
+ }
+
+ public override void SetValue(object obj)
+ {
+ if (obj != null)
+ _value = Converter.ConvertFrom(obj);
+ }
+
+ protected override object GetValueInternal(Type type)
+ {
+ //If we're converting to a List<> type
+ if (type.IsList())
+ {
+ var listType = type.GetPrimaryGenericType();
+
+ //Check to see if this list's data type can be converted to the requested type
+ if (ComponentType.CreateInstance().CanConvertTo(listType))
+ {
+ return TypeHelper.PopulateGenericList(_value.Select(o => (CassandraObject)o).ToList(), listType);
+ }
+ }
+
+ return Converter.ConvertTo(_value, type);
+ }
+
+ protected override TypeCode TypeCode
+ {
+ get { return TypeCode.Object; }
+ }
+
+ public override byte[] ToBigEndian()
+ {
+ return Converter.ToBigEndian(_value);
+ }
+
+ public override void SetValueFromBigEndian(byte[] value)
+ {
+ _value = Converter.FromBigEndian(value);
+ }
+
+ #endregion
+
+ #region Equality
+
+ public override bool Equals(object obj)
+ {
+ List<T> objArray;
+
+ if (obj is List<T>)
+ objArray = ((ListType<T>)obj)._value;
+ else
+ objArray = Converter.ConvertFrom(obj);
+
+ if (objArray == null)
+ return false;
+
+ if (objArray.Count != _value.Count)
+ return false;
+
+ for (int i = 0; i < objArray.Count; i++)
+ {
+ if (!_value[i].Equals(objArray[i]))
+ return false;
+ }
+
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hash = 17;
+ foreach (var keyPart in _value)
+ {
+ hash = hash * 23 + keyPart.GetHashCode();
+ }
+ return hash;
+ }
+ }
+
+ #endregion
+
+ #region Conversion
+
+ public static implicit operator List<T>(ListType<T> type)
+ {
+ return type._value;
+ }
+
+ public static implicit operator ListType<T>(List<T> obj)
+ {
+ return new ListType<T>(obj);
+ }
+
+ public static ListType<T> From<TIn>(List<TIn> obj)
+ {
+ //Check to make sure we can convert
+ if (ComponentType.CreateInstance().CanConvertFrom(typeof(TIn)))
+ {
+ return new ListType<T>(obj.Select(o => (T) CassandraObject.GetCassandraObjectFromObject(o, typeof (T))));
+ }
+
+ throw new ArgumentException(string.Format("can't convert list of type {0} to ListType of type {1}", typeof(TIn), typeof(T)));
+ }
+
+ public static List<TOut> To<TOut>(ListType<T> obj)
+ {
+ //Check to make sure we can convert
+ if (ComponentType.CreateInstance().CanConvertTo(typeof(TOut)))
+ {
+ return new List<TOut>(obj.Select(o => o.GetValue<TOut>()));
+ }
+
+ throw new ArgumentException(string.Format("can't convert ListType of type {1} to List of type {0} to ", typeof(T), typeof(TOut)));
+ }
+
+ public static implicit operator byte[](ListType<T> o) { return ConvertTo<byte[]>(o); }
+ public static implicit operator ListType<T>(byte[] o) { return ConvertFrom(o); }
+
+ public static implicit operator ListType<T>(object[] s)
+ {
+ return new ListType<T>{ _value = new List<T>(s.Select(o => (T)CassandraObject.GetCassandraObjectFromObject(o, typeof(T)))) };
+ }
+
+ public static implicit operator ListType<T>(List<object> s)
+ {
+ return new ListType<T>{ _value = new List<T>(s.Select(o => (T)CassandraObject.GetCassandraObjectFromObject(o, typeof(T)))) };
+ }
+
+ private static TOut ConvertTo<TOut>(ListType<T> type)
+ {
+ if (type == null)
+ return default(TOut);
+
+ return type.GetValue<TOut>();
+ }
+
+ private static ListType<T> ConvertFrom(object o)
+ {
+ var type = new ListType<T>();
+ type.SetValue(o);
+ return type;
+ }
+
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
+ #endregion
+
+ #region IList<CassandraObject> Members
+
+ public int IndexOf(T item)
+ {
+ return _value.IndexOf(item);
+ }
+
+ public void Insert(int index, T item)
+ {
+ _value.Insert(index, item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ _value.RemoveAt(index);
+ }
+
+ public T this[int index]
+ {
+ get { return _value[index]; }
+ set { _value[index] = value; }
+ }
+
+ #endregion
+
+ #region ICollection<CassandraType> Members
+
+ public void Add(T item)
+ {
+ _value.Add(item);
+ }
+
+ public void Clear()
+ {
+ _value.Clear();
+ }
+
+ public bool Contains(T item)
+ {
+ return _value.Contains(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ _value.CopyTo(array, arrayIndex);
+ }
+
+ public bool Remove(T item)
+ {
+ return _value.Remove(item);
+ }
+
+ public int Count { get { return _value.Count; } }
+ public bool IsReadOnly { get { return ((ICollection<CassandraObject>)_value).IsReadOnly; } }
+
+ #endregion
+
+ #region IEnumerable<CassandraType> Members
+
+ public IEnumerator<T> GetEnumerator()
+ {
+ return _value.GetEnumerator();
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #endregion
+ }
+}
View
183 src/Types/ListTypeConverter.cs
@@ -0,0 +1,183 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace FluentCassandra.Types
+{
+ internal class ListTypeConverter<T> : CassandraObjectConverter<List<T>> where T : CassandraObject
+ {
+ protected virtual string CollectionStringBegin { get { return "["; } }
+
+ protected virtual string CollectionStringEnd { get { return "]"; } }
+
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return sourceType == typeof(byte[]) ||
+ sourceType.GetInterfaces().Contains(typeof(IEnumerable<T>)) ||
+ sourceType.GetInterfaces().Contains(typeof(IEnumerable<object>));
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return destinationType == typeof(byte[]) ||
+ destinationType == typeof(List<T>) ||
+ destinationType == typeof(List<CassandraObject>) ||
+ destinationType == typeof(CassandraObject[]) ||
+ destinationType == typeof(List<object>) ||
+ destinationType == typeof(object[]) ||
+ destinationType == typeof(string);
+ }
+
+ public override List<T> ConvertFromInternal(object value)
+ {
+ if (value is byte[])
+ {
+ var components = new List<T>();
+
+ var typeHint = typeof(T);
+
+ using (var bytes = new MemoryStream((byte[])value))
+ {
+ // number of elements
+ var numElementsBytes = new byte[2];
+ if (bytes.Read(numElementsBytes, 0, 2) <= 0)
+ return components;
+
+ var nElements = BitConverter.ToUInt16(numElementsBytes, 0);
+ for (var i = 0; i < nElements; i++)
+ {
+ //get the length of this element
+ var elementLengthBytes = new byte[2];
+ bytes.Read(elementLengthBytes, 0, 2);
+ var elementLength = BitConverter.ToUInt16(elementLengthBytes, 0);
+
+ //read the content of the element into a buffer
+ var buffer = new byte[elementLength];
+ bytes.Read(buffer, 0, elementLength);
+ var component = CassandraObject.GetCassandraObjectFromDatabaseByteArray(buffer, typeHint);
+ components.Add((T)component);
+ }
+ }
+
+ return components;
+ }
+
+ if (value.GetType().GetInterfaces().Contains(typeof(IEnumerable<object>)))
+ return new List<T>(((IEnumerable<object>)value).Cast<T>());
+
+ if (value.GetType().GetInterfaces().Contains(typeof(IEnumerable<T>)))
+ return new List<T>((IEnumerable<T>)value);
+
+ return null;
+ }
+
+ public override object ConvertToInternal(List<T> value, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ return CollectionStringBegin + String.Join(",", value) + CollectionStringEnd; //should format the list into a JSON-esque object
+
+ if (destinationType == typeof(byte[]))
+ {
+ var components = value;
+
+ using (var bytes = new MemoryStream())
+ {
+ //write the number of lengths
+ var elements = (ushort)components.Count;
+ bytes.Write(BitConverter.GetBytes(elements), 0, 2);
+
+ foreach (var c in components)
+ {
+ var b = c.ToBigEndian();
+ var length = (ushort)b.Length;
+
+ // value length
+ bytes.Write(BitConverter.GetBytes(length), 0, 2);
+
+ // value
+ bytes.Write(b, 0, length);
+ }
+
+ return bytes.ToArray();
+ }
+
+ }
+
+ if (destinationType == typeof(CassandraObject[]))
+ return value.ToArray();
+
+ if (destinationType == typeof(object[]))
+ return value.Cast<object>().ToArray();
+
+ if (destinationType == typeof(List<T>))
+ return value;
+
+ if (destinationType == typeof(List<CassandraObject>))
+ return value;
+
+ if (destinationType == typeof(List<object>))
+ return value.Cast<object>().ToList();
+
+ return null;
+ }
+
+ public override byte[] ToBigEndian(List<T> value)
+ {
+ var components = value;
+
+ using (var bytes = new MemoryStream())
+ {
+ //write the number of lengths
+ var elements = (ushort)components.Count;
+ bytes.Write(ConvertEndian(BitConverter.GetBytes(elements)), 0, 2);
+
+ foreach (var c in components)
+ {
+ var b = c.ToBigEndian();
+ var length = (ushort)b.Length;
+
+ // value length
+ bytes.Write(ConvertEndian(BitConverter.GetBytes(length)), 0, 2);
+
+ // value
+ bytes.Write(b, 0, length);
+ }
+
+ return bytes.ToArray();
+ }
+ }
+
+ public override List<T> FromBigEndian(byte[] value)
+ {
+ var components = new List<T>();
+
+ var typeHint = typeof(T);
+
+ using (var bytes = new MemoryStream(value))
+ {
+ // number of elements
+ var numElementsBytes = new byte[2];
+ if (bytes.Read(numElementsBytes, 0, 2) <= 0)
+ return components;
+
+ var nElements = BitConverter.ToUInt16(ConvertEndian(numElementsBytes), 0);
+ for (var i = 0; i < nElements; i++)
+ {
+ //get the length of this element
+ var elementLengthBytes = new byte[2];
+ bytes.Read(elementLengthBytes, 0, 2);
+ var elementLength = BitConverter.ToUInt16(ConvertEndian(elementLengthBytes), 0);
+
+ //read the content of the element into a buffer
+ var buffer = new byte[elementLength];
+ bytes.Read(buffer, 0, elementLength);
+ var component = CassandraObject.GetCassandraObjectFromDatabaseByteArray(buffer, typeHint);
+ components.Add((T)component);
+ }
+ }
+
+ return components;
+ }
+ }
+}
View
10 src/Types/LongType.cs
@@ -118,6 +118,16 @@ private static LongType ConvertFrom(object o)
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
}
}
View
275 src/Types/MapType.cs
@@ -0,0 +1,275 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace FluentCassandra.Types
+{
+ public class MapType<TKey, TValue> : CassandraObject, IDictionary<TKey, TValue> where TKey : CassandraObject where TValue : CassandraObject
+ {
+ private static readonly MapTypeConverter<TKey, TValue> Converter = new MapTypeConverter<TKey, TValue>();
+
+ #region Create
+
+ public MapType() : this(new Dictionary<TKey, TValue>()) { }
+
+ public MapType(IDictionary<TKey, TValue> objects)
+ {
+ _value = new Dictionary<TKey, TValue>(objects);
+ }
+
+ #endregion
+
+ private Dictionary<TKey, TValue> _value;
+
+ public static CassandraType KeyType
+ {
+ get { return (CassandraType)typeof(TKey); }
+ }
+
+ public static CassandraType ValueType
+ {
+ get { return (CassandraType)typeof(TValue); }
+ }
+
+ #region Implementation
+
+ public override object GetValue()
+ {
+ return _value;
+ }
+
+ public override void SetValue(object obj)
+ {
+ if (obj != null)
+ _value = Converter.ConvertFrom(obj);
+ }
+
+ protected override object GetValueInternal(Type type)
+ {
+ //If we're converting to a Dictionary<> type
+ if (type.IsDictionary())
+ {
+ var dictionaryTypes = type.GetAllGenericTypes();
+
+ //Check to see if this list's data type can be converted to the requested type
+ if (KeyType.CreateInstance().CanConvertTo(dictionaryTypes[0]) && ValueType.CreateInstance().CanConvertTo(dictionaryTypes[1]))
+ {
+ return
+ TypeHelper.PopulateGenericDictionary(
+ _value.ToDictionary(k => (CassandraObject) k.Key, v => (CassandraObject) v.Value),
+ dictionaryTypes[0], dictionaryTypes[1]);
+ }
+ }
+
+ return Converter.ConvertTo(_value, type);
+ }
+
+ protected override TypeCode TypeCode
+ {
+ get { return TypeCode.Object; }
+ }
+
+ public override byte[] ToBigEndian()
+ {
+ return Converter.ToBigEndian(_value);
+ }
+
+ public override void SetValueFromBigEndian(byte[] value)
+ {
+ _value = Converter.FromBigEndian(value);
+ }
+
+ #endregion
+
+ #region Equality
+
+ public override bool Equals(object obj)
+ {
+ Dictionary<TKey, TValue> objDict;
+
+ if (obj is Dictionary<TKey, TValue>)
+ objDict = ((MapType<TKey, TValue>)obj)._value;
+ else
+ objDict = Converter.ConvertFrom(obj);
+
+ if (objDict == null)
+ return false;
+
+ if (objDict.Count != _value.Count)
+ return false;
+
+ foreach(var dictItem in objDict)
+ {
+ if (!_value[dictItem.Key].Equals(objDict[dictItem.Key]))
+ return false;
+ }
+
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hash = 17;
+ foreach (var keyPart in _value)
+ {
+ hash = hash * 23 + keyPart.GetHashCode();
+ }
+ return hash;
+ }
+ }
+
+ #endregion
+
+ #region Conversion
+
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
+ public static implicit operator MapType<TKey, TValue>(List<KeyValuePair<TKey, TValue>> obj) { return new MapType<TKey, TValue>(obj.ToDictionary(k => k.Key, v => v.Value)); }
+ public static implicit operator KeyValuePair<TKey, TValue>[](MapType<TKey, TValue> type) { return type._value.ToArray(); }
+ public static implicit operator List<KeyValuePair<TKey, TValue>>(MapType<TKey, TValue> type) { return type._value.ToList(); }
+ public static implicit operator Dictionary<TKey, TValue>(MapType<TKey, TValue> type){ return type._value; }
+ public static implicit operator Dictionary<CassandraObject, CassandraObject>(MapType<TKey, TValue> type) { return type._value.ToDictionary(x => (CassandraObject)x.Key, v => (CassandraObject)v.Value); }
+ public static implicit operator MapType<TKey, TValue>(Dictionary<TKey, TValue> obj){ return new MapType<TKey, TValue>(obj); }
+ public static implicit operator byte[](MapType<TKey, TValue> o) { return ConvertTo<byte[]>(o); }
+ public static implicit operator MapType<TKey, TValue>(byte[] o) { return ConvertFrom(o); }
+
+ private static TOut ConvertTo<TOut>(MapType<TKey, TValue> type)
+ {
+ if (type == null)
+ return default(TOut);
+
+ return type.GetValue<TOut>();
+ }
+
+ private static MapType<TKey, TValue> ConvertFrom(object o)
+ {
+ var type = new MapType<TKey, TValue>();
+ type.SetValue(o);
+ return type;
+ }
+
+ public static MapType<TKey, TValue> From<TKeyIn, TValueIn>(IDictionary<TKeyIn, TValueIn> obj)
+ {
+ //Check to make sure we can convert
+ if (KeyType.CreateInstance().CanConvertFrom(typeof(TKeyIn)) && ValueType.CreateInstance().CanConvertFrom(typeof(TValueIn)))
+ {
+ return new MapType<TKey, TValue>(obj.ToDictionary(
+ k => (TKey)CassandraObject.GetCassandraObjectFromObject(k.Key, KeyType),
+ v => (TValue)CassandraObject.GetCassandraObjectFromObject(v.Value, ValueType)));
+ }
+
+ throw new ArgumentException(string.Format("can't convert IDictionary of type {0},{1} to MapType of type {2},{3}", typeof(TKeyIn), typeof(TValueIn), typeof(TKey), typeof(TValue)));
+ }
+
+ public static Dictionary<TKeyOut, TValueOut> To<TKeyOut, TValueOut>(MapType<TKey, TValue> obj)
+ {
+ //Check to make sure we can convert
+ if (KeyType.CreateInstance().CanConvertFrom(typeof(TKeyOut)) && ValueType.CreateInstance().CanConvertFrom(typeof(TValueOut)))
+ {
+ return obj.ToDictionary(k => k.Key.GetValue<TKeyOut>(), v => v.Value.GetValue<TValueOut>());
+ }
+
+ throw new ArgumentException(string.Format("can't convert MapType of type {0},{1} to Dictionary of type {2},{3}", typeof(TKey), typeof(TValue), typeof(TKeyOut), typeof(TValueOut)));
+ }
+
+ #endregion
+
+ #region IDictionary<CassandraType,CassandraType> Members
+
+ public void Add(KeyValuePair<TKey, TValue> item)
+ {
+ Add(item.Key, item.Value);
+ }
+
+ public void Clear()
+ {
+ _value.Clear();
+ }
+
+ public bool Contains(KeyValuePair<TKey, TValue> item)
+ {
+ return _value.Contains(item);
+ }
+
+
+
+ public bool Remove(KeyValuePair<TKey, TValue> item)
+ {
+ return Remove(item.Key);
+ }
+
+
+ public bool ContainsKey(TKey key)
+ {
+ return _value.ContainsKey(key);
+ }
+
+ public void Add(TKey key, TValue value)
+ {
+ _value.Add(key, value);
+ }
+
+ public bool Remove(TKey key)
+ {
+ return _value.Remove(key);
+ }
+
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ return _value.TryGetValue(key, out value);
+ }
+
+ public TValue this[TKey key]
+ {
+ get { return _value[key]; }
+ set { _value[key] = value; }
+ }
+
+ public ICollection<TKey> Keys { get { return _value.Keys; } }
+ public ICollection<TValue> Values { get { return _value.Values; } }
+
+ #endregion
+
+ #region ICollection<KeyValuePair<CassandraType,CassandraType>> Members
+
+ public int Count { get { return _value.Count; } }
+ public bool IsReadOnly { get { return ((ICollection<KeyValuePair<TKey, TValue>>)_value).IsReadOnly; } }
+
+ public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
+ {
+ ((ICollection<KeyValuePair<TKey, TValue>>)_value).CopyTo(array, arrayIndex);
+ }
+
+ #endregion
+
+ #region IEnumerable<KeyValuePair<CassandraType,CassandraType>> Members
+
+ public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
+ {
+ return _value.GetEnumerator();
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #endregion
+
+ }
+}
View
235 src/Types/MapTypeConverter.cs
@@ -0,0 +1,235 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace FluentCassandra.Types
+{
+ internal class MapTypeConverter<TKey, TValue> : CassandraObjectConverter<Dictionary<TKey, TValue>>
+ where TKey : CassandraObject
+ where TValue : CassandraObject
+ {
+ protected virtual string CollectionStringBegin { get { return "{"; } }
+
+ protected virtual string CollectionStringEnd { get { return "}"; } }
+
+ protected virtual string KeyValueSeparator{ get { return ":"; } }
+
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return sourceType == typeof(byte[]) ||
+ sourceType.GetInterfaces().Contains(typeof(IDictionary<TKey, TValue>)) ||
+ sourceType.GetInterfaces().Contains(typeof(IEnumerable<KeyValuePair<TKey, TValue>>)) ||
+ sourceType.GetInterfaces().Contains(typeof(IEnumerable<object>));
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return destinationType == typeof(byte[]) ||
+ destinationType == typeof(Dictionary<TKey, TValue>) ||
+ destinationType == typeof(Dictionary<CassandraObject, CassandraObject>) ||
+ destinationType == typeof(List<KeyValuePair<TKey, TValue>>) ||
+ destinationType == typeof(List<KeyValuePair<CassandraObject, CassandraObject>>) ||
+ destinationType == typeof(KeyValuePair<CassandraObject, CassandraObject>[]) ||
+ destinationType == typeof(List<object>) ||
+ destinationType == typeof(object[]) ||
+ destinationType == typeof(string);
+ }
+
+ public override Dictionary<TKey, TValue> ConvertFromInternal(object value)
+ {
+ if (value is byte[])
+ {
+ var components = new Dictionary<TKey, TValue>();
+
+ var keyTypeHint = typeof(TKey);
+ var valueTypeHint = typeof (TValue);
+
+ using (var bytes = new MemoryStream((byte[])value))
+ {
+ // number of key / value pairs
+ var numEntriesBytes = new byte[2];
+ if (bytes.Read(numEntriesBytes, 0, 2) <= 0)
+ return components;
+
+ var nEntries = BitConverter.ToUInt16(numEntriesBytes, 0);
+ for (var i = 0; i < nEntries; i++)
+ {
+ //get the length of the key
+ var keyLengthBytes = new byte[2];
+ bytes.Read(keyLengthBytes, 0, 2);
+ var keyLength = BitConverter.ToUInt16(keyLengthBytes, 0);
+
+ //read the content of the key into a buffer
+ var keyBuffer = new byte[keyLength];
+ bytes.Read(keyBuffer, 0, keyLength);
+ var entryKey = CassandraObject.GetCassandraObjectFromDatabaseByteArray(keyBuffer, keyTypeHint);
+
+ //get the length of the value
+ var valueLengthBytes = new byte[2];
+ bytes.Read(valueLengthBytes, 0, 2);
+ var valueLength = BitConverter.ToUInt16(valueLengthBytes, 0);
+
+ //read the content of the key into a buffer
+ var valueBuffer = new byte[valueLength];
+ bytes.Read(valueBuffer, 0, valueLength);
+ var entryValue = CassandraObject.GetCassandraObjectFromDatabaseByteArray(valueBuffer, valueTypeHint);
+
+ components.Add((TKey)entryKey, (TValue)entryValue);
+ }
+ }
+
+ return components;
+ }
+
+ if (value.GetType().GetInterfaces().Contains(typeof(IEnumerable<object>)))
+ return new Dictionary<TKey, TValue>(((IEnumerable<object>)value).Cast<KeyValuePair<TKey, TValue>>().ToDictionary(k => k.Key, v => v.Value));
+
+ if (value.GetType().GetInterfaces().Contains(typeof(IEnumerable<KeyValuePair<TKey, TValue>>)))
+ return new Dictionary<TKey, TValue>(((IEnumerable<KeyValuePair<TKey, TValue>>)value).ToDictionary(k => k.Key, v => v.Value));
+
+ return null;
+ }
+
+ public override object ConvertToInternal(Dictionary<TKey, TValue> value, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ return CollectionStringBegin + String.Join(",", value.Select(x => string.Format("{0}{1}{2}", x.Key, KeyValueSeparator, x.Value))) + CollectionStringEnd; //should format the map into a JSON-esque object
+
+ if (destinationType == typeof(byte[]))
+ {
+ var components = value;
+
+ using (var bytes = new MemoryStream())
+ {
+ //write the number of elements
+ var elements = (ushort)components.Count;
+ bytes.Write(BitConverter.GetBytes(elements), 0, 2);
+
+ foreach (var c in components)
+ {
+
+ var keyBytes = c.Key.ToBigEndian();
+
+ //key length
+ var keyLength = (ushort)keyBytes.Length;
+ bytes.Write(BitConverter.GetBytes(keyLength), 0, 2);
+
+ //key value
+ bytes.Write(keyBytes, 0, keyLength);
+
+ var valueBytes = c.Value.ToBigEndian();
+
+ // value length
+ var valueLength = (ushort)valueBytes.Length;
+ bytes.Write(BitConverter.GetBytes(valueLength), 0, 2);
+
+ // value
+ bytes.Write(valueBytes, 0, valueLength);
+ }
+
+ return bytes.ToArray();
+ }
+
+ }
+
+ if (destinationType == typeof(KeyValuePair<CassandraObject, CassandraObject>[]))
+ return value.ToArray();
+
+ if (destinationType == typeof(object[]))
+ return value.Cast<object>().ToArray();
+
+ if (destinationType == typeof(List<KeyValuePair<CassandraObject, CassandraObject>>) || destinationType == typeof(List<KeyValuePair<TKey, TValue>>))
+ return value.ToList();
+
+ if (destinationType == typeof (Dictionary<TKey, TValue>) ||
+ destinationType == typeof (Dictionary<CassandraObject, CassandraObject>))
+ return value;
+
+ if (destinationType == typeof(List<object>))
+ return value.Cast<object>().ToList();
+
+ return null;
+ }
+
+ public override byte[] ToBigEndian(Dictionary<TKey, TValue> value)
+ {
+ var components = value;
+
+ using (var bytes = new MemoryStream())
+ {
+ //write the number of elements
+ var elements = (ushort)components.Count;
+ bytes.Write(ConvertEndian(BitConverter.GetBytes(elements)), 0, 2);
+
+ foreach (var c in components)
+ {
+
+ var keyBytes = c.Key.ToBigEndian();
+
+ //key length
+ var keyLength = (ushort)keyBytes.Length;
+ bytes.Write(ConvertEndian(BitConverter.GetBytes(keyLength)), 0, 2);
+
+ //key value
+ bytes.Write(keyBytes, 0, keyLength);
+
+ var valueBytes = c.Value.ToBigEndian();
+
+ // value length
+ var valueLength = (ushort)valueBytes.Length;
+ bytes.Write(ConvertEndian(BitConverter.GetBytes(valueLength)), 0, 2);
+
+ // value
+ bytes.Write(valueBytes, 0, valueLength);
+ }
+
+ return bytes.ToArray();
+ }
+ }
+
+ public override Dictionary<TKey, TValue> FromBigEndian(byte[] value)
+ {
+ var components = new Dictionary<TKey, TValue>();
+
+ var keyTypeHint = typeof(TKey);
+ var valueTypeHint = typeof(TValue);
+
+ using (var bytes = new MemoryStream((byte[])value))
+ {
+ // number of key / value pairs
+ var numEntriesBytes = new byte[2];
+ if (bytes.Read(numEntriesBytes, 0, 2) <= 0)
+ return components;
+
+ var nEntries = BitConverter.ToUInt16(ConvertEndian(numEntriesBytes), 0);
+ for (var i = 0; i < nEntries; i++)
+ {
+ //get the length of the key
+ var keyLengthBytes = new byte[2];
+ bytes.Read(keyLengthBytes, 0, 2);
+ var keyLength = BitConverter.ToUInt16(ConvertEndian(keyLengthBytes), 0);
+
+ //read the content of the key into a buffer
+ var keyBuffer = new byte[keyLength];
+ bytes.Read(keyBuffer, 0, keyLength);
+ var entryKey = CassandraObject.GetCassandraObjectFromDatabaseByteArray(keyBuffer, keyTypeHint);
+
+ //get the length of the value
+ var valueLengthBytes = new byte[2];
+ bytes.Read(valueLengthBytes, 0, 2);
+ var valueLength = BitConverter.ToUInt16(ConvertEndian(valueLengthBytes), 0);
+
+ //read the content of the key into a buffer
+ var valueBuffer = new byte[valueLength];
+ bytes.Read(valueBuffer, 0, valueLength);
+ var entryValue = CassandraObject.GetCassandraObjectFromDatabaseByteArray(valueBuffer, valueTypeHint);
+
+ components.Add((TKey)entryKey, (TValue)entryValue);
+ }
+ }
+
+ return components;
+ }
+ }
+}
View
12 src/Types/NullType.cs
@@ -8,7 +8,17 @@ internal class NullType : CassandraObject
private NullType() { }
- public override object GetValue()
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object GetValue()
{
return null;
}
View
12 src/Types/ReversedType.cs
@@ -14,7 +14,17 @@ protected override object GetValueInternal(Type type)
throw new NotImplementedException();
}
- public override object GetValue()
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object GetValue()
{
throw new NotImplementedException();
}
View
306 src/Types/SetType.cs
@@ -0,0 +1,306 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+/*
+ Note: there aren't very fundamental differences between a Set and a List type in CQL3.
+ The biggest differences are:
+
+ 1. Lists can contain duplicates, Sets do not.
+ 2. Lists are ordered and indexed, Sets are unordered.
+
+ If we wanted to be sticklers, the SetType's internal storage mechanism should probably be a HashSet since that
+ more accurately reflects the behavior of the set in Cassandra itself. However, it's really the responsibility of the
+ application developer to model his / her data correctly, not on the driver developer to baby-proof for them.
+
+ -Aaron Stannard (@Aaronontheweb)
+ */
+
+namespace FluentCassandra.Types
+{
+ public class SetType<T> : CassandraObject, IList<T> where T : CassandraObject
+ {
+ private static readonly SetTypeConverter<T> Converter = new SetTypeConverter<T>();
+
+ #region Create
+
+ public SetType() : this(new List<T>())
+ {
+ }
+
+ public SetType(IEnumerable<T> objects)
+ {
+ _value = new List<T>(objects);
+ }
+
+ #endregion
+
+ private List<T> _value;
+
+ public static CassandraType ComponentType
+ {
+ get { return (CassandraType) typeof (T); }
+ }
+
+ #region Implementation
+
+ public override object GetValue()
+ {
+ return _value;
+ }
+
+ public override void SetValue(object obj)
+ {
+ if (obj != null)
+ _value = Converter.ConvertFrom(obj);
+ }
+
+ protected override object GetValueInternal(Type type)
+ {
+ //If we're converting to a List<> type
+ if (type.IsList())
+ {
+ var listType = type.GetPrimaryGenericType();
+
+ //Check to see if this list's data type can be converted to the requested type
+ if (ComponentType.CreateInstance().CanConvertTo(listType))
+ {
+ return TypeHelper.PopulateGenericList(_value.Select(o => (CassandraObject) o).ToList(), listType);
+ }
+ }
+
+ return Converter.ConvertTo(_value, type);
+ }
+
+ protected override TypeCode TypeCode
+ {
+ get { return TypeCode.Object; }
+ }
+
+ public override byte[] ToBigEndian()
+ {
+ return Converter.ToBigEndian(_value);
+ }
+
+ public override void SetValueFromBigEndian(byte[] value)
+ {
+ _value = Converter.FromBigEndian(value);
+ }
+
+ #endregion
+
+ #region Equality
+
+ public override bool Equals(object obj)
+ {
+ List<T> objArray;
+
+ if (obj is List<T>)
+ objArray = ((SetType<T>) obj)._value;
+ else
+ objArray = Converter.ConvertFrom(obj);
+
+ if (objArray == null)
+ return false;
+
+ if (objArray.Count != _value.Count)
+ return false;
+
+ for (int i = 0; i < objArray.Count; i++)
+ {
+ if (!_value[i].Equals(objArray[i]))
+ return false;
+ }
+
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hash = 17;
+ foreach (var keyPart in _value)
+ {
+ hash = hash*23 + keyPart.GetHashCode();
+ }
+ return hash;
+ }
+ }
+
+ #endregion
+
+ #region Conversion
+
+ public static implicit operator List<T>(SetType<T> type)
+ {
+ return type._value;
+ }
+
+ public static implicit operator SetType<T>(List<T> obj)
+ {
+ return new SetType<T>(obj);
+ }
+
+ public static SetType<T> From<TIn>(List<TIn> obj)
+ {
+ //Check to make sure we can convert
+ if (ComponentType.CreateInstance().CanConvertFrom(typeof (TIn)))
+ {
+ return new SetType<T>(obj.Select(o => (T) CassandraObject.GetCassandraObjectFromObject(o, typeof (T))));
+ }
+
+ throw new ArgumentException(string.Format("can't convert list of type {0} to SetType of type {1}",
+ typeof (TIn), typeof (T)));
+ }
+
+ public static List<TOut> To<TOut>(SetType<T> obj)
+ {
+ //Check to make sure we can convert
+ if (ComponentType.CreateInstance().CanConvertTo(typeof (TOut)))
+ {
+ return new List<TOut>(obj.Select(o => o.GetValue<TOut>()));
+ }
+
+ throw new ArgumentException(string.Format("can't convert SetType of type {1} to List of type {0} to ",
+ typeof (T), typeof (TOut)));
+ }
+
+ public static implicit operator byte[](SetType<T> o)
+ {
+ return ConvertTo<byte[]>(o);
+ }
+
+ public static implicit operator SetType<T>(byte[] o)
+ {
+ return ConvertFrom(o);
+ }
+
+ public static implicit operator SetType<T>(object[] s)
+ {
+ return new SetType<T>
+ {
+ _value = new List<T>(s.Select(o => (T) CassandraObject.GetCassandraObjectFromObject(o, typeof (T))))
+ };
+ }
+
+ public static implicit operator SetType<T>(List<object> s)
+ {
+ return new SetType<T>
+ {
+ _value = new List<T>(s.Select(o => (T) CassandraObject.GetCassandraObjectFromObject(o, typeof (T))))
+ };
+ }
+
+ private static TOut ConvertTo<TOut>(SetType<T> type)
+ {
+ if (type == null)
+ return default(TOut);
+
+ return type.GetValue<TOut>();
+ }
+
+ private static SetType<T> ConvertFrom(object o)
+ {
+ var type = new SetType<T>();
+ type.SetValue(o);
+ return type;
+ }
+
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
+ #endregion
+
+ #region IList<CassandraObject> Members
+
+ public int IndexOf(T item)
+ {
+ return _value.IndexOf(item);
+ }
+
+ public void Insert(int index, T item)
+ {
+ _value.Insert(index, item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ _value.RemoveAt(index);
+ }
+
+ public T this[int index]
+ {
+ get { return _value[index]; }
+ set { _value[index] = value; }
+ }
+
+ #endregion
+
+ #region ICollection<CassandraType> Members
+
+ public void Add(T item)
+ {
+ _value.Add(item);
+ }
+
+ public void Clear()
+ {
+ _value.Clear();
+ }
+
+ public bool Contains(T item)
+ {
+ return _value.Contains(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ _value.CopyTo(array, arrayIndex);
+ }
+
+ public bool Remove(T item)
+ {
+ return _value.Remove(item);
+ }
+
+ public int Count
+ {
+ get { return _value.Count; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return ((ICollection<CassandraObject>) _value).IsReadOnly; }
+ }
+
+ #endregion
+
+ #region IEnumerable<CassandraType> Members
+
+ public IEnumerator<T> GetEnumerator()
+ {
+ return _value.GetEnumerator();
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #endregion
+ }
+}
View
15 src/Types/SetTypeConverter.cs
@@ -0,0 +1,15 @@
+namespace FluentCassandra.Types
+{
+ internal class SetTypeConverter<T> : ListTypeConverter<T> where T : CassandraObject
+ {
+ protected override string CollectionStringBegin
+ {
+ get { return "{"; }
+ }
+
+ protected override string CollectionStringEnd
+ {
+ get { return "}"; }
+ }
+ }
+}
View
10 src/Types/TimeUUIDType.cs
@@ -100,6 +100,16 @@ private static TimeUUIDType ConvertFrom(object o)
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
}
}
View
107 src/Types/TypeHelper.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace FluentCassandra.Types
+{
+ /// <summary>
+ /// Internal class for helping with type issues
+ /// </summary>
+ internal static class TypeHelper
+ {
+ public static bool IsList(this Type t)
+ {
+ var enumerable = (from i in t.GetInterfaces()
+ where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>)
+ select i);
+ return enumerable.Any();
+ }
+
+ public static bool IsDictionary(this Type t)
+ {
+ var dict = (from i in t.GetInterfaces()
+ where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>)
+ select i).Any();
+ return dict;
+ }
+
+ public static Type GetIListImplementation(Type t)
+ {
+ return (from i in t.GetInterfaces()
+ where i.IsGenericType && (i.GetGenericTypeDefinition() == typeof(IList<>) || i.GetGenericTypeDefinition() == typeof(IEnumerable<>))
+ select i).FirstOrDefault();
+ }
+
+ public static Type GetIDictionaryImplementation(Type t)
+ {
+ return (from i in t.GetInterfaces()
+ where i.IsGenericType && (i.GetGenericTypeDefinition() == typeof(IDictionary<,>))
+ select i).FirstOrDefault();
+ }
+
+ public static Type GetPrimaryGenericType(this Type t)
+ {
+ var listInterface = GetIListImplementation(t);
+
+ if (listInterface == default(Type))
+ throw new ArgumentException(string.Format("type {0} is does not implement an IList implementation", t));
+
+ Type[] genericArguments = listInterface.GetGenericArguments();
+
+ if(genericArguments.Length == 0)
+ throw new ArgumentException(string.Format("type {0} is not generic, thus it has no generic arguments.", t));
+
+ return genericArguments[0];
+ }
+
+ public static Type[] GetAllGenericTypes(this Type t)
+ {
+ var dictionaryInterface = GetIDictionaryImplementation(t);
+
+ if (dictionaryInterface == default(Type))
+ throw new ArgumentException(string.Format("type {0} is does not implement an IDictionary implementation", t));
+
+ Type[] genericArguments = dictionaryInterface.GetGenericArguments();
+
+ if (genericArguments.Length == 0)
+ throw new ArgumentException(string.Format("type {0} is not generic, thus it has no generic arguments.", t));
+
+ return genericArguments;
+ }
+
+ public static object PopulateGenericList(IList<CassandraObject> objects, Type targetType)
+ {
+ var listType = typeof (List<>).MakeGenericType(targetType);
+
+ var addMethod = listType.GetMethod("Add", new []{targetType});
+ var list = Activator.CreateInstance(listType);
+
+ //convert each CassandraObject into its target type and add it to the new List
+ foreach (var cassandraObj in objects)
+ {
+ addMethod.Invoke(list, new []{cassandraObj.GetValue(targetType)});
+ }
+
+ return list;
+ }
+
+ public static object PopulateGenericDictionary(IDictionary<CassandraObject, CassandraObject> objects,
+ Type targetKeyType, Type targetValueType)
+ {
+ var dictType = typeof (Dictionary<,>).MakeGenericType(targetKeyType, targetValueType);
+ var addMethod = dictType.GetMethod("Add", new[] { targetKeyType, targetValueType });
+
+ var dict = Activator.CreateInstance(dictType);
+
+ //convert each CassandraObject into its target type and add it to the new dictionary
+ foreach (var cassandraPair in objects)
+ {
+ addMethod.Invoke(dict, new[] {cassandraPair.Key.GetValue(targetKeyType), cassandraPair.Value.GetValue(targetValueType)});
+ }
+
+ return dict;
+ }
+ }
+}
View
13 src/Types/UTF8Type.cs
@@ -1,4 +1,5 @@
using System;
+using System.Security.Cryptography;
namespace FluentCassandra.Types
{
@@ -35,7 +36,7 @@ protected override TypeCode TypeCode
#endregion
- public override object GetValue() { return _value; }
+ public override object GetValue() { return _value; }
private string _value;
@@ -95,6 +96,16 @@ private static UTF8Type ConvertFrom(object o)
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
}
}
View
12 src/Types/UUIDType.cs
@@ -35,7 +35,7 @@ protected override TypeCode TypeCode
#endregion
- public override object GetValue() { return _value; }
+ public override object GetValue() { return _value; }
private Guid _value;
@@ -91,6 +91,16 @@ private static UUIDType ConvertFrom(object o)
return type;
}
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ return Converter.CanConvertFrom(sourceType);
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ return Converter.CanConvertTo(destinationType);
+ }
+
#endregion
}
}
View
12 src/Types/VoidType.cs
@@ -34,7 +34,17 @@ public override byte[] ToBigEndian()
throw new NotSupportedException();
}
- public override object GetValue()
+ public override bool CanConvertFrom(Type sourceType)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override bool CanConvertTo(Type destinationType)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override object GetValue()
{
throw new NotSupportedException();
}
View
36 test/FluentCassandra.Integration.Tests/CassandraDatabaseSetup.cs
@@ -112,14 +112,32 @@ public void ResetDatabase()
ColumnNameType = CassandraType.DynamicCompositeType(new Dictionary<char, CassandraType> { { 'a', CassandraType.AsciiType }, { 'd', CassandraType.DoubleType } })
});
- db.ExecuteNonQuery(@"
-CREATE COLUMNFAMILY Users (
+ db.ExecuteNonQuery(@"
+CREATE COLUMNFAMILY ""Users"" (
Id int PRIMARY KEY,
Name ascii,
Email ascii,
Age int
-);", CqlVersion.Cql);
- db.ExecuteNonQuery(@"CREATE INDEX User_Age ON Users (Age);", CqlVersion.Cql);
+);");
+ db.ExecuteNonQuery(@"CREATE INDEX User_Age ON ""Users"" (Age);");
+
+ db.ExecuteNonQuery(@"
+CREATE COLUMNFAMILY Cql3List (
+ Id int PRIMARY KEY,
+ TagList list<text> --list of strings
+);");
+
+ db.ExecuteNonQuery(@"
+CREATE COLUMNFAMILY Cql3Set (
+ Id int PRIMARY KEY,
+ TagSet set<uuid> --set of Guids
+);");
+
+ db.ExecuteNonQuery(@"
+CREATE COLUMNFAMILY Cql3Map (
+ Id int PRIMARY KEY,
+ TagMap map<bigint,uuid> --map of long integers and Guids
+);");
db.Keyspace.ClearCachedKeyspaceSchema();
var family = db.GetColumnFamily<AsciiType>("Standard");
@@ -143,15 +161,9 @@ public void ResetUsersFamily(CassandraColumnFamily userFamily = null)
foreach (var user in Users)
{
- dynamic record = userFamily.CreateRecord(user.Id);
- record.Name = user.Name;
- record.Email = user.Email;
- record.Age = user.Age;
-
- db.Attach(record);
+ var insertQuery = @"INSERT INTO ""Users"" (Id, Name, Email, Age) VALUES ("+ user.Id +", '" + user.Name + "', '"+ user.Email +"', "+ user.Age +")";
+ db.ExecuteNonQuery(insertQuery);
}
-
- db.SaveChanges();
}
public void ResetFamily(CassandraColumnFamily family = null)
View
4 test/FluentCassandra.Integration.Tests/FluentCassandra.Integration.Tests.csproj
@@ -45,6 +45,7 @@
<Compile Include="Connections\ConnectionBuilderTests.cs" />
<Compile Include="Connections\ConnectionProviderTests.cs" />
<Compile Include="Connections\NormalConnectionProviderTests.cs" />
+ <Compile Include="Operations\Cql3CollectionsTest.cs" />
<Compile Include="Linq\LinqToCqlCassandraTests.cs" />
<Compile Include="Linq\LinqToCqlObjectsCassandraTests.cs" />
<Compile Include="Linq\LinqToCqlObjectsTests.cs" />
@@ -100,6 +101,9 @@
<Name>FluentCassandra.Tests</Name>
</ProjectReference>
</ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
View
108 test/FluentCassandra.Integration.Tests/Operations/Cql3CollectionsTest.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using FluentCassandra.Connections;
+using Xunit;
+
+namespace FluentCassandra.Integration.Tests.Operations
+{
+ /// <summary>
+ /// fluent-cassandra support for CQL3 collection types, such as map / list / set
+ /// </summary>
+ public class Cql3CollectionsTest : IUseFixture<CassandraDatabaseSetupFixture>, IDisposable
+ {
+ private CassandraContext _db;
+
+ public void SetFixture(CassandraDatabaseSetupFixture data)
+ {
+ var setup = data.DatabaseSetup(cqlVersion: CqlVersion.Cql3);
+ _db = setup.DB;
+ }
+
+ public void Dispose()
+ {
+ _db.Dispose();
+ }
+
+ [Fact]
+ public void TestReadingCql3List()
+ {
+ //arrange
+ var insertQuery = @"INSERT INTO Cql3List (Id, TagList) VALUES(1, ['item1','item2']);";
+
+ //act
+ _db.ExecuteNonQuery(insertQuery);
+ var results = _db.ExecuteQuery("SELECT * FROM Cql3List").ToList();
+
+ //assert
+ Assert.Equal(1, results.Count());
+ Assert.Equal(2, results.First().Columns.Count);
+
+ var row = (FluentCqlRow)results.First();
+ var id = row.GetColumn("id").ColumnValue.GetValue<int>();
+ var taglist = row.GetColumn("taglist").ColumnValue.GetValue<List<string>>();
+
+ Assert.Equal(1, id);
+ Assert.Equal(2, taglist.Count);
+ Assert.Equal("item1", taglist[0]);
+ Assert.Equal("item2", taglist[1]);
+ }
+
+ [Fact]
+ public void TestReadingCql3Set()
+ {
+ //arrange
+ var guid1 = new Guid("88F1F2FE-B13B-4241-B17E-B8FAB8AC588B");
+ var guid2 = new Guid("1AFBBD02-C4D5-46BD-B5F5-D0DCA91BC049");
+ var guids = new[] { guid1, guid2 };
+ var insertQuery = @"INSERT INTO Cql3Set (Id, TagSet) VALUES(1, {" + guid1 + "," + guid2 + "});";
+
+ //act
+ _db.ExecuteNonQuery(insertQuery);
+ var results = _db.ExecuteQuery("SELECT * FROM Cql3Set").ToList();
+
+ //assert
+ Assert.Equal(1, results.Count());
+ Assert.Equal(2, results.First().Columns.Count);
+
+ var row = (FluentCqlRow)results.First();
+ var id = row.GetColumn("id").ColumnValue.GetValue<int>();
+ var tagset = row.GetColumn("tagset").ColumnValue.GetValue<List<Guid>>();
+
+ Assert.Equal(1, id);
+ Assert.Equal(2, tagset.Count);
+ Assert.True(guids.Contains(tagset[0]));
+ Assert.True(guids.Contains(tagset[1]));
+ }
+
+ [Fact]
+ public void TestReadingCql3Map()
+ {
+ //arrange
+ var mapItem1 = new KeyValuePair<long, Guid>(11310101L, new Guid("88F1F2FE-B13B-4241-B17E-B8FAB8AC588B"));
+ var mapItem2 = new KeyValuePair<long, Guid>(-452117101L, new Guid("1AFBBD02-C4D5-46BD-B5F5-D0DCA91BC049"));
+ var items = new[] {mapItem1, mapItem2};
+
+ var insertQuery = @"INSERT INTO Cql3Map (Id, TagMap) VALUES(1, {" + mapItem1.Key + ":" + mapItem1.Value + ", " + mapItem2.Key + ":" + mapItem2.Value + "});";
+
+ //act
+ _db.ExecuteNonQuery(insertQuery);
+ var results = _db.ExecuteQuery("SELECT * FROM Cql3Map").ToList();
+
+ //assert
+ Assert.Equal(1, results.Count());
+ Assert.Equal(2, results.First().Columns.Count);
+
+ var row = (FluentCqlRow)results.First();
+ var id = row.GetColumn("id").ColumnValue.GetValue<int>();
+ var tagmap = row.GetColumn("tagmap").ColumnValue.GetValue<Dictionary<long,Guid>>();
+
+ Assert.Equal(1, id);
+ Assert.Equal(2, tagmap.Count);
+ Assert.True(tagmap.ContainsKey(mapItem1.Key));
+ Assert.True(tagmap.ContainsKey(mapItem2.Key));
+ Assert.Equal(mapItem1.Value, tagmap[mapItem1.Key]);
+ Assert.Equal(mapItem2.Value, tagmap[mapItem2.Key]);
+ }
+ }
+}
View
6 test/FluentCassandra.Tests/FluentCassandra.Tests.csproj
@@ -67,8 +67,11 @@
<Compile Include="Types\AsciiTypeTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Types\BytesTypeTest.cs" />
+ <Compile Include="Types\ListTypeTest.cs" />
+ <Compile Include="Types\MapTypeTest.cs" />
<Compile Include="Types\NullTypeTest.cs" />
<Compile Include="Types\LexicalUUIDTypeTest.cs" />
+ <Compile Include="Types\SetTypeTest.cs" />
<Compile Include="Types\TimeUUIDTypeTest.cs" />
<Compile Include="Types\LongTypeTest.cs" />
<Compile Include="Types\UTF8TypeTest.cs" />
@@ -82,6 +85,9 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
View
120 test/FluentCassandra.Tests/Types/ListTypeTest.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Xunit;
+
+namespace FluentCassandra.Types
+{
+ public class ListTypeTest
+ {
+ /*
+ * N.B. _listType and _javaByteOrder effectively represent the same content (captured by debugging a live CQL3 read from Cassandra 1.2)
+ */
+
+ private readonly List<UTF8Type> _listType = new List<UTF8Type>()
+ {
+ (UTF8Type) "item1",
+ (UTF8Type) "item2"
+ };
+ private readonly byte[] _javaByteOrder = new byte[] { 0, 2, 0, 5, 105, 116, 101, 109, 49, 0, 5, 105, 116, 101, 109, 50 };
+
+ public byte[] GetBytes(IList<CassandraObject> value)
+ {
+ var components = value;
+
+ using (var bytes = new MemoryStream())
+ {
+ //write the number of lengths
+ var elements = (ushort) components.Count;
+ bytes.Write(BitConverter.GetBytes(elements), 0, 2);
+
+ foreach (var c in components)
+ {
+ var b = c.ToBigEndian();
+ var length = (ushort) b.Length;
+
+ // value length
+ bytes.Write(BitConverter.GetBytes(length), 0, 2);
+
+ // value
+ bytes.Write(b, 0, length);
+ }
+
+ return bytes.ToArray();
+ }
+ }
+
+ [Fact]
+ public void CassandraType_Cast()
+ {
+ //arrange
+ var expected = _listType;
+
+ //act
+ ListType<UTF8Type> actualType = expected;
+ CassandraObject actual = actualType;
+
+ //assert
+ Assert.True(expected.SequenceEqual((CassandraObject[])actual));
+ }
+
+ [Fact]
+ public void Explicit_List_Cast()
+ {
+ //arrange
+ List<int> expected = new List<int>() {1, 2, 3};
+
+ //act
+ ListType<IntegerType> actualType = ListType<IntegerType>.From(expected);
+ CassandraObject actual = actualType;
+ var actualValues = actual.GetValue<List<object>>();
+
+ //assert
+ Assert.True(expected.SequenceEqual(actualValues.Select(Convert.ToInt32)));
+ }
+
+ [Fact]
+ public void Implicit_ByteArray_Cast()
+ {
+ //arrange
+ var expected = _listType;
+ byte[] expectedBytes = GetBytes(_listType.Cast<CassandraObject>().ToList());
+
+ //act
+ ListType<UTF8Type> actual = expectedBytes;
+
+ //assert
+ Assert.True(expected.SequenceEqual(actual));
+ }
+
+ [Fact]
+ public