diff --git a/src/System/Numerics/BigDecimal.cs b/src/System/Numerics/BigDecimal.cs index a517648..3bd1be9 100644 --- a/src/System/Numerics/BigDecimal.cs +++ b/src/System/Numerics/BigDecimal.cs @@ -71,6 +71,9 @@ public BigDecimal(byte[] value) public bool IsZero { get { return _unscaledValue.IsZero; } } public int Sign { get { return _unscaledValue.Sign; } } + public BigInteger UnscaledValue { get { return _unscaledValue; } } + public int Scale { get { return _scale; } } + public override string ToString() { var number = _unscaledValue.ToString("G"); diff --git a/src/Types/CassandraConversionHelper.cs b/src/Types/CassandraConversionHelper.cs index 5a97d3a..86071a3 100644 --- a/src/Types/CassandraConversionHelper.cs +++ b/src/Types/CassandraConversionHelper.cs @@ -54,13 +54,5 @@ internal static Guid ToGuidFromBigEndianBytes(this byte[] value) ReverseHighFieldTimestamp(buffer); return new Guid(buffer); } - - internal static BigDecimal ToBigDecimalFromBigEndianBytes(this byte[] value) - { - var buffer = (byte[])value.Clone(); - Array.Reverse(buffer); - - return new BigDecimal(buffer); - } } } diff --git a/src/Types/CassandraObject.cs b/src/Types/CassandraObject.cs index 367192a..4959ef1 100644 --- a/src/Types/CassandraObject.cs +++ b/src/Types/CassandraObject.cs @@ -120,6 +120,7 @@ private static CassandraObject ConvertFrom(object o) public static implicit operator CassandraObject(DateTime o) { return ConvertFrom(o); } public static implicit operator CassandraObject(DateTimeOffset o) { return ConvertFrom(o); } public static implicit operator CassandraObject(BigInteger o) { return ConvertFrom(o); } + public static implicit operator CassandraObject(BigDecimal o) { return ConvertFrom(o); } public static implicit operator byte[](CassandraObject o) { return ConvertTo(o); } public static implicit operator char[](CassandraObject o) { return ConvertTo(o); } @@ -142,6 +143,7 @@ private static CassandraObject ConvertFrom(object o) public static implicit operator DateTime(CassandraObject o) { return ConvertTo(o); } public static implicit operator DateTimeOffset(CassandraObject o) { return ConvertTo(o); } public static implicit operator BigInteger(CassandraObject o) { return ConvertTo(o); } + public static implicit operator BigDecimal(CassandraObject o) { return ConvertTo(o); } public static implicit operator byte?(CassandraObject o) { return ConvertTo(o); } public static implicit operator sbyte?(CassandraObject o) { return ConvertTo(o); } @@ -161,6 +163,7 @@ private static CassandraObject ConvertFrom(object o) public static implicit operator DateTime?(CassandraObject o) { return ConvertTo(o); } public static implicit operator DateTimeOffset?(CassandraObject o) { return ConvertTo(o); } public static implicit operator BigInteger?(CassandraObject o) { return ConvertTo(o); } + public static implicit operator BigDecimal?(CassandraObject o) { return ConvertTo(o); } public static explicit operator object[](CassandraObject o) { return ConvertTo(o); } public static explicit operator List(CassandraObject o) { return ConvertTo>(o); } diff --git a/src/Types/DecimalTypeConverter.cs b/src/Types/DecimalTypeConverter.cs index bd969d7..1d36830 100644 --- a/src/Types/DecimalTypeConverter.cs +++ b/src/Types/DecimalTypeConverter.cs @@ -94,5 +94,41 @@ public override object ConvertToInternal(BigDecimal value, Type destinationType) return null; } + + public override byte[] ToBigEndian(BigDecimal value) + { + var scale = value.Scale; + var number = value.UnscaledValue; + + var int32Converter = new Int32TypeConverter(); + var bigIntegerConverter = new IntegerTypeConverter(); + + var scaleBytes = int32Converter.ToBigEndian(scale); + var numberBytes = bigIntegerConverter.ToBigEndian(number); + + var bytes = new byte[scaleBytes.Length + numberBytes.Length]; + + Array.Copy(scaleBytes, 0, bytes, 0, scaleBytes.Length); + Array.Copy(numberBytes, 0, bytes, scaleBytes.Length, numberBytes.Length); + + return bytes; + } + + public override BigDecimal FromBigEndian(byte[] value) + { + var scaleBytes = new byte[4]; + var numberBytes = new byte[value.Length - 4]; + + Array.Copy(value, 0, scaleBytes, 0, scaleBytes.Length); + Array.Copy(value, scaleBytes.Length, numberBytes, 0, numberBytes.Length); + + var int32Converter = new Int32TypeConverter(); + var bigIntegerConverter = new IntegerTypeConverter(); + + var scale = int32Converter.FromBigEndian(scaleBytes); + var number = bigIntegerConverter.FromBigEndian(numberBytes); + + return new BigDecimal(number, scale); + } } } diff --git a/test/FluentCassandra.Tests/CassandraDatabaseSetup.cs b/test/FluentCassandra.Tests/CassandraDatabaseSetup.cs index 225c64f..9ecc175 100644 --- a/test/FluentCassandra.Tests/CassandraDatabaseSetup.cs +++ b/test/FluentCassandra.Tests/CassandraDatabaseSetup.cs @@ -82,6 +82,10 @@ public void ResetDatabase() keyspace.TryCreateColumnFamily("StandardTimeUUIDType"); keyspace.TryCreateColumnFamily("StandardUTF8Type"); keyspace.TryCreateColumnFamily("StandardUUIDType"); + keyspace.TryCreateColumnFamily(new CassandraColumnFamilySchema { + FamilyName = "StandardDecimalType", + ColumnNameType = CassandraType.DecimalType + }); keyspace.TryCreateColumnFamily(new CassandraColumnFamilySchema { FamilyName = "StandardCompositeType", ColumnNameType = CassandraType.CompositeType(new[] { CassandraType.AsciiType, CassandraType.DoubleType }) diff --git a/test/FluentCassandra.Tests/FluentCassandra.Tests.csproj b/test/FluentCassandra.Tests/FluentCassandra.Tests.csproj index 4756539..75bf006 100644 --- a/test/FluentCassandra.Tests/FluentCassandra.Tests.csproj +++ b/test/FluentCassandra.Tests/FluentCassandra.Tests.csproj @@ -76,6 +76,7 @@ + @@ -84,6 +85,7 @@ + diff --git a/test/FluentCassandra.Tests/Types/DecimalTypeTest.cs b/test/FluentCassandra.Tests/Types/DecimalTypeTest.cs new file mode 100644 index 0000000..555b2a8 --- /dev/null +++ b/test/FluentCassandra.Tests/Types/DecimalTypeTest.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using Xunit; + +namespace FluentCassandra.Types +{ + public class DecimalTypeTest + { + private readonly BigDecimal bigDecimal = 100002334.4563D; + private readonly byte[] dotNetByteOrder = new byte[] { 179, 69, 9, 214, 232, 0, 4, 0, 0, 0 }; + private readonly byte[] javaByteOrder = new byte[] { 0, 0, 0, 4, 0, 232, 214, 9, 69, 179 }; + + [Fact] + public void CassandraType_Cast() + { + // arrange + BigDecimal expected = bigDecimal; + DecimalType actualType = expected; + + // act + CassandraObject actual = actualType; + + // assert + Assert.Equal(expected, (BigDecimal)actual); + } + + [Fact] + public void Implicit_ByteArray_Cast() + { + // arrange + byte[] expected = dotNetByteOrder; + + // act + DecimalType actualType = expected; + byte[] actual = actualType; + + // assert + Assert.True(expected.SequenceEqual(actual)); + } + + [Fact] + public void Implicit_BigDecimal_Cast() + { + // arrange + BigDecimal expected = bigDecimal; + + // act + DecimalType actual = expected; + + // assert + Assert.Equal(expected, (BigDecimal)actual); + } + + [Fact] + public void Operator_EqualTo() + { + // arrange + var value = bigDecimal; + DecimalType type = value; + + // act + bool actual = type.Equals(value); + + // assert + Assert.True(actual); + } + + [Fact] + public void Operator_NotEqualTo() + { + // arrange + var value = bigDecimal; + DecimalType type = value; + + // act + bool actual = !type.Equals(value); + + // assert + Assert.False(actual); + } + + [Fact] + public void BigDecimal_To_JavaBytes() + { + // arrange + + // act + DecimalType actual = bigDecimal; + + // assert + Assert.True(actual.ToBigEndian().SequenceEqual(javaByteOrder)); + } + + [Fact] + public void JavaBytes_To_BigDecimal() + { + // arrange + + // act + DecimalType actual = new DecimalType(); + actual.SetValueFromBigEndian(javaByteOrder); + + // assert + Assert.Equal(bigDecimal, (BigDecimal)actual); + } + } +} \ No newline at end of file diff --git a/test/FluentCassandra.Tests/TypesToDatabase/DecimalTypeTest.cs b/test/FluentCassandra.Tests/TypesToDatabase/DecimalTypeTest.cs new file mode 100644 index 0000000..25b1f2e --- /dev/null +++ b/test/FluentCassandra.Tests/TypesToDatabase/DecimalTypeTest.cs @@ -0,0 +1,42 @@ +using System; +using System.Numerics; +using Xunit; +using FluentCassandra.Types; + +namespace FluentCassandra.TypesToDatabase +{ + + public class DecimalTypeTest : IUseFixture, IDisposable + { + private CassandraContext _db; + + public void SetFixture(CassandraDatabaseSetupFixture data) + { + var setup = data.DatabaseSetup(); + _db = setup.DB; + } + + public void Dispose() + { + _db.Dispose(); + } + + public const string FamilyName = "StandardDecimalType"; + public const string TestKey = "Test1"; + + [Fact] + public void Save_BigDecimal() + { + // arrange + var family = _db.GetColumnFamily(FamilyName); + BigDecimal expected = 100002334.4563D; + + // act + family.InsertColumn(TestKey, expected, Math.PI); + var actual = family.GetColumn(TestKey, expected); + + // assert + Assert.Equal(expected, (BigDecimal)actual.ColumnName); + } + } +}