Add project files.

This commit is contained in:
Andy Howard
2021-05-05 15:36:28 +01:00
parent 949836d24c
commit a4cf706eb3
353 changed files with 60029 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31112.23
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SacsStandardBase", "SacsStandardBase\SacsStandardBase.csproj", "{3A8EE4B3-2335-420B-9619-D1622AF511FA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SacsStandardDbas", "SacsStandardDbas\SacsStandardDbas.csproj", "{14768EB5-6FA5-4F62-9347-44A667A760C1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SacsStandardMob", "SacsStandardMob\SacsStandardMob.csproj", "{E507AB63-28E3-4FF4-9EA8-2E87C42FFC20}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SacsCoreWeb", "SacsCoreWeb\SacsCoreWeb.csproj", "{AE8FCC39-D13D-4E32-995E-C69352F9F95E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SacsCoreUnitTests", "SacsCoreUnitTests\SacsCoreUnitTests.csproj", "{70F1302F-E8F2-49CB-A09D-889DA59681DD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3A8EE4B3-2335-420B-9619-D1622AF511FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3A8EE4B3-2335-420B-9619-D1622AF511FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A8EE4B3-2335-420B-9619-D1622AF511FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A8EE4B3-2335-420B-9619-D1622AF511FA}.Release|Any CPU.Build.0 = Release|Any CPU
{14768EB5-6FA5-4F62-9347-44A667A760C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{14768EB5-6FA5-4F62-9347-44A667A760C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14768EB5-6FA5-4F62-9347-44A667A760C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{14768EB5-6FA5-4F62-9347-44A667A760C1}.Release|Any CPU.Build.0 = Release|Any CPU
{E507AB63-28E3-4FF4-9EA8-2E87C42FFC20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E507AB63-28E3-4FF4-9EA8-2E87C42FFC20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E507AB63-28E3-4FF4-9EA8-2E87C42FFC20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E507AB63-28E3-4FF4-9EA8-2E87C42FFC20}.Release|Any CPU.Build.0 = Release|Any CPU
{AE8FCC39-D13D-4E32-995E-C69352F9F95E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AE8FCC39-D13D-4E32-995E-C69352F9F95E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE8FCC39-D13D-4E32-995E-C69352F9F95E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE8FCC39-D13D-4E32-995E-C69352F9F95E}.Release|Any CPU.Build.0 = Release|Any CPU
{70F1302F-E8F2-49CB-A09D-889DA59681DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{70F1302F-E8F2-49CB-A09D-889DA59681DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{70F1302F-E8F2-49CB-A09D-889DA59681DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{70F1302F-E8F2-49CB-A09D-889DA59681DD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {19DF326A-93A0-4990-9F4B-C0B0629FC8B3}
EndGlobalSection
EndGlobal
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<RootNamespace>Sacs.UnitTests.Net</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.0" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.0" />
<PackageReference Include="coverlet.collector" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SacsStandardBase\SacsStandardBase.csproj" />
<ProjectReference Include="..\SacsStandardDbas\SacsStandardDbas.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Core\" />
<Folder Include="Standard\Base\Core\" />
<Folder Include="Standard\Base\Dbas\" />
</ItemGroup>
</Project>
@@ -0,0 +1,35 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.App;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sacs.UnitTests.Net.Standard.Base.App
{
//=======================================================================
[TestClass]
public class SacsCommandLineInfo_Tester
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
[TestMethod]
public void SacsCommandLineInfo_CTR()
{
SacsCommandLineInfo cmdLineInfo = new SacsCommandLineInfo(new String[]
{
"-ABC",
"/DEF",
"Andy",
"-SaProcess:ExportData",
});
Assert.IsNotNull(cmdLineInfo);
Assert.AreEqual(4, cmdLineInfo.ParamCount);
Assert.AreEqual(1, cmdLineInfo.ProcessDefinitions.Count);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,52 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.App;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sacs.UnitTests.Net.Standard.Base.App
{
//=======================================================================
[TestClass]
public class SacsProcessDefinition_Tester
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
[TestMethod]
public void SacsProcessDefinition_CTR()
{
// Valid process definitions.
Assert.IsNotNull(new SacsProcessDefinition("SaProcess:ExportData"));
Assert.IsNotNull(new SacsProcessDefinition("SaProcess:Proc=ExportData"));
Assert.IsNotNull(new SacsProcessDefinition("SaProcess:ExportData;User=System"));
// Invalid process definition - blank argument.
try
{
SacsProcessDefinition processDfn = new SacsProcessDefinition("");
}
catch (Exception exc)
{
Assert.AreEqual(typeof(NotSupportedException), exc.GetType());
}
}
//-------------------------------------------------------------------
[TestMethod]
public void SacsProcessDefinition_IsValidProcessDefinition()
{
// Valid process params.
Assert.AreEqual(true, SacsProcessDefinition.IsValidProcessDefinition("SaProcess:ExportData"));
// Invalid process params.
Assert.AreEqual(false, SacsProcessDefinition.IsValidProcessDefinition(""));
Assert.AreEqual(false, SacsProcessDefinition.IsValidProcessDefinition("Abc"));
Assert.AreEqual(false, SacsProcessDefinition.IsValidProcessDefinition("SaProcess"));
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,179 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sacs.UnitTests.Net.Standard.Base.Bus
{
[TestClass]
public class SysId_Tester
{
//-------------------------------------------------------------------
// Data members.
private readonly SysId idNull = null;
private readonly SysId id_ = new SysId();
private readonly SysId id0 = new SysId(0);
private readonly SysId id1 = new SysId(1);
//-------------------------------------------------------------------
[TestMethod]
public void SysId_CTR_Void ()
{
Assert.IsNotNull(new SysId());
Assert.AreEqual(new SysId().ToInt(), 0);
Assert.IsTrue(String.IsNullOrEmpty(new SysId().ToString()));
Assert.IsTrue(StringUtil.IsEmpty(new SysId().ToString()));
Assert.AreEqual(new SysId().ToString(), "");
Assert.AreEqual(new SysId().ToString(), string.Empty);
}
[TestMethod]
public void SysId_CTR_Long ()
{
// Zero - invalid.
Assert.IsNotNull(new SysId(0));
// 1 - Valid.
Assert.IsNotNull(new SysId(1));
Assert.IsNotNull(new SysId(-1));
}
//-------------------------------------------------------------------
// Is Valid.
[TestMethod]
public void SysId_IsValid ()
{
Assert.IsFalse(SysId.IsValid(null));
Assert.IsFalse(SysId.IsValid(new SysId()));
Assert.IsFalse(SysId.IsValid(new SysId(0)));
Assert.IsTrue(SysId.IsValid(new SysId(1)));
Assert.IsTrue(SysId.IsValid(new SysId(-1)));
}
//-------------------------------------------------------------------
// Invalid on null.
[TestMethod]
public void SysId_InvalidOnNull ()
{
Assert.IsNotNull(SysId.InvalidOnNull(null));
Assert.IsFalse(SysId.IsValid(SysId.InvalidOnNull(null)));
Assert.IsFalse(SysId.IsValid(SysId.InvalidOnNull(new SysId())));
Assert.IsFalse(SysId.IsValid(SysId.InvalidOnNull(new SysId(0))));
Assert.IsTrue(SysId.IsValid(SysId.InvalidOnNull(new SysId(1))));
Assert.IsTrue(SysId.IsValid(SysId.InvalidOnNull(new SysId(-1))));
}
//-------------------------------------------------------------------
[TestMethod]
public void SysId_Equals_Object ()
{
Assert.IsFalse(id_.Equals(null));
Assert.IsFalse(id0.Equals(null));
Assert.IsFalse(id1.Equals(null));
Assert.IsFalse(id_.Equals((Object)id_));
Assert.IsFalse(id_.Equals((Object)id0));
Assert.IsFalse(id_.Equals((Object)id1));
Assert.IsFalse(id0.Equals((Object)id_));
Assert.IsFalse(id0.Equals((Object)id0));
Assert.IsFalse(id0.Equals((Object)id1));
Assert.IsFalse(id1.Equals((Object)id_));
Assert.IsFalse(id1.Equals((Object)id0));
Assert.IsTrue(id1.Equals((Object)id1));
}
[TestMethod]
public void SysId_Equals_SysId ()
{
// Invalid values always unequal.
Assert.IsFalse(id_.Equals(null));
Assert.IsFalse(id_.Equals(idNull));
Assert.IsFalse(id_.Equals(id_));
Assert.IsFalse(id_.Equals(id0));
Assert.IsFalse(id_.Equals(new SysId()));
// Valid values.
Assert.IsTrue(id1.Equals(new SysId(1)));
for (int i = 1; i <= 1000; i++)
{
SysId idI = new SysId(i);
for (int j = 1; j <= 1000; j++)
{
SysId idJ = new SysId(j);
if (i == j) Assert.IsTrue(idI.Equals(idJ));
else Assert.IsFalse(idI.Equals(idJ));
}
}
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// S y s I d L i s t
//-------------------------------------------------------------------
[TestMethod]
public void SysId_List_GetIdCount ()
{
List<SysId> ids = new List<SysId>()
{
new SysId(),
new SysId(1),
new SysId(2),
new SysId(3),
new SysId(),
new SysId(3),
new SysId(2),
new SysId(1),
new SysId(),
};
// Unique, valid only.
Assert.AreEqual(3, ids.GetIdCount());
Assert.AreEqual(3, ids.GetIdCount(true,true));
// Unique including invalid.
Assert.AreEqual(4, ids.GetIdCount(false));
Assert.AreEqual(4, ids.GetIdCount(false,true));
// Duplicate, valid only.
Assert.AreEqual(6, ids.GetIdCount(true, false));
// Duplicate including invalid.
Assert.AreEqual(9, ids.GetIdCount(false,false));
}
//-------------------------------------------------------------------
[TestMethod]
public void SysId_List_HasValidIds ()
{
List<SysId> ids = new List<SysId>();
// Single valid Id.
ids.Clear();
ids.Add(new SysId(123));
Assert.AreEqual(true, ids.HasValidIds());
// Several valid ids.
ids.Clear();
ids.AddRange(new List<SysId>()
{
new SysId(1),
new SysId(2),
new SysId(3),
});
Assert.AreEqual(true, ids.HasValidIds());
// Invalid only.
ids.Clear();
ids.AddRange(new List<SysId>()
{
new SysId(),
new SysId(),
new SysId(),
});
Assert.AreEqual(false, ids.HasValidIds());
// Add valid Id.
ids.Add(new SysId(123));
Assert.AreEqual(true, ids.HasValidIds());
}
//-------------------------------------------------------------------
}
}
@@ -0,0 +1,94 @@
using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
using Sarand.Net.Standard.Base.Data;
namespace Sacs.UnitTests.Net.Standard.Base.Data
{
//=======================================================================
[TestClass]
public class SacsAesIvCrypter_Tester
{
//-------------------------------------------------------------------
// Data members.
private static Random random = new Random();
private TestContext testContextInstance;
//-------------------------------------------------------------------
// Construction.
public SacsAesIvCrypter_Tester ()
{
}
//-------------------------------------------------------------------
/// <summary>
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///</summary>
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
//-------------------------------------------------------------------
#region Additional test attributes
//
// You can use the following additional attributes as you write your tests:
//
// Use ClassInitialize to run code before running the first test in the class
// [ClassInitialize()]
// public static void MyClassInitialize(TestContext testContext) { }
//
// Use ClassCleanup to run code after all tests in a class have run
// [ClassCleanup()]
// public static void MyClassCleanup() { }
//
// Use TestInitialize to run code before running each test
// [TestInitialize()]
// public void MyTestInitialize() { }
//
// Use TestCleanup to run code after each test has run
// [TestCleanup()]
// public void MyTestCleanup() { }
//
#endregion
//-------------------------------------------------------------------
[TestMethod]
public void SacsAesIvCrypter_Test ()
{
for (int i = 0; i < 200; i++)
{
String data = RandomString(random.Next(10, 2048), true);
int dataLength = data.Length;
String key = RandomString(random.Next(10, 128), true);
int keyLength = key.Length;
String encrypted = SacsAesIvCrypter.Encrypt(data, key);
int encryptedLength = encrypted.Length;
Assert.AreEqual(data, SacsAesIvCrypter.Decrypt(encrypted, key));
}
}
//-------------------------------------------------------------------
/// <summary>Generate random alphanumeric string</summary>
public static String RandomString (int length, bool includeLowerCase = true, bool includeSymbols = true)
{
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
if (includeLowerCase) chars += "abcdefghijklmnopqrstuvwxyz";
if (includeSymbols) chars += "`¬!\"£$%^&*()-=_+{}[];:'@#~<>?/.,\\| ";
return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.Data;
namespace Sacs.UnitTests.Net.Standard.Base.Data
{
//=======================================================================
[TestClass]
public class SacsCsvHandler_Tester
{
//-------------------------------------------------------------------
[TestMethod]
public void ParseCsvRecord_FieldCount ()
{
Assert.AreEqual(0, SacsCsvHandler.ParseCsvRecord(null).Count);
Assert.AreEqual(1, SacsCsvHandler.ParseCsvRecord("").Count);
Assert.AreEqual(2, SacsCsvHandler.ParseCsvRecord(",").Count);
Assert.AreEqual(3, SacsCsvHandler.ParseCsvRecord("A,B,C").Count);
Assert.AreEqual(5, SacsCsvHandler.ParseCsvRecord("A,B,C,D,E").Count);
}
//-------------------------------------------------------------------
[TestMethod]
public void ParseCsvRecord_Simple_NoText ()
{
String data = "A,B,C";
List<String> fields = SacsCsvHandler.ParseCsvRecord(data);
Assert.AreEqual(3, fields.Count);
Assert.AreEqual("B", fields[1]);
}
//-------------------------------------------------------------------
[TestMethod]
public void ParseCsvRecord_Text_Simple ()
{
List<String> fields = SacsCsvHandler.ParseCsvRecord("A,\"B\",C");
Assert.AreEqual(3, fields.Count);
Assert.AreEqual("A", fields[0]);
Assert.AreEqual("B", fields[1]);
Assert.AreEqual("C", fields[2]);
fields = SacsCsvHandler.ParseCsvRecord("A,\"B\",\"C\"");
Assert.AreEqual(3, fields.Count);
Assert.AreEqual("A", fields[0]);
Assert.AreEqual("B", fields[1]);
Assert.AreEqual("C", fields[2]);
}
//-------------------------------------------------------------------
[TestMethod]
public void ParseCsvRecord_Text_WithFieldDelim ()
{
List<String> fields = SacsCsvHandler.ParseCsvRecord("A,\",\",C");
Assert.AreEqual(3, fields.Count);
Assert.AreEqual("A", fields[0]);
Assert.AreEqual(",", fields[1]);
Assert.AreEqual("C", fields[2]);
}
//-------------------------------------------------------------------
[TestMethod]
public void ParseCsvRecord_Text_EmbeddedTextDelims ()
{
List<String> fields = SacsCsvHandler.ParseCsvRecord("A,\"\"\"\",C");
Assert.AreEqual(3, fields.Count);
Assert.AreEqual("A", fields[0]);
Assert.AreEqual("\"", fields[1]);
Assert.AreEqual("C", fields[2]);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,85 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Sacs.UnitTests.Net.Standard.Base.Data
{
//=======================================================================
[TestClass]
public class SacsDataFn_Tester
{
//-------------------------------------------------------------------
// Data memebrs.
private static Random random = new Random();
//-------------------------------------------------------------------
/// <summary>Create random alphanumeric string of specified length.</summary>
public static string RandomString (int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
//-------------------------------------------------------------------
[TestMethod]
public void Sapphire ()
{
String encodedEncrypted = "6NP R78:P.8$2Q";
String raw = SacsDataFn._sadcs (encodedEncrypted, new SacsData("SaItemOptionMgr_Odbc"));
Assert.AreEqual("sarand", raw);
}
//-------------------------------------------------------------------
[TestMethod]
public void Encode ()
{
// Test the Encoding algorythm.
for (int i = 0; i < 1000; i++)
{
String rawData = RandomString(random.Next(10,21));
SacsData dataEnc = SacsDataFn._saecd (new SacsData(rawData).DataBuffer);
Assert.AreNotEqual(rawData, dataEnc.NullTerminatedString);
SacsData dataDec = SacsDataFn._sadcd (dataEnc.DataBuffer);
Assert.AreEqual(rawData, dataDec.NullTerminatedString);
}
}
//-------------------------------------------------------------------
[TestMethod]
public void Encrypt_String ()
{
// Test the Encryption algorythm.
for (int i = 0; i < 1000; i++)
{
String rawData = RandomString(random.Next(10,21));
String dataEnc = SacsDataFn._saecs (rawData);
Assert.AreNotEqual(rawData, dataEnc);
String dataDec = SacsDataFn._sadcs (dataEnc);
Assert.AreEqual(rawData, dataDec);
}
}
//-------------------------------------------------------------------
[TestMethod]
public void RC4Encrypt_String ()
{
// Test the RC4 algorythm:
for (int i = 0; i < 1000; i++)
{
String rawData = RandomString(random.Next(10,21));
SacsData dataEnc = SacsDataFn.RC4 (new SacsData(rawData).DataBuffer);
Assert.AreNotEqual(rawData, dataEnc.NullTerminatedString);
SacsData dataDec = SacsDataFn.RC4 (dataEnc.DataBuffer);
Assert.AreEqual(rawData, dataDec.NullTerminatedString);
}
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,61 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Sacs.UnitTests.Net.Standard.Base.Data
{
//=======================================================================
[TestClass]
public class SacsData_Tester
{
//-------------------------------------------------------------------
// Data memebrs.
private static Random random = new Random();
//-------------------------------------------------------------------
/// <summary>Create random alphanumeric string of specified length.</summary>
public static string RandomString (int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
//-------------------------------------------------------------------
[TestMethod]
public void CTR_String ()
{
for (int i = 0; i < 1000; i++)
{
String rawValue = RandomString(random.Next(10,21));
// Default (ASCII) encoding.
Assert.AreEqual(rawValue, (String)new SacsData(rawValue));
// UniCode encoding.
Assert.AreEqual(rawValue, (String)new SacsData(rawValue, Encoding.Unicode));
}
}
//-------------------------------------------------------------------
[TestMethod]
public void CTR_Copy ()
{
for (int i = 0; i < 1000; i++)
{
String rawValue = RandomString(random.Next(10,21));
// Default (ASCII) encoding.
Assert.AreEqual(rawValue, (String)new SacsData(new SacsData(rawValue)));
// UTF-8 encoding.
Assert.AreEqual(rawValue, (String)new SacsData(new SacsData(rawValue, Encoding.UTF8)));
// UniCode encoding.
Assert.AreEqual(rawValue, (String)new SacsData(new SacsData(rawValue, Encoding.Unicode)));
}
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,86 @@
using System.Drawing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Sacs.UnitTests.Net.Standard.Base.Generic
{
[TestClass]
public class ObjectReferences_Tester
{
//-------------------------------------------------------------------
[TestMethod]
public void ObjectReferences_ChangeValuesOnReferencedObject ()
{
// size2 starts as a reference to size1.
Size size1 = new Size(10, 20);
Size size2 = size1;
Size size3 = new Size(10, 20);
Assert.AreEqual(size1, size2);
Assert.AreEqual(size1, size3);
Assert.AreEqual(size2, size3);
// Change size1 values - doesn't change size2.
size1.Width = 100;
size1.Height = 200;
Assert.AreNotEqual(size1, size2);
Assert.AreNotEqual(size1, size3);
Assert.AreEqual(size2, size3);
}
[TestMethod]
public void ObjectReferences_ChangeValuesOnObjectReference ()
{
// size2 starts as a reference to size1.
Size size1 = new Size(10, 20);
Size size2 = size1;
Size size3 = new Size(10, 20);
Assert.AreEqual(size1, size2);
Assert.AreEqual(size1, size3);
Assert.AreEqual(size2, size3);
// Change size2 values - doesn't change size1.
size2.Width = 100;
size2.Height = 200;
Assert.AreNotEqual(size1, size2);
Assert.AreEqual(size1, size3);
Assert.AreNotEqual(size2, size3);
}
//-------------------------------------------------------------------
// Modify class value.
class TestData
{
public int a = 0;
public int B { get; set; }
public Size size1 = new Size(10,20);
public Size Size2 { get; set; } = new Size(30, 40);
}
[TestMethod]
public void ObjectReferences_ChangeClassValues ()
{
TestData td1 = new TestData();
TestData td2 = td1;
Assert.AreEqual(td1, td2);
td1.a++; // td2 is still a reference to td1 with the same values.
Assert.AreEqual(td1, td2);
Assert.AreEqual(td1.a, td2.a);
Assert.AreEqual(td1.B, td2.B);
Assert.AreEqual(td1.size1, td2.size1);
Assert.AreEqual(td1.Size2, td2.Size2);
td1.B++; // td2 is still a reference to td1 with the same values.
Assert.AreEqual(td1, td2);
Assert.AreEqual(td1.a, td2.a);
Assert.AreEqual(td1.B, td2.B);
Assert.AreEqual(td1.size1, td2.size1);
Assert.AreEqual(td1.Size2, td2.Size2);
td1.size1.Width += 10; // td2 is still a reference to td1 with the same values.
Assert.AreEqual(td1, td2);
Assert.AreEqual(td1.a, td2.a);
Assert.AreEqual(td1.B, td2.B);
Assert.AreEqual(td1.size1, td2.size1);
Assert.AreEqual(td1.Size2, td2.Size2);
td1.Size2 = new Size(td1.Size2.Width, td1.Size2.Height + 10); // td2 is still a reference to td1 with the same values.
Assert.AreEqual(td1, td2);
Assert.AreEqual(td1.a, td2.a);
Assert.AreEqual(td1.B, td2.B);
Assert.AreEqual(td1.size1, td2.size1);
Assert.AreEqual(td1.Size2, td2.Size2);
}
//-------------------------------------------------------------------
}
}
@@ -0,0 +1,19 @@
using System.Text.RegularExpressions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Sacs.UnitTests.Net.Standard.Base.Generic
{
[TestClass]
public class Regex_Tester
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
[TestMethod]
public void Regex_FolderPaths ()
{
Assert.AreEqual(true, Regex.IsMatch("abcabcabc\\.metadata\\.plugins\\abcabcabc", ".*?\\\\\\.metadata\\\\\\.plugins\\\\.*?"));
}
//-------------------------------------------------------------------
}
}
@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
namespace Sacs.UnitTests.Net.Standard.Base.Helpers
{
//=======================================================================
[TestClass]
public class SacsCsvHelper_Tester
{
//-------------------------------------------------------------------
// C o n v e r t F r o m C S V
//-------------------------------------------------------------------
[TestMethod]
public void ParseCsv_Data ()
{
List<String> data = new List<string>()
{
"A,B,C",
"D,\"E\",F,\"\"\"G\"\"\",H,\"I,\"",
"W,X,Y,Z"
};
// Parser configurations - all for 'standard' conversion.
List<SacsCsvHelper.ParserConfig> parserConfigs = new List<SacsCsvHelper.ParserConfig>()
{
null,
new SacsCsvHelper.ParserConfig() { BufferSize = 1024 },
new SacsCsvHelper.ParserConfig() { CountBytes = true },
new SacsCsvHelper.ParserConfig() { Encoding = Encoding.ASCII },
new SacsCsvHelper.ParserConfig() { LineBreakInQuotedFieldIsBadData = true },
new SacsCsvHelper.ParserConfig() { Comment = '\'' },
new SacsCsvHelper.ParserConfig() { AllowComments = true },
new SacsCsvHelper.ParserConfig() { IgnoreBlankLines = false },
new SacsCsvHelper.ParserConfig() { IgnoreQuotes = false },
new SacsCsvHelper.ParserConfig() { Quote = '"' },
new SacsCsvHelper.ParserConfig() { Delimiter = "," },
new SacsCsvHelper.ParserConfig() { Escape = '"' },
//new SacsCsvHelper.ParserConfig() { TrimOptions = TrimOptions.Trim },
};
foreach (SacsCsvHelper.ParserConfig parserConfig in parserConfigs)
{
List<String[]> records = SacsCsvHelper.ParseCsv(data.Concat(Environment.NewLine), parserConfig);
Assert.IsNotNull(records);
// Record count.
Assert.AreEqual(data.Count, records.Count);
// Field counts.
Assert.AreEqual(3, records[0].Length);
Assert.AreEqual(6, records[1].Length);
Assert.AreEqual(4, records[2].Length);
// Field values.
Assert.AreEqual("B", records[0][1]);
Assert.AreEqual("E", records[1][1]);
Assert.AreEqual("\"G\"", records[1][3]);
Assert.AreEqual("I,", records[1][5]);
Assert.AreEqual("X", records[2][1]);
}
}
//-------------------------------------------------------------------
[TestMethod]
public void ParseCsv_Data_InvalidQuotes ()
{
List<String> data = new List<string>()
{
"A,B,C",
"D,\"E,G,F,G,H,I",
"W,X,Y,Z"
};
SacsCsvHelper.ParserConfig config = new SacsCsvHelper.ParserConfig();
List<String[]> records = SacsCsvHelper.ParseCsv(data.Concat(Environment.NewLine));
Assert.IsNotNull(records);
// 1 invalid record ignored.
Assert.AreEqual(data.Count - 1, records.Count);
}
//-------------------------------------------------------------------
// C o n v e r t T o C S V
//-------------------------------------------------------------------
[TestMethod]
public void ToCsv_Fields ()
{
//SaCsvHelper csvHelper = new SaCsvHelper();
// Single record, alternative collections.
Assert.AreEqual("A,B,C", SacsCsvHelper.ToCsv(new String[] { "A", "B", "C" }));
Assert.AreEqual("A,B,C", SacsCsvHelper.ToCsv(new Collection<String>() { "A", "B", "C" }));
Assert.AreEqual("A,B,C", SacsCsvHelper.ToCsv(new List<String>() { "A", "B", "C" }));
Assert.AreEqual("D,E,F", SacsCsvHelper.ToCsv(new String[] { "D", "E", "F" }));
// Embedded delimiter.
Assert.AreEqual("\"A,\",B,C", SacsCsvHelper.ToCsv(new String[] { "A,", "B", "C" }));
Assert.AreEqual("A,\",B\",C", SacsCsvHelper.ToCsv(new String[] { "A", ",B", "C" }));
// Embedded quotes.
Assert.AreEqual("\"\"\"A\"\"\",B,C", SacsCsvHelper.ToCsv(new String[] { "\"A\"", "B", "C" }));
}
//-------------------------------------------------------------------
[TestMethod]
public void ToCsv_Records ()
{
//SaCsvHelper csvHelper = new SaCsvHelper();
List<String[]> dataRecords = new List<String[]>
{
new String[]{ "A", "B", "C" },
new String[]{ "D", "E", "F", "G", "H", "I" },
new String[]{ "W", "X", "Y", "Z" }
};
List<String> records = SacsCsvHelper.ToCsv(dataRecords);
// Multiple records, multiple times to verify clearing of data each time.
for (int i = 0; i < 10; i++)
{
Assert.AreEqual(3, records.Count);
Assert.AreEqual("A,B,C", records[0]);
Assert.AreEqual("D,E,F,G,H,I", records[1]);
Assert.AreEqual("W,X,Y,Z", records[2]);
}
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,259 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.UnitTests.Core.Base.Utils
{
//=======================================================================
[TestClass]
public class DateUtil_Tester
{
//-------------------------------------------------------------------
// Data members.
private Random random = new Random();
private DateTime today = DateTime.Today;
//-------------------------------------------------------------------
[TestMethod]
public void DateTime_CTR_Void ()
{
Assert.IsNotNull(new DateTime());
Assert.AreEqual(new DateTime(), DateTime.MinValue);
}
//-------------------------------------------------------------------
// S e t T i m e
//-------------------------------------------------------------------
[TestMethod]
public void SetTime_DateTime ()
{
for (int i = 0; i < 1000; i++)
{
DateTime date = new DateTime(random.Next(1970, 2100), random.Next(1, 13), random.Next(1, 28), random.Next(0, 24), random.Next(0, 60), random.Next(0, 60));
DateTime time = new DateTime(random.Next(1970, 2100), random.Next(1, 13), random.Next(1, 28), random.Next(0, 24), random.Next(0, 60), random.Next(0, 60));
Assert.AreEqual(DateUtil.FormatXmlDate(date), DateUtil.FormatXmlDate(date.SetTime(time)));
Assert.AreEqual(DateUtil.FormatXmlTime(time), DateUtil.FormatXmlTime(date.SetTime(time)));
}
}
[TestMethod]
public void SetTime_TimeSpan ()
{
for (int i = 0; i < 1000; i++)
{
DateTime date = new DateTime(random.Next(1970, 2100), random.Next(1, 13), random.Next(1, 28), random.Next(0, 24), random.Next(0, 60), random.Next(0, 60));
TimeSpan time = new TimeSpan(random.Next(0, 24), random.Next(0, 60), random.Next(0, 60));
Assert.AreEqual(DateUtil.FormatXmlDate(date), DateUtil.FormatXmlDate(date.SetTime(time)));
Assert.AreEqual(DateUtil.FormatXmlTime(today + time), DateUtil.FormatXmlTime(date.SetTime(time)));
}
}
//-------------------------------------------------------------------
// R o u n d T i m e
//-------------------------------------------------------------------
[TestMethod]
public void RoundTime ()
{
for (int i = 0; i < 2000; i++)
{
DateTime datetime = new DateTime(random.Next(1970, 2100), random.Next(1, 13), random.Next(1, 28), random.Next(0, 24), random.Next(0, 60), random.Next(0, 60));
ushort intervalMins = (ushort)random.Next(1,61);
{
// Round up without seconds.
int expectedMins = ((datetime.Minute / intervalMins) + (datetime.Minute % intervalMins > 0 ? 1 : 0)) * intervalMins;
DateTime expected = datetime.Date + new TimeSpan(datetime.Hour,expectedMins,0);
DateTime rounded = DateUtil.RoundTime(datetime,intervalMins,DateUtil.RoundingDirection.Up,true);
Assert.AreEqual(DateUtil.FormatXmlDateTime(expected), DateUtil.FormatXmlDateTime(rounded));
}
{
// Round up with seconds.
int expectedMins = ((datetime.Minute / intervalMins) + ((datetime.Minute * 60 + datetime.Second) % (intervalMins * 60) > 0 ? 1 : 0)) * intervalMins;
DateTime expected = datetime.Date + new TimeSpan(datetime.Hour,expectedMins,0);
DateTime rounded = DateUtil.RoundTime(datetime,intervalMins,DateUtil.RoundingDirection.Up,false);
Assert.AreEqual(DateUtil.FormatXmlDateTime(expected), DateUtil.FormatXmlDateTime(rounded));
}
{
// Round down without seconds.
int expectedMins = (datetime.Minute / intervalMins) * intervalMins;
DateTime expected = datetime.Date + new TimeSpan(datetime.Hour,expectedMins,0);
DateTime rounded = DateUtil.RoundTime(datetime,intervalMins,DateUtil.RoundingDirection.Down,true);
Assert.AreEqual(DateUtil.FormatXmlDateTime(expected), DateUtil.FormatXmlDateTime(rounded));
}
{
// Round down with seconds.
int expectedMins = (datetime.Minute / intervalMins) * intervalMins;
DateTime expected = datetime.Date + new TimeSpan(datetime.Hour,expectedMins,0);
DateTime rounded = DateUtil.RoundTime(datetime,intervalMins,DateUtil.RoundingDirection.Down,false);
Assert.AreEqual(DateUtil.FormatXmlDateTime(expected), DateUtil.FormatXmlDateTime(rounded));
}
{
// Nearest without seconds.
int expectedMins = ((datetime.Minute / intervalMins) + (datetime.Minute % intervalMins > (intervalMins/2) ? 1 : 0)) * intervalMins;
DateTime expected = datetime.Date + new TimeSpan(datetime.Hour,expectedMins,0);
DateTime rounded = DateUtil.RoundTime(datetime,intervalMins,DateUtil.RoundingDirection.Nearest,true);
if (!DateUtil.FormatXmlDateTime(expected).Equals(DateUtil.FormatXmlDateTime(rounded)))
{
//String s = "";
}
Assert.AreEqual(DateUtil.FormatXmlDateTime(expected), DateUtil.FormatXmlDateTime(rounded));
}
{
// Nearest with seconds.
int expectedMins = ((datetime.Minute / intervalMins) + ((datetime.Minute * 60 + datetime.Second) % (intervalMins * 60) > (intervalMins*60/2) ? 1 : 0)) * intervalMins;
DateTime expected = datetime.Date + new TimeSpan(datetime.Hour,expectedMins,0);
DateTime rounded = DateUtil.RoundTime(datetime,intervalMins,DateUtil.RoundingDirection.Nearest,false);
if (!DateUtil.FormatXmlDateTime(expected).Equals(DateUtil.FormatXmlDateTime(rounded)))
{
//String s = "";
}
Assert.AreEqual(DateUtil.FormatXmlDateTime(expected), DateUtil.FormatXmlDateTime(rounded));
}
}
}
//-------------------------------------------------------------------
// C o n v e r t F r o m S t r i n g
//-------------------------------------------------------------------
[TestMethod]
public void DateTime_ParseXmlDate ()
{
String xmlDate = "2015-05-28";
Assert.AreEqual(new DateTime(2015, 5, 28), DateUtil.ParseXmlDate(xmlDate));
}
//-------------------------------------------------------------------
[TestMethod]
public void DateTime_ParseXmlTime ()
{
String xmlTime = "16:25:57";
Assert.AreEqual(new DateTime(today.Year, today.Month, today.Day, 16, 25, 57), DateUtil.ParseXmlTime(xmlTime));
}
//-------------------------------------------------------------------
[TestMethod]
public void DateTime_ParseXmlDateTime ()
{
String xmlDateTime = "2010-05-28T17:05:01";
Assert.AreEqual(new DateTime(2010, 5, 28, 17, 5, 1), DateUtil.ParseXmlDateTime(xmlDateTime));
}
//-------------------------------------------------------------------------
// V a l i d a t i o n
//-------------------------------------------------------------------------
[TestMethod]
public void DateTime_IsValid ()
{
// Valid.
Assert.AreEqual(true, DateUtil.IsValid(DateTime.Now));
Assert.AreEqual(true, DateUtil.IsValid(DateTime.Today));
Assert.AreEqual(true, DateUtil.IsValid(new DateTime(2018, 2, 12, 10, 32, 56)));
// Invalid.
Assert.AreEqual(false, DateUtil.IsValid(DateTime.MinValue));
Assert.AreEqual(false, DateUtil.IsValid(DateTime.MaxValue));
Assert.AreEqual(false, DateUtil.IsValid(new DateTime(0)));
Assert.AreEqual(false, DateUtil.IsValid(new DateTime(1, 1, 1)));
// Nullable.
Assert.AreEqual(false, DateUtil.IsValid(null));
}
//-------------------------------------------------------------------
// Relative dates - Week.
// DateUtil.StartOfWeek
[TestMethod]
public void DateTime_StartOfWeek ()
{
for (int i = 0; i < 1000; i++)
{
// Random date between 0AD and 3000AD (or thereabouts):
DateTime date = new DateTime(random.Next(0, 365 * 3000) * TimeSpan.TicksPerDay);
//Trace.WriteLine("DateUtil_Tester.DateTime_StartOfWeek Date=" + date.Format("ddd d-MMM-yyyy") + ", Start of week=" + date.StartOfWeek().Format("ddd d-MMM-yyyy"));
Assert.AreEqual(date.StartOfWeek().Format("ddd"), "Sun");
}
}
// DateUtil.StartOfWeek
[TestMethod]
public void DateTime_StartOfWorkingWeek ()
{
for (int i = 0; i < 1000; i++)
{
// Random date between 0AD and 3000AD (or thereabouts):
DateTime date = new DateTime(random.Next(0, 365 * 3000) * TimeSpan.TicksPerDay);
//Trace.WriteLine("DateUtil_Tester.DateTime_StartOfWorkingWeek Date=" + date.Format("ddd d-MMM-yyyy") + ", Start of week=" + date.StartOfWorkingWeek().Format("ddd d-MMM-yyyy"));
Assert.AreEqual(date.StartOfWorkingWeek().Format("ddd"), "Mon");
}
}
//-------------------------------------------------------------------
// Relative dates - Month.
// DateUtil.StartOfMonth
[TestMethod]
public void DateTime_StartOfMonth ()
{
DateTime dt20160723 = new DateTime(2016, 7, 23);
Assert.AreEqual(dt20160723.StartOfMonth(), new DateTime(2016, 7, 1));
}
// DateUtil.EndOfMonth
[TestMethod]
public void DateTime_DaysInMonth ()
{
DateTime dt20160723 = new DateTime(2016, 7, 23);
Assert.AreEqual(dt20160723.DaysInMonth(), 31);
}
// DateUtil.EndOfMonth
[TestMethod]
public void DateTime_EndOfMonth ()
{
DateTime dt20160723 = new DateTime(2016, 7, 23);
Assert.AreEqual(dt20160723.EndOfMonth(), new DateTime(2016, 7, 31));
}
//-------------------------------------------------------------------
// F o r m a t t i n g
//-------------------------------------------------------------------
[TestMethod]
public void DateTime_Format_Void ()
{
DateTime dt20160723 = new DateTime(2016, 7, 23);
DateTime dt20160723172829 = new DateTime(2016, 7, 23, 17, 28, 39);
Assert.AreEqual(dt20160723.Format("dd/MM/yyyy"), "23/07/2016");
Assert.AreEqual(dt20160723.Format("dd/MM/yyyy HH:mm:ss"), "23/07/2016 00:00:00");
Assert.AreEqual(dt20160723172829.Format("dd/MM/yyyy"), "23/07/2016");
Assert.AreEqual(dt20160723172829.Format("dd/MM/yyyy HH:mm:ss"), "23/07/2016 17:28:39");
}
//-------------------------------------------------------------------
[TestMethod]
public void DateTime_FormatXmlDate ()
{
DateTime dt20160723 = new DateTime(2016, 7, 23);
DateTime dt20160723172829 = new DateTime(2016, 7, 23, 17, 28, 39);
Assert.AreEqual("2016-07-23", DateUtil.FormatXmlDate(dt20160723));
Assert.AreEqual("2016-07-23", DateUtil.FormatXmlDate(dt20160723172829));
}
//-------------------------------------------------------------------
[TestMethod]
public void DateTime_FormatXmlTime ()
{
DateTime dt20160723172829 = new DateTime(2016, 7, 23, 17, 28, 39);
Assert.AreEqual("17:28:39", DateUtil.FormatXmlTime(dt20160723172829));
}
//-------------------------------------------------------------------
[TestMethod]
public void DateTime_FormatXmlDateTime ()
{
DateTime dt20160723172829 = new DateTime(2016, 7, 23, 17, 28, 39);
Assert.AreEqual("2016-07-23T17:28:39", DateUtil.FormatXmlDateTime(dt20160723172829));
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,71 @@
using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.UnitTests.Core.Base.Utils
{
[TestClass]
public class EnumUtil_Tester
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
[TestMethod]
public void EnumUtil_IsValid ()
{
Assert.IsTrue(EnumUtil.IsValid<SaMessageLevel>(0));
Assert.IsTrue(EnumUtil.IsValid<SaMessageLevel>(1));
Assert.IsTrue(EnumUtil.IsValid<SaMessageLevel>(2));
Assert.IsTrue(EnumUtil.IsValid<SaMessageLevel>(3));
Assert.IsTrue(EnumUtil.IsValid<SaMessageLevel>(4));
Assert.IsTrue(EnumUtil.IsValid<SaMessageLevel>(5));
}
//-------------------------------------------------------------------
[TestMethod]
public void EnumUtil_GetName ()
{
Assert.AreEqual("Info", EnumUtil.GetName<SaMessageLevel>(SaMessageLevel.Info));
Assert.AreEqual("Debug", EnumUtil.GetName<SaMessageLevel>(SaMessageLevel.Debug));
}
//-------------------------------------------------------------------
[TestMethod]
public void EnumUtil_GetValues ()
{
Assert.AreEqual(9,EnumUtil.GetValues<SaMessageLevel>().ToList().Count);
Assert.AreEqual(SaMessageLevel.Info,EnumUtil.GetValues<SaMessageLevel>().ToArray()[6]);
}
//-------------------------------------------------------------------
[TestMethod]
public void EnumUtil_ParseEnum ()
{
Assert.AreEqual(SaMessageLevel.Info, EnumUtil.ParseEnum<SaMessageLevel>("Info"));
Assert.AreEqual(SaMessageLevel.Debug, EnumUtil.ParseEnum<SaMessageLevel>("Debug"));
}
[TestMethod]
[ExpectedException(typeof(ArgumentException), "Requested value 'ABC' was not found.")]
public void EnumUtil_ParseEnum_Exception ()
{
SaMessageLevel messageLevel = EnumUtil.ParseEnum<SaMessageLevel>("ABC");
}
//-------------------------------------------------------------------
[TestMethod]
public void EnumUtil_StartsWith ()
{
Assert.AreEqual(SaMessageLevel.Info, EnumUtil.StartsWith<SaMessageLevel>("I", SaMessageLevel.Error));
Assert.AreEqual(SaMessageLevel.Info, EnumUtil.StartsWith<SaMessageLevel>("i", SaMessageLevel.Error));
Assert.AreEqual(SaMessageLevel.Info, EnumUtil.StartsWith<SaMessageLevel>("inf", SaMessageLevel.Error));
Assert.AreEqual(SaMessageLevel.Debug, EnumUtil.StartsWith<SaMessageLevel>("D", SaMessageLevel.Error));
Assert.AreEqual(SaMessageLevel.Debug, EnumUtil.StartsWith<SaMessageLevel>("d", SaMessageLevel.Error));
Assert.AreEqual(SaMessageLevel.Debug, EnumUtil.StartsWith<SaMessageLevel>("DE", SaMessageLevel.Error));
Assert.AreEqual(SaMessageLevel.Debug, EnumUtil.StartsWith<SaMessageLevel>("DEB", SaMessageLevel.Error));
Assert.AreEqual(SaMessageLevel.Debug, EnumUtil.StartsWith<SaMessageLevel>("DEBU", SaMessageLevel.Error));
Assert.AreEqual(SaMessageLevel.Debug, EnumUtil.StartsWith<SaMessageLevel>("DEBUG", SaMessageLevel.Error));
Assert.AreEqual(EnumUtil.StartsWith<SaMessageLevel>("Err", SaMessageLevel.Error), SaMessageLevel.Error);
Assert.AreEqual(SaMessageLevel.None, EnumUtil.StartsWith<SaMessageLevel>("", SaMessageLevel.Error));
Assert.AreEqual(SaMessageLevel.Error, EnumUtil.StartsWith<SaMessageLevel>("ABC", SaMessageLevel.Error));
}
//-------------------------------------------------------------------
}
}
@@ -0,0 +1,63 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.Utils;
using System;
namespace Sarand.UnitTests.Core.Base.Utils
{
[TestClass]
public class FtpUtil_Tester
{
//-------------------------------------------------------------------
// Data members.
private Random random = new Random();
//-------------------------------------------------------------------
[TestMethod]
public void FtpUtil_CleansePath ()
{
Assert.AreEqual("abc", FtpUtil.CleansePath("abc"));
Assert.AreEqual("abc", FtpUtil.CleansePath("/abc"));
Assert.AreEqual("abc", FtpUtil.CleansePath("\\abc"));
Assert.AreEqual("abc", FtpUtil.CleansePath("/abc/"));
Assert.AreEqual("abc", FtpUtil.CleansePath("\\abc\\"));
Assert.AreEqual("abc/def", FtpUtil.CleansePath("abc/def"));
Assert.AreEqual("abc/def", FtpUtil.CleansePath("abc\\def"));
Assert.AreEqual("abc/def", FtpUtil.CleansePath("/abc/def"));
Assert.AreEqual("abc/def", FtpUtil.CleansePath("\\abc\\def"));
Assert.AreEqual("abc/def", FtpUtil.CleansePath("abc/def/"));
Assert.AreEqual("abc/def", FtpUtil.CleansePath("abc\\def\\"));
Assert.AreEqual("abc/def", FtpUtil.CleansePath("/abc/def/"));
Assert.AreEqual("abc/def", FtpUtil.CleansePath("\\abc\\def\\"));
Assert.AreEqual("abc/def", FtpUtil.CleansePath("/abc//def/"));
Assert.AreEqual("abc/def", FtpUtil.CleansePath("\\abc\\\\def\\"));
Assert.AreEqual("abc-def", FtpUtil.CleansePath("abc-def"));
Assert.AreEqual("abc.def", FtpUtil.CleansePath("abc.def"));
}
//-------------------------------------------------------------------
[TestMethod]
public void FtpUtil_ParentFolder ()
{
Assert.AreEqual("", FtpUtil.GetParentFolder("abc"));
Assert.AreEqual("", FtpUtil.GetParentFolder("/abc"));
Assert.AreEqual("", FtpUtil.GetParentFolder("\\abc"));
Assert.AreEqual("", FtpUtil.GetParentFolder("/abc/"));
Assert.AreEqual("", FtpUtil.GetParentFolder("\\abc\\"));
Assert.AreEqual("abc", FtpUtil.GetParentFolder("abc/def"));
Assert.AreEqual("abc", FtpUtil.GetParentFolder("abc\\def"));
Assert.AreEqual("abc", FtpUtil.GetParentFolder("/abc/def"));
Assert.AreEqual("abc", FtpUtil.GetParentFolder("\\abc\\def"));
Assert.AreEqual("abc", FtpUtil.GetParentFolder("abc/def/"));
Assert.AreEqual("abc", FtpUtil.GetParentFolder("abc\\def\\"));
Assert.AreEqual("abc", FtpUtil.GetParentFolder("/abc/def/"));
Assert.AreEqual("abc", FtpUtil.GetParentFolder("\\abc\\def\\"));
Assert.AreEqual("abc", FtpUtil.GetParentFolder("/abc//def/"));
Assert.AreEqual("abc", FtpUtil.GetParentFolder("\\abc\\\\def\\"));
Assert.AreEqual("", FtpUtil.GetParentFolder("abc-def"));
Assert.AreEqual("", FtpUtil.GetParentFolder("abc.def"));
}
//-------------------------------------------------------------------
}
}
@@ -0,0 +1,128 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.UnitTests.Core.Base.Utils
{
[TestClass]
public class StringListUtil_Tester
{
//-------------------------------------------------------------------
// Data members.
private Random random = new Random();
//-------------------------------------------------------------------
[TestMethod]
public void StringList_CreateList ()
{
List<String> values = StringListUtil.CreateList("ABC",3);
Assert.AreEqual(3, values.Count);
Assert.AreEqual("ABC,ABC,ABC", values.Concat(","));
}
//-------------------------------------------------------------------
[TestMethod]
public void StringList_RemoveBlanks ()
{
List<String> values = new List<string>();
values.Add("");
values.Add("A");
values.Add(" ");
values.Add("B");
values.Add("");
values.Add("C");
Assert.AreEqual(6, values.Count);
// Remove blanks without trimming.
values.RemoveBlanks(false);
Assert.AreEqual(4, values.Count);
// Remove blanks with trimming.
values.RemoveBlanks(true);
Assert.AreEqual(3, values.Count);
// Only blanks in list.
values.Clear();
values.Add("");
values.Add(" ");
values.Add("");
values.Add(" ");
values.Add("");
Assert.AreEqual(5, values.Count);
values.RemoveBlanks();
Assert.AreEqual(0, values.Count);
}
//-------------------------------------------------------------------
[TestMethod]
public void StringList_RemoveDuplicatesCI ()
{
List<String> values = new List<string>();
values.Add("C");
values.Add("A");
values.Add("c");
values.Add("B");
values.Add("b");
values.Add("C");
Assert.AreEqual(6, values.Count);
Assert.AreEqual("C,A,C,B,B,C", values.Concat(",").ToUpper());
// Remove blanks without trimming.
values.RemoveDuplicatesCI();
Assert.AreEqual(3, values.Count);
Assert.AreEqual("C,A,B", values.Concat(","));
}
//-------------------------------------------------------------------
[TestMethod]
public void StringList_Split ()
{
// Simple delimiter.
List<String> values = StringListUtil.Split("A:B:C",":");
Assert.AreEqual(3, values.Count);
Assert.AreEqual("A,B,C", values.Concat(","));
// Simple delimiter, null value.
values = StringListUtil.Split(null, ":");
Assert.AreEqual(0, values.Count);
Assert.AreEqual("", values.Concat(","));
// Simple delimiter, empty value.
values = StringListUtil.Split("", ":");
Assert.AreEqual(1, values.Count);
Assert.AreEqual("", values.Concat(","));
// Null delimiter.
values = StringListUtil.Split("A:B:C", null);
Assert.AreEqual(1, values.Count);
Assert.AreEqual("A:B:C", values.Concat(","));
// Blank delimiter.
values = StringListUtil.Split("A:B:C", "");
Assert.AreEqual(1, values.Count);
Assert.AreEqual("A:B:C", values.Concat(","));
// Blank values.
values = StringListUtil.Split("A::B: :C:::", ":", true, true);
Assert.AreEqual(3, values.Count);
Assert.AreEqual("A,B,C", values.Concat(","));
}
//-------------------------------------------------------------------
[TestMethod]
public void StringList_Concat ()
{
List<String> values = new List<string>();
values.Add("");
values.Add("A");
values.Add(" ");
values.Add("B");
values.Add("");
values.Add("C");
values.Add(" ");
Assert.AreEqual(7, values.Count);
Assert.AreEqual(",A,,B,,C,", values.Concat(","));
Assert.AreEqual(",A, ,B,,C, ", values.Concat(",", "", "", false, false));
Assert.AreEqual(",A,,B,,C,", values.Concat(",", "", "", true, false));
Assert.AreEqual("A,B,C", values.Concat(",", "", "", true, true));
}
//-------------------------------------------------------------------
}
}
@@ -0,0 +1,99 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.UnitTests.Core.Base.Utils
{
[TestClass]
public class StringUtil_Tester
{
//-------------------------------------------------------------------
// Data members.
private Random random = new Random();
//-------------------------------------------------------------------
// P a d d i n g
//-------------------------------------------------------------------
[TestMethod]
public void StringUtil_PadLeft ()
{
// Leading spaces.
Assert.AreEqual(" ", "".PadLeft(3, ' '));
Assert.AreEqual(" A", "A".PadLeft(3, ' '));
Assert.AreEqual(" AB", "AB".PadLeft(3, ' '));
Assert.AreEqual("ABC", "ABC".PadLeft(3, ' '));
Assert.AreEqual("ABCD", "ABCD".PadLeft(3, ' '));
// Leading zeroes.
Assert.AreEqual("00", "".PadLeft(2, '0'));
Assert.AreEqual("01", "1".PadLeft(2, '0'));
Assert.AreEqual("123", "123".PadLeft(3, '0'));
Assert.AreEqual("0123", "123".PadLeft(4, '0'));
Assert.AreEqual("00123", "123".PadLeft(5, '0'));
}
[TestMethod]
public void StringUtil_PadRight ()
{
// Leading spaces.
Assert.AreEqual(" ", "".PadRight(3, ' '));
Assert.AreEqual("A ", "A".PadRight(3, ' '));
Assert.AreEqual("AB ", "AB".PadRight(3, ' '));
Assert.AreEqual("ABC", "ABC".PadRight(3, ' '));
Assert.AreEqual("ABCD", "ABCD".PadRight(3, ' '));
// Trailing zeroes.
Assert.AreEqual("00", "".PadRight(2, '0'));
Assert.AreEqual("10", "1".PadRight(2, '0'));
Assert.AreEqual("12", "12".PadRight(2, '0'));
Assert.AreEqual("123", "123".PadRight(3, '0'));
Assert.AreEqual("1230", "123".PadRight(4, '0'));
Assert.AreEqual("12300", "123".PadRight(5, '0'));
}
//-------------------------------------------------------------------
// S u b S t r i n g
//-------------------------------------------------------------------
[TestMethod]
public void StringUtil_SubstringSafe ()
{
Assert.AreEqual(StringUtil.SubstringSafe(null, 0), "");
Assert.AreEqual(StringUtil.SubstringSafe("", 0), "");
Assert.AreEqual("".SubstringSafe(0), "");
// Right portion from start position.
Assert.AreEqual("abc".SubstringSafe(-3), "abc");
Assert.AreEqual("abc".SubstringSafe(-2), "abc");
Assert.AreEqual("abc".SubstringSafe(-1), "abc");
Assert.AreEqual("abc".SubstringSafe(0), "abc");
Assert.AreEqual("abc".SubstringSafe(1), "bc");
Assert.AreEqual("abc".SubstringSafe(2), "c");
Assert.AreEqual("abc".SubstringSafe(3), "");
// Zero length.
Assert.AreEqual(StringUtil.SubstringSafe(null, 0, 0), "");
Assert.AreEqual(StringUtil.SubstringSafe("", 0, 0), "");
Assert.AreEqual("".SubstringSafe(0, 0), "");
Assert.AreEqual("abc".SubstringSafe(-1, 0), "");
Assert.AreEqual("abc".SubstringSafe(0, 0), "");
Assert.AreEqual("abc".SubstringSafe(1, 0), "");
Assert.AreEqual("abc".SubstringSafe(2, 0), "");
Assert.AreEqual("abc".SubstringSafe(3, 0), "");
Assert.AreEqual("abc".SubstringSafe(4, 0), "");
// Mid portion.
Assert.AreEqual("abcde".SubstringSafe(0, 3), "abc");
Assert.AreEqual("abcde".SubstringSafe(1, 3), "bcd");
Assert.AreEqual("abcde".SubstringSafe(2, 3), "cde");
// Excessive length.
Assert.AreEqual("abcde".SubstringSafe(-1, 12), "abcde");
Assert.AreEqual("abcde".SubstringSafe(0, 12), "abcde");
Assert.AreEqual("abcde".SubstringSafe(1, 12), "bcde");
Assert.AreEqual("abcde".SubstringSafe(2, 12), "cde");
Assert.AreEqual("abcde".SubstringSafe(3, 12), "de");
Assert.AreEqual("abcde".SubstringSafe(4, 12), "e");
Assert.AreEqual("abcde".SubstringSafe(5, 12), "");
}
//-------------------------------------------------------------------
}
}
+16
View File
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Sacs.Net.Core</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SacsStandardBase\SacsStandardBase.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Web\App\" />
</ItemGroup>
</Project>
+208
View File
@@ -0,0 +1,208 @@
using System;
using System.IO;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.IO;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.App
{
//=======================================================================
public static class SaLogger
{
//-------------------------------------------------------------------
// Data members.
private static String logDirectoryPath = String.Empty;
private static String logFilenamePrefix = String.Empty;
//-------------------------------------------------------------------
// Construction.
//-------------------------------------------------------------------
// Properties.
public static SaMessageLevel ConsoleLevel { get; set; } = SaMessageLevel.Debug;
public static SaMessageLevel LogFileLevel { get; set; } = SaMessageLevel.Info;
public static String LogFolder
{
get { return logDirectoryPath; }
set
{
logDirectoryPath = "";
if (FileUtil.ValidateDirectory(value, true))
logDirectoryPath = value;
}
}
public static bool LoggingToFile
{
get { return !StringUtil.IsEmpty(logDirectoryPath); }
}
public static String LogFilenamePrefix
{
get { return StringUtil.EmptyOnNull(logFilenamePrefix); }
set { logFilenamePrefix = value; }
}
public static bool IncludeHostInFileMessage { get; set; } = true;
public static bool IncludeUserInFileMessage { get; set; } = true;
//-------------------------------------------------------------------
// Logging.
// TODO - Enhanced logging:
// Include: System.Runtime.CompilerServices
// public static void Error (String message,
// [System.Runtime.CompilerServices.CallerFilePath] string file = "",
// [System.Runtime.CompilerServices.CallerMemberName] string member = "",
// [System.Runtime.CompilerServices.CallerLineNumber] int line = 0)
// {
// Debug.WriteLine("{0}_{1}({2}): {3}", Path.GetFileName(file), member, line, message);
// }
public static bool Error (String message, bool logToFile = false)
{
Log(message, SaMessageLevel.Error, logToFile);
return false;
}
public static bool Warn (String message, bool logToFile = false)
{
Log(message, SaMessageLevel.Warning, logToFile);
return false;
}
public static void Info (String message, bool logToFile = false)
{
Log(message, SaMessageLevel.Info, logToFile);
}
public static void Debug (String message, bool logToFile = false)
{
Log(message, SaMessageLevel.Debug, logToFile);
}
public static void Trace (String message, bool logToFile = false)
{
Log(message, SaMessageLevel.Trace, logToFile);
}
//-------------------------------------------------------------------
/// <summary>
/// Log message.
/// <para></para>
/// <para>Error and warning messages are forced to file unless logToFile is explicitly false</para>
/// </summary>
public static void Log (String message, SaMessageLevel level = SaMessageLevel.Info, bool? logToFile = null)
{
if (level == SaMessageLevel.None) return;
message = message.Trim();
WriteToConsole(FormatMessage(message, level), level);
if (LogToFile(level, logToFile))
WriteToLogFile(FormatMessage(message, level, true), level);
}
private static bool LogToFile (SaMessageLevel level, bool? logToFile)
{
switch (level)
{
case SaMessageLevel.Fatal:
case SaMessageLevel.Exception:
case SaMessageLevel.Error:
case SaMessageLevel.Warning:
return true;
}
return (logToFile != null && logToFile == true);
}
private static String FormatMessage (String message, SaMessageLevel level, bool forFile = false)
{
DateTime now = DateTime.Now;
if (forFile)
return
now.ToString("yyyyMMdd:HHmmss ") + GetLevelAbbrFile(level) + " " +
(IncludeHostInFileMessage ? SacsSystem.ComputerName.PadRight(12) + " " : "") +
(IncludeUserInFileMessage ? SacsSystem.UserName.PadRight(16) + " " : "") +
message;
return now.ToString("yyyyMMdd:HHmmss ") + GetLevelAbbrFile(level) + " " + message;
}
//-------------------------------------------------------------------
/// <summary>Message level abbreviation.</summary>
public static String GetLevelAbbr (SaMessageLevel level)
{
String abbr = SacsSystem.GetMessageLevelCode(level);
return (!StringUtil.IsEmpty(abbr) ? abbr : "UNK");
}
private static String GetLevelAbbrFile (SaMessageLevel level)
{
return GetLevelAbbr(level).PadRight(3, ' ').Substring(0, 3).ToUpper();
}
private static String GetLevelAbbrConsole (SaMessageLevel level)
{
// Visual Studio Output window colour coded snippets.
if (level == SaMessageLevel.Fatal) return "Error [Fatal]";
if (level == SaMessageLevel.Exception) return "Error [Exception]";
if (level == SaMessageLevel.Error) return "Error";
if (level == SaMessageLevel.Warning) return "Warning";
return GetLevelAbbr(level).PadRight(3, ' ').Substring(0, 3).ToUpper();
}
//-------------------------------------------------------------------
public static void WriteBlankLine (bool logToFile = false)
{
WriteToConsole("", SaMessageLevel.Error);
if (logToFile)
WriteToLogFile("", SaMessageLevel.Error);
}
public static void WriteBlankLines(int lineCount, bool logToFile = false)
{
for (int i = 0; i < Math.Min(10, lineCount); i++)
{
WriteToConsole("", SaMessageLevel.Error);
if (logToFile)
WriteToLogFile("", SaMessageLevel.Error);
}
}
//-------------------------------------------------------------------
private static void WriteToConsole (String message, SaMessageLevel level = SaMessageLevel.Info)
{
#if DEBUG
// NB There should be a way to colour error/warning messages.
// Ref. https://stackoverflow.com/questions/2426609/how-do-i-write-color-text-to-the-visual-studio-output-window-from-c
// Console output.
if (level.CompareTo(ConsoleLevel) <= 0)
{
// Use Debug.Write rather than Console.Write.
System.Diagnostics.Debug.WriteLine(message);
//System.Console.WriteLine(message));
}
#endif
}
// Ref. https://stackoverflow.com/questions/6195084/thread-safe-logging-class-implementation
private static readonly object _syncObject = new object();
private static void WriteToLogFile (String message, SaMessageLevel level = SaMessageLevel.Info)
{
// File output - skip if not logging to file or message level is higher than that for logging to file.
if (!LoggingToFile || level.CompareTo(LogFileLevel) > 0) return;
// Only one thread can own the lock, so other threads entering
// this method will wait here until lock is available.
lock (_syncObject)
{
String logFolder = GetLogFolder();
if (FileUtil.ValidateDirectory(logFolder, true))
{
String logPath = logFolder + Path.DirectorySeparatorChar + GetLogFilename();
if (!StringUtil.IsEmpty(message) || File.Exists(logPath))
{
using (StreamWriter file = File.AppendText(logPath))
file.WriteLine(message);
}
}
}
}
//-------------------------------------------------------------------
private static String GetLogFolder ()
{
return logDirectoryPath + Path.DirectorySeparatorChar + DateTime.Today.Format("yyyy") + Path.DirectorySeparatorChar + DateTime.Today.Format("yyyyMM");
}
private static String GetLogFilename ()
{
return FileUtil.FormatFilename(LogFilenamePrefix + (!StringUtil.IsEmpty(LogFilenamePrefix) ? "-" : "") + DateTime.Today.Format("yyyyMMdd") + ".log");
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sarand.Net.Standard.Base.App
{
//=======================================================================
/// <summary>
/// Base application object.
/// </summary>
public class SacsApplication
{
//-------------------------------------------------------------------
// Protected construction.
protected SacsApplication()
{
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.App
{
//=======================================================================
/// <summary>
/// Aids in parsing the command line at application startup.
/// <para>Ref. CCommandLineInfo Class</para>
/// </summary>
public class SacsCommandLineInfo
{
//-------------------------------------------------------------------
// Definitions.
public class Param
{
internal Param (String name, String value)
{
Name = StringUtil.EmptyOnNull(name);
Value = StringUtil.EmptyOnNull(value);
}
public String Name { get; }
public String Value { get; }
}
public enum ShellCommand
{
/// <summary>Indicates that no file name was found on the command line.</summary>
FileNew,
/// <summary>Indicates that a file name was found on the command line and that none of the following flags were found on the command line: /p, /pt, /dde.</summary>
FileOpen,
/// <summary>Indicates that the /p flag was found on the command line.</summary>
FilePrint,
/// <summary>Indicates that the /pt flag was found on the command line.</summary>
FilePrintTo,
/// <summary>Indicates that the /dde flag was found on the command line.</summary>
FileDDE,
/// <summary>Indicates that the /Register or /Regserver flag was found on the command line and the application was asked to register.</summary>
AppRegister,
/// <summary>Indicates that the /Unregister or /Unregserver application was asked to unregister.</summary>
AppUnregister,
/// <summary>Indicates that the application was restarted by the restart manager.</summary>
RestartByRestartManager,
FileNothing = -1
};
//-------------------------------------------------------------------
// Data members.
private readonly List<String> parameters = new List<string>();
//-------------------------------------------------------------------
// Construction.
public SacsCommandLineInfo (string[] args)
{
// Intercept specific arguments.
foreach (String arg in args)
{
if (arg.EqualsCI("/Automation")) RunAutomated = true;
if (arg.EqualsCI("/Embedding")) RunEmbedded = true;
}
// Store raw parameters.
parameters.AddRange(args);
// Extract process definitions.
foreach (String arg in args)
{
if (arg.StartsWith("-") || arg.StartsWith("/"))
{
String processDfnArg = StringUtil.SubstringSafe(arg,1);
if (SacsProcessDefinition.IsValidProcessDefinition(processDfnArg))
{
try
{
processDefinitions.Add(new SacsProcessDefinition(processDfnArg));
}
catch (Exception exc)
{
SaLogger.Warn(GetType().Name + ".CTR Invalid process definition: " + exc.Message, true);
}
}
}
}
}
//-------------------------------------------------------------------
// Properties.
/// <summary>Indicates that the /Automation flag was found on the command line.</summary>
public bool RunAutomated { get; private set; } = false;
/// <summary>Indicates that the /Embedding flag was found on the command line.</summary>
public bool RunEmbedded { get; private set; } = false;
/// <summary>Indicates that the splash screen should be displayed.</summary>
public bool ShowSplash { get; private set; } = false;
//-------------------------------------------------------------------
// Printing properties.
/// <summary>Indicates the file name to be opened or printed; empty if the shell command is New or DDE.<para>The value of the first non-flag parameter on the command line.</para></summary>
public String FileName { get; private set; } = "";
/// <summary>Indicates the printer name if the shell command is Print To; otherwise empty.<para>The value of the second non-flag parameter on the command line.</para></summary>
public String PrinterName { get; private set; } = "";
/// <summary>Indicates the driver name if the shell command is Print To; otherwise empty.<para>The value of the third non-flag parameter on the command line.</para></summary>
public String DriverName { get; private set; } = "";
/// <summary>Indicates the port name if the shell command is Print To; otherwise empty.<para>The value of the fourth non-flag parameter on the command line.</para></summary>
public String PortName { get; private set; } = "";
//-------------------------------------------------------------------
// Restart properties.
/// <summary>
/// Indicates the unique restart identifier for the restart manager if the restart manager restarted the application.
/// <para>The restart identifier is unique for each instance of the application.</para>
/// <para>If the restart manager exits the application and is configured to restart it, the restart manager executes the application from the command line with the restart identifier as an optional parameter.</para>
/// <para>When the restart manager uses the restart identifier, the application can reopen the previously open documents and recover autosaved files.</para>
/// </summary>
public String RestartIdentifier { get; private set; } = "";
//-------------------------------------------------------------------
// Application parameters.
public int ParamCount { get { return parameters.Count; } }
public List<String> Params { get { return new List<string>(parameters); } }
// Redirected from.
public String RedirectedFrom { get; private set; } = "";
//-------------------------------------------------------------------
// Process definitions.
public readonly List<SacsProcessDefinition> processDefinitions = new List<SacsProcessDefinition>();
public List<SacsProcessDefinition> ProcessDefinitions { get { return new List<SacsProcessDefinition>(processDefinitions); } }
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.App
{
//=======================================================================
/// <summary>
/// Process definitions found on command line.
/// <para>Used to automatically run process on start up, and then close application.</para>
/// <para>Command line examples:</para>
/// <para>app /Process:ExportData</para>
/// <para>app /SaProc:ExportData</para>
/// <para>app /Proc:ProcId=ExportData;User=uat</para>
/// <para>app /SaProcess:Proc=ExportData;User=uat</para>
/// </summary>
public class SacsProcessDefinition
{
//-------------------------------------------------------------------
// Data members.
private static List<String> validProcessPrefix = StringListUtil.Split("Proc,Process,SaProc,SaProcess",",");
private static List<String> validProcessNameParam = StringListUtil.Split("Proc,Process,ProcId,ProcessId",",");
//-------------------------------------------------------------------
// Construction.
public SacsProcessDefinition (String commandLineArg)
{
// First field should be a valid process prefix.
if (!IsValidProcessDefinition(commandLineArg)) throw new NotSupportedException("SacsProcessDefinition Invalid process command line arg: '" + commandLineArg + "'");
// A single colon is expected.
String[] fields = commandLineArg.Split(':');
if (fields.Length != 2) throw new NotSupportedException("SacsProcessDefinition Colon expected in command line arg: '" + commandLineArg + "'");
// Extract process params.
String[] processParams = fields[1].Split(';');
foreach (String processParam in processParams)
{
// Each parameter is either delimited as 'name=value' or is interpretted as the process name if no equals.
String[] paramValues = processParam.Split('=');
if (paramValues.Length == 1 || (paramValues.Length == 2 && validProcessNameParam.ContainsCI(paramValues[0])))
{
// Parameter is the process name.
String processName = (paramValues.Length == 1 ? paramValues[0] : paramValues[1]);
if (!StringUtil.IsEmpty(ProcessName)) throw new NotSupportedException("SacsProcessDefinition Multiple process names in command line arg: '" + commandLineArg + "'");
ProcessName = processName;
}
else
{
if (Params.HasValue(paramValues[0])) throw new NotSupportedException("SacsProcessDefinition Duplicate param name '"+ paramValues[0] + "' in command line arg: '" + commandLineArg + "'");
Params.AddValue(paramValues[0], new DataValue(paramValues[1]));
}
}
}
//-------------------------------------------------------------------
// Properties.
public String ProcessName { get; }
public NamedValue.Map Params { get; } = new NamedValue.Map();
//-------------------------------------------------------------------
// Utilities.
public static bool IsValidProcessDefinition (String commandLineArg)
{
if (StringUtil.IsEmpty(commandLineArg)) return false;
foreach (String validPrefix in validProcessPrefix)
{
if (commandLineArg.StartsWithCI(validPrefix + ":"))
return true;
}
return false;
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class SacsProcessDefinitionList
{
//-------------------------------------------------------------------
// Locate process in the list.
public static SacsProcessDefinition GetProcessDefinition (this List<SacsProcessDefinition> values, String name)
{
return values.Find(item => item.ProcessName.EqualsCI(name));
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,19 @@
namespace Sarand.Net.Standard.Base.App
{
//=======================================================================
//[Serializable()]
public class SacsSessionConfig
{
//-------------------------------------------------------------------
// Construction.
protected SacsSessionConfig ()
{
}
//-------------------------------------------------------------------
// Properties.
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sarand.Net.Standard.Base.App
{
//=======================================================================
public class SacsSessionContext
{
//-------------------------------------------------------------------
/// <summary>Hidden construction for deserialisation.</summary>
protected SacsSessionContext()
{
}
// Protected construction - use derived object.
protected SacsSessionContext(ushort sessionIdx)
{
Index = sessionIdx;
}
//-------------------------------------------------------------------
// Properties.
public ushort Index { get; protected set; }
//-------------------------------------------------------------------
// Comparison.
public bool Equals(SacsSessionContext sessionContext)
{
if (sessionContext.Index != Index) return false;
return true;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+201
View File
@@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.App
{
//=======================================================================
public partial class SacsSystem
{
//-------------------------------------------------------------------
// Data members.
private static readonly List<TypeCodeDesc> statusCodes = new List<TypeCodeDesc>();
private static readonly List<TypeCodeDesc> messageLevels = new List<TypeCodeDesc>();
private static readonly List<TypeCodeDesc> interfaceCategories = new List<TypeCodeDesc>();
private static readonly List<TypeCodeDesc> interfaceProcessDirections = new List<TypeCodeDesc>();
//-------------------------------------------------------------------
// Static initialisation.
static SacsSystem ()
{
// Status codes.
statusCodes.Add(SaStatus.Current, "CUR", "Current");
statusCodes.Add(SaStatus.Redundant, "RED", "Redundant");
statusCodes.Add(SaStatus.Superseded, "SUP", "Superseded");
statusCodes.Add(SaStatus.Discontinued, "DSC", "Discontinued");
statusCodes.Add(SaStatus.Provisional, "PRO", "Provisional");
statusCodes.Add(SaStatus.Confirmed, "CON", "Confirmed");
statusCodes.Add(SaStatus.Enabled, "ENA", "Enabled");
statusCodes.Add(SaStatus.Disabled, "DIS", "Disabled");
statusCodes.Add(SaStatus.Created, "CRE", "Created");
statusCodes.Add(SaStatus.Updated, "UPD", "Updated");
statusCodes.Add(SaStatus.Reinstated, "REI", "Reinstated");
statusCodes.Add(SaStatus.Deleted, "DEL", "Deleted");
statusCodes.Add(SaStatus.Required, "REQ", "Required");
statusCodes.Add(SaStatus.Awaiting, "AWA", "Awaiting");
statusCodes.Add(SaStatus.Preparation, "PRE", "Preparation");
statusCodes.Add(SaStatus.Raised, "RAI", "Raised");
statusCodes.Add(SaStatus.InProgress, "WIP", "In progress");
statusCodes.Add(SaStatus.Active, "ATV", "Active");
statusCodes.Add(SaStatus.Inactive, "INA", "Inactive");
statusCodes.Add(SaStatus.OnHold, "HLD", "On Hold");
statusCodes.Add(SaStatus.Imported, "IMP", "Imported");
statusCodes.Add(SaStatus.Exported, "EXP", "Exported");
statusCodes.Add(SaStatus.Open, "OPE", "Open");
statusCodes.Add(SaStatus.Closed, "CLO", "Closed");
statusCodes.Add(SaStatus.Completed, "COM", "Completed");
statusCodes.Add(SaStatus.Invoiced, "INV", "Invoiced");
statusCodes.Add(SaStatus.Cancelled, "CAN", "Cancelled");
statusCodes.Add(SaStatus.Successful, "SUC", "Successful");
statusCodes.Add(SaStatus.Failed, "FAI", "Failed");
statusCodes.Add(SaStatus.Actioned, "ACT", "Actioned");
statusCodes.Add(SaStatus.Authorised, "AUT", "Authorised");
statusCodes.Add(SaStatus.Accepted, "ACC", "Accepted");
statusCodes.Add(SaStatus.Rejected, "REJ", "Rejected");
statusCodes.Add(SaStatus.Submitted, "SUB", "Submitted");
statusCodes.Add(SaStatus.Queried, "QUE", "Queried");
statusCodes.Add(SaStatus.Granted, "GRA", "Granted");
statusCodes.Add(SaStatus.Denied, "DEN", "Denied");
statusCodes.Add(SaStatus.Logged, "LOG", "Logged");
// Message levels.
messageLevels.Add(SaMessageLevel.Fatal, "FAT", "Fatal");
messageLevels.Add(SaMessageLevel.Exception, "EXC", "Exception");
messageLevels.Add(SaMessageLevel.Error, "ERR", "Error");
messageLevels.Add(SaMessageLevel.Warning, "WRN", "Warning");
messageLevels.Add(SaMessageLevel.Info, "INF", "Info");
messageLevels.Add(SaMessageLevel.Debug, "DBG", "Debug");
messageLevels.Add(SaMessageLevel.Trace, "TRC", "Trace");
// Interface categories.
interfaceCategories.Add(SaInterfaceCategory.Website, "WEB", "Website");
interfaceCategories.Add(SaInterfaceCategory.Accounts, "ACC", "Accounts");
interfaceCategories.Add(SaInterfaceCategory.System, "SYS", "System");
interfaceCategories.Add(SaInterfaceCategory.File, "FIL", "File");
// Interface process directions.
interfaceProcessDirections.Add(SaInterfaceProcessDirection.Inbound, "INB", "Inbound");
interfaceProcessDirections.Add(SaInterfaceProcessDirection.Outbound, "OUT", "Outbound");
interfaceProcessDirections.Add(SaInterfaceProcessDirection.Both, "BTH", "In/Out");
}
//-------------------------------------------------------------------
// Protected Construction.
protected SacsSystem () : base()
{
}
//-------------------------------------------------------------------
// E n v i r o n m e n t
//-------------------------------------------------------------------
public enum AppMode { Debug, Release, }
/// <summary>Debug or Release.</summary>
public static AppMode Mode { get; } =
#if !DEBUG
AppMode.Release;
#else
AppMode.Debug;
#endif
public static bool DebugMode { get { return Mode == AppMode.Debug; } }
public static bool ReleaseMode { get { return Mode == AppMode.Release; } }
public static bool IsDevHost ()
{
return ComputerName.StartsWithCI("sarand");
}
//-------------------------------------------------------------------
/// <summary>
/// Computer name
/// <para>Windows: System.Environment.MachineName</para>
/// <para>Web: HttpContext.Current.Server.MachineName</para>
/// <para>FQDN: System.Net.Dns.GetHostName()</para>
/// </summary>
public static String ComputerName { get; protected set; } = String.Empty;
//-------------------------------------------------------------------
/// <summary>To be populated by application<para>Windows: Environment.UserName</para></summary>
public static String UserName { get; protected set; } = String.Empty;
//-------------------------------------------------------------------
/// <summary>
/// Application Path.
/// <para>Windows: System.Reflection.Assembly.GetEntryAssembly().Location;</para>
/// </summary>
public static String ApplicationPath { get; protected set; } = String.Empty;
//-------------------------------------------------------------------
/// <summary>
/// Application folder.
/// <para>Windows: System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);</para>
/// </summary>
public static String ApplicationFolder { get; protected set; } = String.Empty;
//-------------------------------------------------------------------
/// <summary>
/// DateTime when the executing assembly was built.
/// </summary>
public static DateTime AssemblyBuildAt { get; set; } = DateTime.Now;
//-------------------------------------------------------------------
// C o d e L i s t s
//-------------------------------------------------------------------
public static List<TypeCodeDesc> GetCodesOfType (SaSysCodeType codeType)
{
switch (codeType)
{
case SaSysCodeType.MessageLevel:
return new List<TypeCodeDesc>(messageLevels);
case SaSysCodeType.InterfaceCategory:
return new List<TypeCodeDesc>(interfaceCategories);
case SaSysCodeType.InterfaceProcessDirection:
return new List<TypeCodeDesc>(interfaceProcessDirections);
}
return null;
}
//-------------------------------------------------------------------
// Status utilities.
public static SaStatus GetStatusForCode (String status)
{
return (SaStatus)statusCodes.GetTypeForCode(status,(int)SaStatus.Unknown);
}
public static String GetStatusCode (SaStatus status)
{
return statusCodes.GetCodeForType(status, status.ToString());
}
public static String GetStatusDesc (SaStatus status)
{
return statusCodes.GetDescForType(status, status.ToString());
}
public static String GetStatusDescForCode (String status)
{
return statusCodes.GetDescForCode(status, status);
}
public static List<TypeCodeDesc> GetStatusCodeList (List<SaStatus> statuses)
{
List <TypeCodeDesc> statusCodeList = new List<TypeCodeDesc>();
foreach (var status in statuses)
{
TypeCodeDesc statusCode = statusCodes.Find(x => x.Type==(int)status);
if (statusCode != null)
statusCodeList.Add(statusCode);
}
return statusCodeList;
}
//-------------------------------------------------------------------
// Message level utilities.
public static SaMessageLevel GetMessageLevelFromCode (String code)
{
return (SaMessageLevel)messageLevels.GetTypeForCode(code);
}
public static String GetMessageLevelCode (SaMessageLevel level)
{
return messageLevels.GetCodeForType(level);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+75
View File
@@ -0,0 +1,75 @@
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sarand.Net.Standard.Base.App
{
//=======================================================================
public class SacsVersion
{
//-------------------------------------------------------------------
// Construction.
public SacsVersion (String name, String version, DateTime buildAt)
{
Name = name;
Version = version;
BuildAt = buildAt;
try
{
String[] versionInts = version.Split(new char[] { ',' }, 4);
if (versionInts.Length > 0) Ver1 = (UInt16)StringUtil.ToShort(versionInts[0]);
if (versionInts.Length > 1) Ver2 = (UInt16)StringUtil.ToShort(versionInts[1]);
if (versionInts.Length > 2) Ver3 = (UInt16)StringUtil.ToShort(versionInts[2]);
if (versionInts.Length > 3) Ver4 = (UInt16)StringUtil.ToShort(versionInts[3]);
}
catch (Exception exc)
{
SaLogger.Warn(GetType().Name + " " + exc.GetType().Name + " on parsing version: " + exc.Message);
}
VersionInt = Ver1 << 48 + Ver2 << 32 + Ver3 << 16 + Ver4;
}
//-------------------------------------------------------------------
// Properties.
public String Name { get; }
public String Version { get; }
public Int64 VersionInt { get; }
public UInt16 Ver1 { get; }
public UInt16 Ver2 { get; }
public UInt16 Ver3 { get; }
public UInt16 Ver4 { get; }
public DateTime BuildAt { get; }
//-------------------------------------------------------------------
// Comparison.
public override bool Equals (object obj)
{
if (obj is SacsVersion) return Equals(obj as SacsVersion);
return base.Equals(obj);
}
public override int GetHashCode ()
{
var hashCode = 2121240903;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Version);
hashCode = hashCode * -1521134295 + VersionInt.GetHashCode();
return hashCode;
}
public bool Equals (SacsVersion version)
{
if (VersionInt == version.VersionInt) return true;
return false;
}
public int CompareTo (SacsVersion version, bool includeName = false)
{
int versionCompare = VersionInt.CompareTo(version.VersionInt);
if (!includeName || versionCompare != 0) return versionCompare;
return Name.CompareTo(version.Name);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+109
View File
@@ -0,0 +1,109 @@
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
namespace Sarand.Net.Standard.Base.Bus
{
//=======================================================================
[Serializable()]
public class ItemId
{
//-------------------------------------------------------------------
// Data members.
public enum ItemIdType { Unknown, SysId, String }
private ItemIdType idType = ItemIdType.Unknown;
//-------------------------------------------------------------------
// Construction.
public ItemId() { }
public ItemId(short itemType, SysId id, String caption = "")
{
Type = itemType;
Id = SysId.InvalidOnNull(id);
idType = ItemIdType.SysId;
Caption = StringUtil.EmptyOnNull(caption);
}
public ItemId(short itemType, String stringId, String caption = "")
{
Type = itemType;
StringId = StringUtil.EmptyOnNull(stringId);
idType = ItemIdType.String;
Caption = StringUtil.EmptyOnNull(caption);
}
//-------------------------------------------------------------------
// Properties.
[XmlAttribute]
public short Type { get; private set; } = 0;
[XmlAttribute]
public SysId Id { get; private set; } = new SysId();
public String StringId { get; private set; } = "";
public ItemIdType IdType { get { return idType; } }
// Caption - display text to identify this item in a list.
public String Caption { get; set; } = "";
// Data value - additional information for this item.
public String Data { get; set; } = "";
//-------------------------------------------------------------------
// Utilities.
public static bool IsValid(ItemId itemId)
{
if (itemId == null) return false;
if (itemId.Type < 1) return false;
if (itemId.idType == ItemIdType.SysId && !SysId.IsValid(itemId.Id)) return false;
if (itemId.idType == ItemIdType.String && StringUtil.IsEmpty(itemId.StringId)) return false;
return true;
}
//-------------------------------------------------------------------
// Comparison.
public override bool Equals(object obj)
{
if (obj == null) return false;
return Equals(obj as ItemId);
}
public bool Equals(ItemId itemId)
{
if (itemId == null) return false;
if (this.Type != itemId.Type) return false;
if (this.idType != itemId.idType) return false;
if (idType == ItemIdType.SysId && SysId.ToInt(Id) != SysId.ToInt(itemId.Id)) return false;
if (idType == ItemIdType.String && !StringId.EqualsCI(itemId.StringId)) return false;
return true;
}
public override int GetHashCode()
{
return SysId.ToInt(Id);
}
//-------------------------------------------------------------------
// Conversion.
public override String ToString()
{
return ToString(this);
}
// Null-safe static versions.
public static String ToString(ItemId itemId)
{
return ToString(itemId, String.Empty);
}
public static String ToString(ItemId itemId, String invalidValue)
{
if (itemId == null || !IsValid(itemId)) return invalidValue;
return itemId.Type.ToString() + (SysId.IsValid(itemId.Id) ? ":" + SysId.ToString(itemId.Id) : "");
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class ItemIdList
{
public static void Add(this List<ItemId> values, short type, SysId id)
{
values.Add(new ItemId(type, id));
}
}
//=======================================================================
}
@@ -0,0 +1,105 @@
using System;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.Bus
{
//=======================================================================
public abstract class SaErrorStateItem : SacsDisposable, ISacsStateItem
{
//-------------------------------------------------------------------
// Definitions.
public enum ErrorMessage
{
// Item validation.
ItemNotSpecified,
ValiditemId,
InvalidItemId,
// Common properties.
InvalidStatus,
}
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Static initialisation.
static SaErrorStateItem ()
{
}
//-------------------------------------------------------------------
// Construction.
protected SaErrorStateItem ()
{
}
protected override void Dispose (bool disposing)
{
if (disposing)
{
}
}
//-------------------------------------------------------------------
// Properties.
//-------------------------------------------------------------------
// E r r o r S t a t e
//-------------------------------------------------------------------
private SacsErrorState errorState = null;
public bool HasErrorState ()
{
return errorState != null;
}
public SacsErrorState ErrorState
{
get { return errorState; }
}
public String ErrorStateMessage
{
get { return (errorState != null ? errorState.Message : String.Empty); }
}
/// <summary>
/// Record the error state message.
/// <para>Blank message does not set the error state - leaves previous state intact.</para>
/// <para>Return value is fale unless the message is blank.</para>
/// </summary>
public bool SetErrorState (String message)
{
if (StringUtil.IsEmpty(message)) return true;
this.errorState = new SacsErrorState(message);
return false;
}
/// <summary>Clear the error state.</summary>
public void ClearErrorState ()
{
errorState = null;
}
//-------------------------------------------------------------------
// Error state population.
protected bool SetErrorState (ErrorMessage errorMessage)
{
String message = errorMessage.ToString();
switch (errorMessage)
{
case ErrorMessage.ItemNotSpecified:
message = "Item not specified.";
break;
case ErrorMessage.ValiditemId:
message = "Item has a valid id.";
break;
case ErrorMessage.InvalidItemId:
message = "Invalid item id.";
break;
case ErrorMessage.InvalidStatus:
message = "Invalid status.";
break;
}
return SetErrorState(message);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,466 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.Bus
{
//=======================================================================
public class UpdateStamp
{
private DateTime on = DateTime.Now;
private SysId by = new SysId();
private String host = String.Empty;
public DateTime On
{
get { return on; }
set { on = value; }
}
public SysId By
{
get { return by; }
set { by = SysId.InvalidOnNull(value); }
}
public String Host
{
get { return host; }
set { host = StringUtil.EmptyOnNull(value); }
}
}
//=======================================================================
public class SacsHierarchyItem : SaErrorStateItem
{
//-------------------------------------------------------------------
// Definitions.
public class Data
{
//---------------------------------------------------------------
// Common properties.
private SysId id = new SysId();
private String notes = String.Empty;
/// <summary>Item identifier.</summary>
public SysId Id
{
get { return SysId.InvalidOnNull(id); }
set
{
CheckChanged(id, value);
id = SysId.InvalidOnNull(value);
}
}
/// <summary>Item notes.</summary>
public String Notes
{
get { return StringUtil.EmptyOnNull(notes); }
set
{
CheckChanged(notes, value);
notes = StringUtil.EmptyOnNull(value);
}
}
//---------------------------------------------------------------
// Flags.
private UInt16 flags = 0;
/// <summary>Item flags.<para>Up to 16 flags supported.</para></summary>
public UInt16 Flags
{
get { return flags; }
set
{
CheckChanged(flags, value);
flags = value;
}
}
/// <summary>Get flag value - position 1 - 16</summary>
public bool GetFlag (Byte flagPos)
{
return SacsHierarchyItem.GetFlag(Flags, flagPos);
}
/// <summary>Set flag value - position 1 - 16</summary>
public void SetFlag (Byte flagPos, bool value)
{
CheckChanged(GetFlag(flagPos), value);
Flags = SacsHierarchyItem.SetFlag(Flags, flagPos, value);
}
//---------------------------------------------------------------
// Status.
private SaStatus status = SaStatus.Unknown;
/// <summary>Item status.</summary>
public SaStatus Status
{
get { return status; }
set
{
CheckChangedEnum(status, value);
status = value;
}
}
public String StatusCode
{
get { return SacsSystem.GetStatusCode(Status); }
set { Status = SacsSystem.GetStatusForCode(value); }
}
public String StatusDesc
{
get { return SacsSystem.GetStatusDesc(Status); }
}
public bool IsStatusCurrent { get { return Status == SaStatus.Current; } }
public bool IsStatusActive { get { return Status == SaStatus.Active; } }
public bool IsStatusEnabled { get { return Status == SaStatus.Enabled; } }
public bool IsStatusCompleted { get { return Status == SaStatus.Completed; } }
public bool IsStatusCancelled { get { return Status == SaStatus.Cancelled; } }
public bool IsStatusDeleted { get { return Status == SaStatus.Deleted; } }
//---------------------------------------------------------------
// Update stamps.
public UpdateStamp CreateStamp { get; protected set; } = new UpdateStamp();
public UpdateStamp UpdateStamp { get; protected set; } = new UpdateStamp();
//---------------------------------------------------------------
// Population.
/// <summary>Copy common properties to this object.</summary>
protected void PopulateFrom (Data sourceData)
{
Id = sourceData.Id;
Notes = sourceData.Notes;
Status = sourceData.Status;
Flags = sourceData.Flags;
CreateStamp = sourceData.CreateStamp;
UpdateStamp = sourceData.UpdateStamp;
}
/// <summary>Copy comon properties to the target object.</summary>
internal void PopulateTo (Data targetData)
{
targetData.PopulateFrom(this);
}
//---------------------------------------------------------------
// Property change tracking.
private bool changed = false;
/// <summary>Flag to indicate whether any property has been modified.</summary>
public bool Changed
{
get { return changed; }
set
{
if (changed == value) return;
changed = value;
}
}
/// <summary>Clear 'Changed' flag.</summary>
public void AcceptChanges ()
{
Changed = false;
}
//---------------------------------------------------------------
// Compare old and new property values and set 'Changed' if different.
protected void CheckChanged (SysId oldValue, SysId newValue)
{
if (Changed) return;
// Compare by numeric value so that invalid values are equal.
Changed = SysId.ToInt(oldValue) != SysId.ToInt(newValue);
}
protected void CheckChanged (ItemId oldValue, ItemId newValue)
{
if (Changed) return;
Changed = (oldValue.Type != newValue.Type) || (!oldValue.Id.Equals(newValue.Id));
}
protected void CheckChanged (String oldValue, String newValue)
{
if (Changed) return;
Changed = !StringUtil.EmptyOnNull(oldValue).Equals(StringUtil.EmptyOnNull(newValue));
}
protected void CheckChanged (bool oldValue, bool newValue)
{
if (Changed) return;
Changed = (oldValue != newValue);
}
protected void CheckChangedEnum<T>(T oldValue, T newValue) where T : struct, IConvertible
{
if (!(oldValue is Enum) || !(newValue is Enum))
throw new ArgumentException(GetType().Name+ ".CheckChangedEnum<T> Parameters must be enumerated types. Type supplied: " + oldValue.GetType().Name + ", " + newValue.GetType().Name);
if (Changed) return;
Changed = ((int)(oldValue as IConvertible) != (int)(newValue as IConvertible));
}
protected void CheckChanged (int oldValue, int newValue)
{
if (Changed) return;
Changed = (oldValue != newValue);
}
protected void CheckChanged (double oldValue, double newValue)
{
if (Changed) return;
Changed = (oldValue != newValue);
}
protected void CheckChanged (decimal oldValue, decimal newValue)
{
if (Changed) return;
Changed = (oldValue != newValue);
}
protected void CheckChanged (DateTime oldValue, DateTime newValue)
{
if (Changed) return;
Changed = !oldValue.Equals(newValue);
}
protected void CheckChanged (List<String> oldValues, List<String> newValues)
{
if (Changed) return;
Changed = !oldValues.ContentEquals(newValues);
}
protected void CheckChanged<T> (List<T> oldValues, List<T> newValues)
{
if (Changed) return;
if (oldValues.Count() != newValues.Count()) Changed = true;
else
{
for (int idx = 0; idx < oldValues.Count(); idx++)
{
if (!oldValues[idx].Equals(newValues[idx]))
{
Changed = true;
return;
}
}
}
}
protected void CheckChanged (byte[] oldValue, byte[] newValue)
{
if (Changed) return;
if ((oldValue == null) != (newValue != null)) Changed = true;
else if (oldValue.Length != newValue.Length) Changed = true;
if (oldValue != null && newValue != null)
Changed = !oldValue.SequenceEqual(newValue);
}
//---------------------------------------------------------------
}
//-------------------------------------------------------------------
// Property validation.
public enum ValidValue
{
/// <summary>Property is valid (or has not been validate).</summary>
Valid,
/// <summary>Property is invalid.</summary>
Invalid,
/// <summary>Property is to be ignored during validation.</summary>
Ignore,
}
public class ErrField
{
// Identifies the field that has invalid data.
public ValidValue Id { get; set; } = ValidValue.Valid;
public ValidValue Notes { get; set; } = ValidValue.Valid;
public ValidValue Status { get; set; } = ValidValue.Valid;
public ValidValue Flags { get; set; } = ValidValue.Valid;
}
//-------------------------------------------------------------------
// Data members.
// Data object holds object business properties.
// Starts as internal class, replaced on construction by object specific class.
private Data data = new Data();
//-------------------------------------------------------------------
// Construction.
protected SacsHierarchyItem ()
{
}
protected SacsHierarchyItem (SysId id)
{
data.Id = id;
}
//-------------------------------------------------------------------
// Disposal.
protected override void Dispose (bool disposing)
{
if (disposing)
{
}
}
//-------------------------------------------------------------------
// Properties.
public virtual bool ItemHasId { get { return true; } }
public SysId Id
{
get
{
if (!ItemHasId) throw new NotSupportedException(GetType().Name + ".Id Item has no Id property");
return data.Id;
}
set
{
if (!ItemHasId && SysId.IsValid(data.Id)) throw new NotSupportedException(GetType().Name + ".Id Item has no Id property");
if (SysId.IsValid(data.Id) && (!SysId.IsValid(value) || !data.Id.Equals(value)))
throw new NotSupportedException("SacsHierarchyItem.Id.set Id already has a valid value");
data.Id = value;
}
}
public String Notes
{
get { return StringUtil.EmptyOnNull(data.Notes); }
set { data.Notes = value; }
}
//-------------------------------------------------------------------
// Status.
public SaStatus Status
{
get { return data.Status; }
set { data.Status = value; }
}
public String StatusCode
{
get { return data.StatusCode; }
set { data.StatusCode = value; }
}
public String StatusDesc
{
get { return data.StatusDesc; }
}
public bool IsStatusCurrent { get { return data.IsStatusCurrent; } }
public bool IsStatusActive { get { return data.IsStatusActive; } }
public bool IsStatusEnabled { get { return data.IsStatusEnabled; } }
public bool IsStatusCompleted { get { return data.IsStatusCompleted; } }
public bool IsStatusCancelled { get { return data.IsStatusCancelled; } }
public bool IsStatusDeleted { get { return data.IsStatusDeleted; } }
//-------------------------------------------------------------------
// Flags.
public UInt16 Flags
{
get { return data.Flags; }
protected set { data.Flags = value; }
}
/// <summary>Flags are positioned from 1, up to 16 allowed.</summary>
public static bool GetFlag (UInt16 flags, Byte flagPos)
{
bool flag = false;
if (flagPos > 0 && flagPos <= 16)
flag = (((flags >> (flagPos - 1)) & 0x0001) == 1 ? true : false);
return flag;
}
/// <summary>Flags are positioned from 1, up to 16 allowed.</summary>
public bool GetFlag (Byte flagPos)
{
return GetFlag(data.Flags, flagPos);
}
/// <summary>Set flag value - position 1 - 16</summary>
public static UInt16 SetFlag (UInt16 flags, Byte flagPos, bool value)
{
if (flagPos > 0 && flagPos <= 16)
{
UInt16 mask = (UInt16)(1 << (flagPos - 1));
if (value)
flags |= mask;
else
flags &= (UInt16)(~mask);
}
return flags;
}
/// <summary>Set flag value - position 1 - 16</summary>
public void SetFlag (Byte flagPos, bool value)
{
data.Flags = SetFlag(data.Flags, flagPos, value);
}
//-------------------------------------------------------------------
// Update stamps.
public UpdateStamp CreateStamp { get { return data.CreateStamp; } }
public void SetCreateStamp (DateTime on, SysId by, String host)
{
CreateStamp.On = on;
CreateStamp.By = by;
CreateStamp.Host = host;
}
public UpdateStamp UpdateStamp { get { return data.UpdateStamp; } }
public void SetUpdateStamp (DateTime on, SysId by, String host)
{
UpdateStamp.On = on;
UpdateStamp.By = by;
UpdateStamp.Host = host;
}
//-------------------------------------------------------------------
// Populate data item - only once on construction.
protected void SetData (Data data)
{
String fn = GetType().Name + ".SetData";
// Can only be replaced if currently internal class.
if (this.data.GetType() != typeof(Data))
throw new NotSupportedException(fn + " Data object has already been specified");
// If Id has already been specified (on construction) and supplied Id is not valid, set it to the existing value.
if (ItemHasId && SysId.IsValid(Id) && !SysId.IsValid(data.Id)) data.Id = Id;
// Properties that cannot be changed if previously specified.
if (ItemHasId && SysId.IsValid(Id) && SysId.ToInt(Id) != SysId.ToInt(data.Id))
throw new NotSupportedException(fn + " Id cannot be changed");
this.data.PopulateTo(data);
this.data = data;
}
//-------------------------------------------------------------------
// Property change tracking.
/// <summary>Flag to indicate whether any property has been modified.</summary>
public bool Changed
{
get { return data.Changed; }
set
{
if (data.Changed == value) return;
data.Changed = value;
}
}
/// <summary>Clear the 'Changed' flag to indicate that no changes have been made.</summary>
public void AcceptChanges ()
{
Changed = false;
}
//-------------------------------------------------------------------
// Format value for display.
public static String FormatForDisplay (String value, int maxLength = 0)
{
if (StringUtil.IsEmpty(value)) return String.Empty;
// Replace non-display characters.
value = value.Replace("\t", " ").Replace("\r", " ").Replace("\n", " ");
// Remove leading, trailing and multiple internal spaces.
value = value.Trim().Replace(" +", " ");
// Restrict to maximum length.
if (maxLength > 0 && value.Length > maxLength)
{
value = value.Left(maxLength);
if (maxLength > 20) value += "...";
}
return value;
}
//-------------------------------------------------------------------
// Parameters.
public class Params
{
// Construction.
protected Params ()
{
}
/// <summary>Primary table alias to use if different from default.</summary>
public String TableAlias { get; set; }
/// <summary>Flag to indicate whether default filter ('1=1') is included if no other filter.</summary>
public bool IncludeDefaultFilter { get; set; } = true;
/// <summary>Results list paging.</summary>
public DbQuery.Paging ResultsPaging { get; } = new DbQuery.Paging();
/// <summary>Sort columns.</summary>
public List<ColumnDfn> SortColumns { get; } = new List<ColumnDfn>();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+63
View File
@@ -0,0 +1,63 @@
using System;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.Bus
{
//=======================================================================
public abstract class SacsItemMgr : SaErrorStateItem
{
//-------------------------------------------------------------------
// Definitions.
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Static initialisation.
static SacsItemMgr ()
{
}
//-------------------------------------------------------------------
// Construction.
protected SacsItemMgr ()
{
}
//-------------------------------------------------------------------
// Disposal.
protected override void Dispose (bool disposing)
{
if (disposing)
{
}
base.Dispose(disposing);
}
//-------------------------------------------------------------------
// Properties.
//-------------------------------------------------------------------
// V a l i d a t i o n
//-------------------------------------------------------------------
// String attribute validation (mandatory and maximum length.
// If the value fails the validation, an exception is set:
protected bool IsValidValue (String value, String attributeName, long maxLen, bool mandatory = false, bool fixedLen = false)
{
// Check for mandatory values:
if (mandatory && StringUtil.IsEmpty(value))
return SetErrorState(attributeName + " is blank");
// Check that value does not exceed the maximum number of characters:
if (maxLen > 0 && StringUtil.GetLength(value) > maxLen)
return SetErrorState(attributeName + " must not exceed " + maxLen.ToString() + " character" + ((maxLen > 1) ? "s" : ""));
// Check that fixed length values are the specified length:
if (fixedLen && maxLen > 0 && (mandatory || !value.IsEmpty()) && StringUtil.GetLength(value) != maxLen)
return SetErrorState(attributeName + " must be " + maxLen.ToString() + " character" + ((maxLen > 1) ? "s" : "") + " in length");
return true;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+176
View File
@@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.Bus
{
//=======================================================================
//[Serializable()]
public class SysId
{
//-------------------------------------------------------------------
// Data members.
private int id = 0;
//-------------------------------------------------------------------
// Construction.
public SysId () { }
public SysId (int id)
{
this.id = id;
}
//-------------------------------------------------------------------
// Properties - for serialisation.
//[XmlAttribute]
public int Id
{
get { return id; }
set
{
if (id != 0 && value != 0 && value != id) throw new NotSupportedException("SysId value cannot be changed once valid");
id = value;
}
}
//-------------------------------------------------------------------
// Utilities.
public static bool IsValid (SysId sysId)
{
return (sysId != null && sysId.id != 0);
}
public static SysId InvalidOnNull (SysId sysId)
{
return (sysId != null ? sysId : new SysId());
}
//-------------------------------------------------------------------
// Comparison.
public override bool Equals (object obj)
{
if (obj == null) return false;
return Equals(obj as SysId);
}
public bool Equals (SysId sysId)
{
if (sysId == null) return false;
// False if either value invalid.
if (id == 0) return false;
return ToInt().Equals(InvalidOnNull(sysId).ToInt());
}
public override int GetHashCode ()
{
return id;
}
//-------------------------------------------------------------------
// Conversion.
public int ToInt ()
{
return ToInt(this);
}
public override String ToString ()
{
return ToString(this);
}
public String ToString (String invalidValue)
{
return ToString(this, invalidValue);
}
// Null-safe static versions.
public static int ToInt (SysId sysId)
{
return (IsValid(sysId) ? sysId.id : 0);
}
public static String ToString (SysId sysId)
{
return ToString(sysId, String.Empty);
}
public static String ToString (SysId sysId, String invalidValue)
{
return (IsValid(sysId) ? sysId.id.ToString() : invalidValue);
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class SysIdList
{
//-------------------------------------------------------------------
// List contents.
public static int GetIdCount (this List<SysId> values, bool ignoreInvalid = true, bool ignoreDuplicates = true)
{
if (ignoreDuplicates)
{
values = values.Select(id => id).Distinct().ToList();
// SysId.Equals treats invalid ids as inequal so 'Distinct' doesn't remove them.
if (values.FindIndex(id => !SysId.IsValid(id)) >= 0)
{
// Remove all invalid entries and put one back to correct the count.
values.RemoveAll(id => !SysId.IsValid(id));
values.Add(new SysId());
}
}
return values.Count(id => !ignoreInvalid || SysId.IsValid(id));
}
public static bool HasValidIds (this List<SysId> values)
{
return (values.Find(id => SysId.IsValid(id)) != null);
}
public static bool Equals (this List<SysId> values, List<SysId> values2)
{
return values.OrderBy(id => id.Id).SequenceEqual(values2.OrderBy(id => id.Id));
}
//-------------------------------------------------------------------
// Populate list.
public static void AddId (this List<SysId> values, SysId sysId, bool ignoreInvalid = true, bool ignoreDuplicates = true)
{
if (ignoreInvalid && !SysId.IsValid(sysId)) return;
if (ignoreDuplicates && values.Contains(sysId)) return;
values.Add(sysId);
}
public static void AddIds (this List<SysId> values, List<SysId> sysIds, bool ignoreInvalid = true, bool ignoreDuplicates = true)
{
foreach (SysId id in sysIds)
values.AddId(id, ignoreInvalid, ignoreDuplicates);
}
//-------------------------------------------------------------------
// Extract from list.
/// <summary>Remove specified number of entries from the list and return them.</summary>
/// <returns></returns>
public static List<SysId> ExtractIds (this List<SysId> values, int numberToRemove)
{
List<SysId> ids = new List<SysId>();
while (values.Count > 0 && ids.Count < numberToRemove)
{
SysId id = values.ElementAt(0);
values.RemoveAt(0);
ids.Add(id);
}
return ids;
}
//-------------------------------------------------------------------
/// <summary>
/// Combine items in list as a delimited string.
/// </summary>
/// <param name="values"></param>
/// <param name="delimiter"></param>
/// <param name="ignoreInvalid"></param>
/// <returns></returns>
public static String Concat (this List<SysId> values, String delimiter, bool ignoreInvalid = true)
{
List<String> ids = new List<string>();
foreach (SysId id in values.FindAll(id => !ignoreInvalid || SysId.IsValid(id)))
ids.Add(id.ToString());
return ids.Concat(delimiter);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+73
View File
@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.Bus
{
//=======================================================================
//[Serializable()]
public class SysIdString
{
//-------------------------------------------------------------------
// Construction.
public SysIdString (SysId id, String value)
{
Id = id;
Value = value;
}
//-------------------------------------------------------------------
// Properties - for serialisation.
//[XmlAttribute]
public SysId Id { get; private set; } = new SysId();
public String Value { get; private set; } = String.Empty;
//-------------------------------------------------------------------
// Utilities.
//-------------------------------------------------------------------
// Comparison.
public override bool Equals (object obj)
{
if (obj == null) return false;
if (!(obj is SysIdString)) return false;
return Equals(obj as SysIdString);
}
public bool Equals (SysIdString value)
{
if (value == null) return false;
// False if either Id value invalid.
if (SysId.IsValid(this.Id) != SysId.IsValid(value.Id)) return false;
return (SysId.ToInt(Id).Equals(SysId.ToInt(value.Id)) && Value.Equals(value.Value));
}
public override int GetHashCode ()
{
return SysId.ToInt(Id);
}
//-------------------------------------------------------------------
// Conversion.
//-------------------------------------------------------------------
}
//=======================================================================
public static class SysIdStringList
{
public static void Add(this List<SysIdString> values, SysId id, String value)
{
values.Add(new SysIdString(id, value));
}
public static String GetValueForId(this List<SysIdString> values, SysId id)
{
if (SysId.IsValid(id))
{
foreach (SysIdString value in values)
if (value.Id.Equals(id))
return value.Value;
}
return String.Empty;
}
}
//=======================================================================
}
+260
View File
@@ -0,0 +1,260 @@
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.CodeTypes
{
//=======================================================================
public class SaCode : SacsHierarchyItem
{
//-------------------------------------------------------------------
// Columns.
public enum Column
{
Pos = 0,
// Code type properties.
CodeTypeId,
CodeType_Code,
CodeType_Desc,
// Code properties.
Id,
Code,
Description,
Sequence,
DataValue,
Flag1_Y,
Flag1_Tick,
Flag2_Y,
Flag2_Tick,
// Common properties.
Notes,
Flags,
Status,
Status_Desc,
Status_Ind,
CreateOn_Date,
UpdateOn_Date,
}
//-------------------------------------------------------------------
/// <summary>Properties other than common.</summary>
public class DataProperties
{
public SysId CodeTypeId { get; set; } = new SysId();
public string Code { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public ushort Sequence { get; set; } = 0;
public string DataValue { get; set; } = string.Empty;
}
//-------------------------------------------------------------------
// Data.
public new class Data : SacsHierarchyItem.Data
{
// Data members.
private readonly DataProperties data = new DataProperties();
// Construction.
public Data ()
{
Status = DefaultStatus;
}
// Properties.
public SysId CodeTypeId
{
get { return data.CodeTypeId; }
set { CheckChanged(data.CodeTypeId, value); data.CodeTypeId = value; }
}
public string Code
{
get { return data.Code; }
set { CheckChanged(data.Code, value); data.Code = value; }
}
public string Description
{
get { return data.Description; }
set { CheckChanged(data.Description, value); data.Description = value; }
}
public ushort Sequence
{
get { return data.Sequence; }
set { CheckChanged(data.Sequence, value); data.Sequence = value; }
}
public string DataValue
{
get { return data.DataValue; }
set { CheckChanged(data.DataValue, value); data.DataValue = value; }
}
// Population.
internal void PopulateFrom (Data data)
{
CodeTypeId = data.CodeTypeId;
Code = data.Code;
Description = data.Description;
Sequence = data.Sequence;
DataValue = data.DataValue;
base.PopulateFrom(data);
}
public Data Clone ()
{
Data data = new Data();
data.PopulateFrom(this);
return data;
}
}
//-------------------------------------------------------------------
// Error field - identify properties with validation error.
public new class ErrField : SacsHierarchyItem.ErrField
{
// Identifies the field that has invalid data.
public ValidValue CodeType { get; set; } = ValidValue.Valid;
public ValidValue Code { get; set; } = ValidValue.Valid;
public ValidValue Description { get; set; } = ValidValue.Valid;
public ValidValue Sequence { get; set; } = ValidValue.Valid;
public ValidValue DataValue { get; set; } = ValidValue.Valid;
}
//-------------------------------------------------------------------
// Data members.
private readonly Data data = new Data();
// Flags.
//-------------------------------------------------------------------
// Construction.
public SaCode (SysId id, SysId codeTypeId, string code) :
base(id)
{
base.SetData(data);
CodeTypeId = codeTypeId;
Code = code;
Status = DefaultStatus;
}
//-------------------------------------------------------------------
// Statuses.
private static readonly List<SaStatus> validStatuses = new List<SaStatus>()
{
SaStatus.Current,
SaStatus.Redundant
};
public static SaStatus DefaultStatus { get { return ActiveStatus; } }
public static SaStatus ActiveStatus { get { return SaStatus.Current; } }
public static SaStatus InactiveStatus { get { return SaStatus.Redundant; } }
public static List<SaStatus> ValidStatuses { get { return new List<SaStatus>(validStatuses); } }
public static DataValue.TriStateType GetStatusTriState (SaStatus status)
{
switch (status)
{
case SaStatus.Current:
return Core.DataValue.TriStateType.Green;
case SaStatus.Redundant:
return Core.DataValue.TriStateType.Red;
case SaStatus.Deleted:
return Core.DataValue.TriStateType.Purple;
}
return Core.DataValue.TriStateType.Unknown;
}
//-------------------------------------------------------------------
// Properties.
public Data GetData () { return data.Clone(); }
public void SetData (Data data)
{
string fn = GetType().Name + ".SetData";
// Properties that cannot be changed if previously specified.
if (SysId.IsValid(Id) && SysId.ToInt(Id) != SysId.ToInt(data.Id))
throw new NotSupportedException(fn + " Id cannot be changed");
if (!StringUtil.IsEmpty(Code) && !Code.Equals(data.Code))
throw new NotSupportedException(fn + " Code cannot be changed");
// Populate individual properties to ensure that 'Changed' indicator is kept up to date.
this.data.PopulateFrom(data);
}
public SysId CodeTypeId
{
get { return data.CodeTypeId; }
set { data.CodeTypeId = value; }
}
public string Code
{
get { return data.Code; }
set { data.Code = value; }
}
public string Description
{
get { return data.Description; }
set { data.Description = value; }
}
public ushort Sequence
{
get { return data.Sequence; }
set { data.Sequence = value; }
}
public string DataValue
{
get { return data.DataValue; }
set { data.DataValue = value; }
}
//-------------------------------------------------------------------
// Flags - specific to code type definition.
//-------------------------------------------------------------------
// List parameters.
public enum StatusCat
{
Any,
ActiveOnly,
InactiveOnly,
}
/// <summary>
/// Item list parameters.
/// </summary>
public new class Params : SacsHierarchyItem.Params
{
public List<SysId> IncludedIds { get; } = new List<SysId>();
public List<SysId> ExcludedIds { get; } = new List<SysId>();
public List<SysId> CodeTypeIds { get; } = new List<SysId>();
public List<String> CodeTypeCodes { get; } = new List<string>();
public string Code { get; set; }
public string CodePartial { get; set; }
public string Desc { get; set; }
public string DescPartial { get; set; }
public StatusCat StatusCat { get; set; } = StatusCat.ActiveOnly;
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class SaCodeList
{
// Contents.
public static bool ContainsCode (this List<SaCode> codes, String code, bool currentOnly = true)
{
if (StringUtil.IsEmpty(code)) return false;
return codes.Exists(item => item.Code.EqualsCI(code) && (!currentOnly || item.IsStatusCurrent));
}
// Retrieve code.
public static SaCode GetCodeById (this List<SaCode> codes, SysId id)
{
return codes.Find(item => item.Id.Equals(id));
}
public static SaCode GetCodeByCode (this List<SaCode> codes, string code)
{
return codes.Find(item => item.Code.EqualsCI(code));
}
// Retrieve code's description.
public static string GetDescForCode (this List<SaCode> codes, string code, String defaultDesc = "")
{
SaCode saCode = codes.Find(item => item.Code.EqualsCI(code));
if (saCode != null)
return saCode.Description;
return defaultDesc;
}
}
//=======================================================================
}
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.CodeTypes
{
//=======================================================================
public class SaCodeDfn
{
//-------------------------------------------------------------------
public SaCodeDfn (String code, String description)
{
Code = code;
Description = description;
}
//-------------------------------------------------------------------
// Properties.
public String Code { get; private set; } = String.Empty;
public String Description { get; private set; } = String.Empty;
public String Data { get; set; } = String.Empty;
public String Notes { get; set; } = String.Empty;
public ushort Sequence { get; private set; } = 0;
//-------------------------------------------------------------------
}
//=======================================================================
public static class SaCodeDfnList
{
public static void Add(this List<SaCodeDfn> values, String code, String description)
{
values.Add(new SaCodeDfn(code, description));
}
}
//=======================================================================
}
@@ -0,0 +1,229 @@
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.CodeTypes
{
//=======================================================================
public class SaCodeType : SacsHierarchyItem
{
//-------------------------------------------------------------------
// Columns.
public enum Column
{
Pos = 0,
// Code type properties.
Id,
Code,
Description,
MaxCodeLen,
CodeFormat,
NextCode,
AutoAssignCode_Y,
AutoAssignCode_Tick,
Codes_Count,
// Common properties.
Notes,
Flags,
Status,
Status_Desc,
Status_Ind,
CreateOn_Date,
UpdateOn_Date,
}
//-------------------------------------------------------------------
/// <summary>Properties other than common.</summary>
public class DataProperties
{
public String Code { get; set; } = String.Empty;
public String Description { get; set; } = String.Empty;
public byte MaxCodeLen { get; set; } = 0;
public String CodeFormat { get; set; } = String.Empty;
public String NextCode { get; set; } = String.Empty;
}
//-------------------------------------------------------------------
// Data.
public new class Data : SacsHierarchyItem.Data
{
// Data members.
private readonly DataProperties data = new DataProperties();
// Construction.
public Data ()
{
Status = DefaultStatus;
}
// Properties.
public String Code
{
get { return data.Code; }
set { CheckChanged(data.Code, value); data.Code = value; }
}
public String Description
{
get { return data.Description; }
set { CheckChanged(data.Description, value); data.Description = value; }
}
public byte MaxCodeLen
{
get { return data.MaxCodeLen; }
set { CheckChanged(data.MaxCodeLen, value); data.MaxCodeLen = value; }
}
public String CodeFormat
{
get { return data.CodeFormat; }
set { CheckChanged(data.CodeFormat, value); data.CodeFormat = value; }
}
public String NextCode
{
get { return data.NextCode; }
set { CheckChanged(data.NextCode, value); data.NextCode = value; }
}
// Population.
internal void PopulateFrom (Data data)
{
Code = data.Code;
Description = data.Description;
MaxCodeLen = data.MaxCodeLen;
CodeFormat = data.CodeFormat;
NextCode = data.NextCode;
base.PopulateFrom(data);
}
public Data Clone ()
{
Data data = new Data();
data.PopulateFrom(this);
return data;
}
}
//-------------------------------------------------------------------
// Error field - identify properties with validation error.
public new class ErrField : SacsHierarchyItem.ErrField
{
// Identifies the field that has invalid data.
public ValidValue Code { get; set; } = ValidValue.Valid;
public ValidValue Description { get; set; } = ValidValue.Valid;
public ValidValue MaxCodeLen { get; set; } = ValidValue.Valid;
public ValidValue CodeFormat { get; set; } = ValidValue.Valid;
public ValidValue NextCode { get; set; } = ValidValue.Valid;
}
//-------------------------------------------------------------------
// Data members.
private readonly Data data = new Data();
// Flags.
public static readonly Byte Flag_AutoAssignCode = 0x01;
//-------------------------------------------------------------------
// Construction.
public SaCodeType (SysId id, String code) :
base(id)
{
base.SetData(data);
Code = code;
Status = DefaultStatus;
}
//-------------------------------------------------------------------
// Statuses.
private static readonly List<SaStatus> validStatuses = new List<SaStatus>()
{
SaStatus.Current
};
public static SaStatus DefaultStatus { get { return CurrentStatus; } }
public static SaStatus CurrentStatus { get { return SaStatus.Current; } }
public static List<SaStatus> ValidStatuses { get { return new List<SaStatus>(validStatuses); } }
public static DataValue.TriStateType GetStatusTriState (SaStatus status)
{
switch (status)
{
case SaStatus.Current:
return Core.DataValue.TriStateType.Green;
case SaStatus.Deleted:
return Core.DataValue.TriStateType.Purple;
}
return Core.DataValue.TriStateType.Unknown;
}
//-------------------------------------------------------------------
// Properties.
public Data GetData () { return data.Clone(); }
public void SetData (Data data)
{
String fn = GetType().Name + ".SetData";
// Properties that cannot be changed if previously specified.
if (SysId.IsValid(Id) && SysId.ToInt(Id) != SysId.ToInt(data.Id))
throw new NotSupportedException(fn + " Id cannot be changed");
if (!StringUtil.IsEmpty(Code) && !Code.Equals(data.Code))
throw new NotSupportedException(fn + " Code cannot be changed");
// Populate individual properties to ensure that 'Changed' indicator is kept up to date.
this.data.PopulateFrom(data);
}
public String Code
{
get { return data.Code; }
set { data.Code = value; }
}
public String Description
{
get { return data.Description; }
set { data.Description = value; }
}
public byte MaxCodeLen
{
get { return data.MaxCodeLen; }
set { data.MaxCodeLen = value; }
}
public String CodeFormat
{
get { return data.CodeFormat; }
set { data.CodeFormat = value; }
}
public String NextCode
{
get { return data.NextCode; }
set { data.NextCode = value; }
}
//-------------------------------------------------------------------
// Flags.
public bool AutoAssignCode
{
get { return GetFlag(Flag_AutoAssignCode); }
set { SetFlag(Flag_AutoAssignCode, value); }
}
//-------------------------------------------------------------------
// List parameters.
public enum StatusCat
{
Any,
ActiveOnly,
}
/// <summary>
/// Item list parameters.
/// </summary>
public new class Params : SacsHierarchyItem.Params
{
public List<SysId> IncludedIds { get; } = new List<SysId>();
public List<SysId> ExcludedIds { get; } = new List<SysId>();
public String Code { get; set; }
public String CodePartial { get; set; }
public String Desc { get; set; }
public String DescPartial { get; set; }
public StatusCat StatusCat { get; set; } = StatusCat.ActiveOnly;
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class SaCodeTypeList
{
}
//=======================================================================
}
@@ -0,0 +1,149 @@
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Sarand.Net.Standard.Base.CodeTypes
{
//=======================================================================
public abstract class SaCodeTypeConsole : SaErrorStateItem
{
//-------------------------------------------------------------------
// Data members.
protected readonly List<SaCodeTypeDfn> codeTypeDfns = new List<SaCodeTypeDfn>();
//-------------------------------------------------------------------
// Static initialisation.
static SaCodeTypeConsole ()
{
}
//-------------------------------------------------------------------
// Construction.
protected SaCodeTypeConsole ()
{
}
//-------------------------------------------------------------------
// Properties.
public abstract bool DatabaseIsValid { get; }
public abstract bool CanUpdateDatabase { get; }
//-------------------------------------------------------------------
// C o d e T y p e D e f i n i t i o n s
//-------------------------------------------------------------------
public List<SaCodeTypeDfn> CodeTypeDefinitions { get { return new List<SaCodeTypeDfn>(codeTypeDfns); } }
public SaCodeTypeDfn GetCodeTypeDefinition (String codeTypeCode)
{
ClearErrorState();
foreach (SaCodeTypeDfn codeTypeDfn in codeTypeDfns)
if (codeTypeDfn.Code.EqualsCI(codeTypeCode))
return codeTypeDfn;
return null;
}
public abstract SaCodeTypeDfn GetCodeTypeDefinition (SysId codeTypeId);
public virtual String GetDescForCodeType (String codeTypeCode)
{
ClearErrorState();
SaCodeTypeDfn codeTypeDfn = GetCodeTypeDefinition(codeTypeCode);
if (codeTypeDfn != null) return codeTypeDfn.Description;
return "";
}
public bool IsCodeTypeSequenceAllowed (String codeTypeCode)
{
ClearErrorState();
SaCodeTypeDfn codeTypeDfn = GetCodeTypeDefinition(codeTypeCode);
if (codeTypeDfn != null) return codeTypeDfn.AllowSequence;
return false;
}
//-------------------------------------------------------------------
// Code Type registration - for use in validation and population of default database.
public bool RegisterCodeType (SaCodeTypeDfn codeTypeDfn)
{
ClearErrorState();
if (codeTypeDfns.GetCodeTypeDfnByCode(codeTypeDfn.Code) != null) return false;
codeTypeDfns.Add(codeTypeDfn);
return true;
}
public bool RegisterCodesOfType (String codeTypeCode, List<CodeDesc> codeDfns)
{
ClearErrorState();
SaCodeTypeDfn codeTypeDfn = GetCodeTypeDefinition(codeTypeCode);
if (codeTypeDfn == null) return false;
codeTypeDfn.DefaultCodeDefinitions.Clear();
foreach (CodeDesc codeDFn in codeDfns)
{
codeTypeDfn.DefaultCodeDefinitions.Add(new SaCodeDfn(codeDFn.Code,codeDFn.Desc));
}
return true;
}
//*******************************************************************
// C o d e T y p e s
//*******************************************************************
public abstract bool CodeTypeExists (String codeTypeCode);
public abstract int GetCodeTypeCount (SaCodeType.Params prmCodeType);
public abstract SaCodeType GetCodeType (SaCodeType.Params prmCodeType);
public abstract List<SaCodeType> GetCodeTypeList (SaCodeType.Params prmCodeType);
// Code type list.
public abstract ColumnDfn GetCodeTypeColumnDfn (SaCodeType.Column columnId, String caption = "", int width = 0);
public abstract DbResultSet ExecuteCodeTypeQuery (List<ColumnDfn> columns, SaCodeType.Params prm);
/// <summary>
/// Extract and format result set data value where specific handling is required.
/// <para>Where response is an invalid data value, format value using column.FormatValue(values).</para>
/// </summary>
public abstract DataValue GetCodeTypeDataValue (ColumnDfn column, NamedValue.Map values);
// Population.
public abstract bool PopulateCodeType (String codeTypeCode, DbUpdateInfo dbUpdateInfo);
// Maintain code types - returns true if changes made.
public abstract bool MaintainCodeTypes (bool userCanManageCodeTypes, DbUpdateInfo dbUpdateInfo);
public abstract bool MaintainCodeType (String codeTypeCode, DbUpdateInfo dbUpdateInfo);
//*******************************************************************
// C o d e s
//*******************************************************************
// Check whether code exists.
public abstract bool CodeExistsForCodeType (String codeTypeCode, String code, bool activeOnly = true);
// Codes for code type.
public abstract bool CodeTypeHasCodes (String codeTypeCode, bool activeOnly = true);
public abstract int GetCodeCountForCodeType (String codeTypeCode, bool activeOnly = true);
public abstract SaCode GetCodeForCodeType (String codeTypeCode, String code, bool activeOnly = true);
public abstract List<CodeDesc> GetCodeDescsForCodeType (String codeTypeCode, bool activeOnly = true);
public abstract List<SaCode> GetCodeListForCodeType (String codeTypeCode, bool activeOnly = true);
// Code list.
public abstract ColumnDfn GetCodeColumnDfn (SaCode.Column columnId, String caption = "", int width = 0);
public abstract List<SaCode> GetCodeList (SaCode.Params prmCode);
public abstract DbResultSet ExecuteCodeQuery (List<ColumnDfn> columns, SaCode.Params prm);
/// <summary>
/// Extract and format result set data value where specific handling is required.
/// <para>Where response is an invalid data value, format value using column.FormatValue(values).</para>
/// </summary>
public abstract DataValue GetCodeDataValue (ColumnDfn column, NamedValue.Map values);
// Codes by filter.
public abstract int GetCodeCount (SaCode.Params prmCode);
public abstract SaCode GetCode (SaCode.Params prmCode);
/// <summary>Get filtered codes, indexed by Code Type code.</summary>
public abstract Dictionary<String, List<SaCode>> GetCodesByCodeType (SaCode.Params prmCode);
public abstract Task<Dictionary<String, List<SaCode>>> GetCodesByCodeTypeAsync (SaCode.Params prmCode);
/// <summary>Get filtered codes and descriptions, indexed by Code Type code.</summary>
public abstract Dictionary<String, List<CodeDesc>> GetCodeDescsByCodeType (SaCode.Params prmCode);
//-------------------------------------------------------------------
// Create codes.
public abstract bool CreateCodeOfType (SysId codeTypeId, String code, String desc, DbUpdateInfo dbUpdateInfo);
public abstract bool CreateCodeOfType (String codeTypeCode, String code, String desc, DbUpdateInfo dbUpdateInfo);
// Population.
public abstract bool PopulateCode (String codeTypeCode, String code, String desc, bool active, DbUpdateInfo dbUpdateInfo);
//-------------------------------------------------------------------
// Select codes.
public abstract List<String> SelectCodes (String codeTypeCode, List<String> codesSelected);
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,351 @@
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
//---------------------------------------------------------------------------
// Code generated by Sarand Database Utilities Thu 3-Oct-2019 15:10:33
//---------------------------------------------------------------------------
namespace Sarand.Net.Standard.Base.CodeTypes
{
public class SaCodeTypeDbDfn
{
//-------------------------------------------------------------------
// Table identifiers.
public enum TableIdx
{
Unknown = 0,
SaCode,
SaCodeType,
}
//-------------------------------------------------------------------
// Column identifiers.
public enum ColumnIdx
{
Unknown = 0,
SaCode_Id,
SaCode_CodeTypeId,
SaCode_Code,
SaCode_Description,
SaCode_Seq,
SaCode_Data,
SaCode_Notes,
SaCode_Flags,
SaCode_Status,
SaCode_CreateOn,
SaCode_CreateBy,
SaCode_CreateHost,
SaCode_UpdateOn,
SaCode_UpdateBy,
SaCode_UpdateHost,
SaCodeType_Id,
SaCodeType_Code,
SaCodeType_Name,
SaCodeType_MaxCodeLen,
SaCodeType_CodeFormat,
SaCodeType_NextCode,
SaCodeType_Notes,
SaCodeType_Flags,
SaCodeType_Status,
SaCodeType_CreateOn,
SaCodeType_CreateBy,
SaCodeType_CreateHost,
SaCodeType_UpdateOn,
SaCodeType_UpdateBy,
SaCodeType_UpdateHost,
}
//-------------------------------------------------------------------
// Data members.
private static readonly int tableCount = 2;
private static readonly int columnCount = 30;
private static ulong maxTextLen = 0;
//-------------------------------------------------------------------
// Retrieve Table details (eg. name: 'Company').
public static int GetTableCount ()
{
return tableCount;
}
public static String GetTableName (TableIdx table, bool delimitReservedWords = true)
{
int tableIdx = (int)table - 1;
if (tableIdx < 0 || tableIdx >= tableCount) return String.Empty;
String tableName = tables[(int)tableIdx];
if (delimitReservedWords) tableName = DelimitReservedWord(tableName);
return tableName;
}
//-------------------------------------------------------------------
// Identify the table that corresponds with the specified column.
public static TableIdx GetTableForColumn (ColumnIdx column)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return TableIdx.Unknown;
return columns[columnIdx].TableIdx;
}
//-------------------------------------------------------------------
// Retrieve the specified Column's name (Qualified include table name: '(<Table>.Column>)').
public static String GetColumnName (ColumnIdx column, bool delimitReservedWords = true)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return String.Empty;
String columnName = columns[columnIdx].Name;
if (delimitReservedWords) columnName = DelimitReservedWord(columnName);
return columnName;
}
public static String GetColumnNameQual (ColumnIdx column, String tableAlias = "", bool braces = false)
{
String columnName = GetColumnName(column);
if (!String.IsNullOrEmpty(columnName))
{
String tableName = StringUtil.EmptyOnNull(tableAlias);
if (tableName.IsEmpty()) tableName = GetTableName(GetTableForColumn(column));
columnName = tableName + (tableName.Length > 0 ? "." : "") + columnName;
if (braces)
columnName = "(" + columnName + ")";
}
return columnName;
}
//-------------------------------------------------------------------
// Reserved words.
private static DbReservedWordDelim reservedWordDelim = DbReservedWordDelim.SquareBrackets;
public static void SetReservedWordDelim (DbReservedWordDelim reservedWordDelim)
{
SaCodeTypeDbDfn.reservedWordDelim = reservedWordDelim;
}
public static bool IsReservedWord (String word)
{
if (String.IsNullOrEmpty(word)) return false;
if (reservedWords.Contains(word, StringComparer.OrdinalIgnoreCase)) return true;
return false;
}
public static String DelimitReservedWord (String word)
{
if (!IsReservedWord(word)) return word;
switch (reservedWordDelim)
{
case DbReservedWordDelim.None:
break;
case DbReservedWordDelim.DoubleQuotes:
word = "\"" + word + "\"";
break;
case DbReservedWordDelim.SquareBrackets:
default:
word = "[" + word + "]";
break;
}
return word;
}
//-------------------------------------------------------------------
// Retrieve information about the specified Column.
public static DbColumnDataType GetColDataType (ColumnIdx column)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return DbColumnDataType.Unknown;
return columns[columnIdx].DataType;
}
public static short GetColNativeDataType (ColumnIdx column)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return 0;
return columns[columnIdx].NativeDataType;
}
public static void SetMaxTextLen (ulong maxTextLen)
{
SaCodeTypeDbDfn.maxTextLen = maxTextLen;
}
public static bool IsColFmtText (ColumnIdx column)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return false;
return (columns[columnIdx].DataType == DbColumnDataType.VarChar);
}
public static ulong GetColTextLen (ColumnIdx column)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return 0;
if (columns[columnIdx].DataType == DbColumnDataType.VarChar)
{
ulong textLen = columns[columnIdx].DataLen;
if (maxTextLen > 0 && textLen > maxTextLen) textLen = maxTextLen;
return textLen;
}
return 0;
}
public static bool IsColFmtBool (ColumnIdx column)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return false;
return (columns[columnIdx].DataType == DbColumnDataType.Bit);
}
public static bool IsColFmtNumeric (ColumnIdx column)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return false;
switch (columns[columnIdx].DataType)
{
case DbColumnDataType.TinyInt:
case DbColumnDataType.SmallInt:
case DbColumnDataType.Int:
case DbColumnDataType.BigInt:
case DbColumnDataType.Single:
case DbColumnDataType.Double:
case DbColumnDataType.Decimal:
return true;
}
return false;
}
//-------------------------------------------------------------------
// Table definitions.
private static readonly String[] tables =
{
"SaCode",
"SaCodeType",
};
//-------------------------------------------------------------------
// Table column definitions.
internal class ColumnDfn
{
internal TableIdx TableIdx { get; set; }
internal String Name { get; set; }
internal DbColumnDataType DataType { get; set; }
internal short NativeDataType { get; set; }
internal bool IsIdentity { get; set; }
internal ulong DataLen { get; set; }
internal ColumnDfn (int tableIdx, String name, int dataType, short nativeDataType, bool isIdentity, ulong dataLen)
{
this.TableIdx = (TableIdx)tableIdx;
this.Name = name;
this.DataType = (DbColumnDataType)dataType;
this.NativeDataType = nativeDataType;
this.IsIdentity = isIdentity;
this.DataLen = dataLen;
}
}
//-------------------------------------------------------------------
private static readonly ColumnDfn[] columns =
{
new ColumnDfn(1,"Id",4,4,false,10),
new ColumnDfn(1,"CodeTypeId",4,4,false,10),
new ColumnDfn(1,"Code",0,-9,false,32),
new ColumnDfn(1,"Description",0,-9,false,128),
new ColumnDfn(1,"Seq",3,5,false,5),
new ColumnDfn(1,"Data",0,-9,false,255),
new ColumnDfn(1,"Notes",0,-9,false,255),
new ColumnDfn(1,"Flags",3,5,false,5),
new ColumnDfn(1,"Status",0,-9,false,3),
new ColumnDfn(1,"CreateOn",9,93,false,23),
new ColumnDfn(1,"CreateBy",4,4,false,10),
new ColumnDfn(1,"CreateHost",0,-9,false,64),
new ColumnDfn(1,"UpdateOn",9,93,false,23),
new ColumnDfn(1,"UpdateBy",4,4,false,10),
new ColumnDfn(1,"UpdateHost",0,-9,false,64),
new ColumnDfn(2,"Id",4,4,false,10),
new ColumnDfn(2,"Code",0,-9,false,32),
new ColumnDfn(2,"Name",0,-9,false,128),
new ColumnDfn(2,"MaxCodeLen",2,-6,false,3),
new ColumnDfn(2,"CodeFormat",0,-9,false,32),
new ColumnDfn(2,"NextCode",0,-9,false,32),
new ColumnDfn(2,"Notes",0,-9,false,255),
new ColumnDfn(2,"Flags",3,5,false,5),
new ColumnDfn(2,"Status",0,-9,false,3),
new ColumnDfn(2,"CreateOn",9,93,false,23),
new ColumnDfn(2,"CreateBy",4,4,false,10),
new ColumnDfn(2,"CreateHost",0,-9,false,64),
new ColumnDfn(2,"UpdateOn",9,93,false,23),
new ColumnDfn(2,"UpdateBy",4,4,false,10),
new ColumnDfn(2,"UpdateHost",0,-9,false,64),
};
//-------------------------------------------------------------------
// Reserved words.
private static readonly List<String> reservedWords = new List<String>()
{
"ADD",
"ALL",
"ALTER",
"AND",
"ANY",
"AS",
"ASC",
"AVG",
"BEGIN",
"BETWEEN",
"BY",
"CASE",
"CAST",
"COLUMN",
"COMMIT",
"CONSTRAINT",
"CONVERT",
"COUNT",
"CREATE",
"CURRENCY",
"DEFAULT",
"DELETE",
"DESC",
"DISTINCT",
"DROP",
"ELSE",
"END",
"EXECUTE",
"EXISTS",
"FALSE",
"FROM",
"GRANT",
"GROUP",
"HAVING",
"IN",
"INDEX",
"INNER",
"INSERT",
"INTO",
"IS",
"JOIN",
"KEY",
"LEFT",
"LIKE",
"MAX",
"MIN",
"NOT",
"NULL",
"ON",
"OR",
"ORDER",
"OUTER",
"OUTPUT",
"PRIMARY",
"PROCEDURE",
"REVOKE",
"ROLLBACK",
"ROWCOUNT",
"SELECT",
"SET",
"SQL",
"SUM",
"TABLE",
"THEN",
"TRANSACTION",
"TRUE",
"UNION",
"UNIQUE",
"UPDATE",
"USER",
"VALUES",
"VIEW",
"WHEN",
"WHERE",
};
//-------------------------------------------------------------------
}
}
@@ -0,0 +1,53 @@
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.CodeTypes
{
//=======================================================================
public class SaCodeTypeDfn
{
//-------------------------------------------------------------------
public SaCodeTypeDfn (String code, String description, byte maxCodeLen = 0, String notes = "")
{
Code = code;
Description = description;
MaxCodeLen = maxCodeLen;
Notes = notes;
}
//-------------------------------------------------------------------
// Properties.
public String Code { get; } = String.Empty;
public String Description { get; } = String.Empty;
public byte MaxCodeLen { get; } = 0;
public String Notes { get; } = String.Empty;
public bool AllowMixedCode { get; set; } = false;
public bool AllowSequence { get; set; } = false;
public bool AllowData { get; set; } = false;
public bool AllowMixedData { get; set; } = false;
public String DataLabel { get; set; } = String.Empty;
public List<FlagDfn> FlagDefinitions { get; } = new List<FlagDfn>();
public List<SaCodeDfn> DefaultCodeDefinitions { get; } = new List<SaCodeDfn>();
//-------------------------------------------------------------------
}
//=======================================================================
public static class SaCodeTypeDfnList
{
// Retrieve definition from the list.
public static SaCodeTypeDfn GetCodeTypeDfnByCode (this List<SaCodeTypeDfn> values, String code)
{
return values.Find(item => item.Code.EqualsCI(code));
}
// Add definition to the list.
public static void Add(this List<SaCodeTypeDfn> values, String code, String description)
{
values.Add(new SaCodeTypeDfn(code, description));
}
}
//=======================================================================
}
@@ -0,0 +1,319 @@
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Sarand.Net.Standard.Base.CodeTypes
{
//=======================================================================
public abstract class SaCodeTypeMgr : SacsItemMgr
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Static initialisation.
static SaCodeTypeMgr ()
{
}
//-------------------------------------------------------------------
// Construction.
protected SaCodeTypeMgr ()
{
}
//-------------------------------------------------------------------
// Properties.
public abstract bool DatabaseIsValid { get; }
public abstract bool CanUpdateDatabase { get; }
//*******************************************************************
// C o d e T y p e s
//*******************************************************************
// Associated data.
public abstract bool CodeTypeHasCodes (SysId codeTypeId);
//-------------------------------------------------------------------
// Create Item.
public abstract SaCodeType CreateCodeType (String code);
//-------------------------------------------------------------------
// Retrieve individual Items.
public abstract bool CodeTypeExistsById (SysId id);
public abstract bool CodeTypeExistsByCode (String code);
public abstract SaCodeType GetCodeType (SaCodeType.Params prmCodeType);
public abstract SaCodeType GetCodeTypeById (SysId id);
public abstract SaCodeType GetCodeTypeByCode (String code);
//-------------------------------------------------------------------
// Validate Code Type.
public bool IsValidCodeType (SaCodeType codeType)
{
ClearErrorState();
if (codeType == null) return SetErrorState(ErrorMessage.ItemNotSpecified);
return IsValidCodeTypeData(codeType.GetData(), new SaCodeType.ErrField());
}
public virtual bool IsValidCodeTypeData (SaCodeType.Data codeTypeData, SaCodeType.ErrField errField)
{
ClearErrorState();
if (!IsValidValue(codeTypeData.Code, "code", (long)SaCodeTypeDbDfn.GetColTextLen(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_Code), true))
{
errField.Code = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
if (!IsValidValue(codeTypeData.Description, "description", (long)SaCodeTypeDbDfn.GetColTextLen(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_Name), true))
{
errField.Description = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
if (!IsValidValue(codeTypeData.CodeFormat, "code format", (long)SaCodeTypeDbDfn.GetColTextLen(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_CodeFormat)))
{
errField.CodeFormat = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
if (!IsValidValue(codeTypeData.NextCode, "next code", (long)SaCodeTypeDbDfn.GetColTextLen(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_NextCode)))
{
errField.NextCode = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
// Common properties.
if (!IsValidValue(codeTypeData.Notes, "notes", (long)SaCodeTypeDbDfn.GetColTextLen(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_Notes)))
{
errField.Notes = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
if (!SaCodeType.ValidStatuses.Contains(codeTypeData.Status))
{
errField.Status = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState(ErrorMessage.InvalidStatus);
}
return true;
}
//-------------------------------------------------------------------
// CodeType database interface.
public abstract bool InsertCodeType (SaCodeType codeType, DbUpdateInfo dbUpdateInfo);
public abstract bool UpdateCodeType (SaCodeType codeType, DbUpdateInfo dbUpdateInfo);
public virtual bool CodeTypeCanBeDeleted (SaCodeType codeType)
{
ClearErrorState();
if (CodeTypeHasCodes(codeType.Id)) return SetErrorState("code type has codes");
return true;
}
public abstract bool DeleteCodeType (SaCodeType codeType, DbUpdateInfo dbUpdateInfo);
//-------------------------------------------------------------------
// Code Type list.
public abstract ColumnDfn GetCodeTypeColumnDfn (SaCodeType.Column columnId, String caption = "", int width = 0);
public abstract List<String> GetCodeTypeQueryFilter (SaCodeType.Params prm);
public abstract long GetCodeTypeCount (SaCodeType.Params prm);
public abstract List<SaCodeType> GetCodeTypeList (SaCodeType.Params prm);
public abstract Task<List<SaCodeType>> GetCodeTypeListAsync (SaCodeType.Params prm);
public abstract DbResultSet ExecuteCodeTypeQuery (List<ColumnDfn> columns, SaCodeType.Params prm);
public abstract Task<DbResultSet> ExecuteCodeTypeQueryAsync (List<ColumnDfn> columns, SaCodeType.Params prm);
//-------------------------------------------------------------------
/// <summary>
/// Extract and format result set data value where specific handling is required.
/// <para>Where response is an invalid data value, format value using column.FormatValue(values).</para>
/// </summary>
public virtual DataValue GetCodeTypeDataValue (ColumnDfn column, NamedValue.Map values)
{
DataValue value = new DataValue();
switch ((SaCodeType.Column)column.Id)
{
//-----------------------------------------------------------
// Item type.
case SaCodeType.Column.AutoAssignCode_Y:
case SaCodeType.Column.AutoAssignCode_Tick:
{
ushort flags = (ushort)values.GetValue(column.DataName).AsLong();
bool autoAssignCode = SacsHierarchyItem.GetFlag(flags, SaCodeType.Flag_AutoAssignCode);
if (column.Id == (int)SaCodeType.Column.AutoAssignCode_Y)
value = new DataValue(autoAssignCode ? "Y" : "");
else
value = new DataValue(autoAssignCode);
}
break;
//-----------------------------------------------------------
// Notes.
case SaCodeType.Column.Notes:
value = new DataValue(SacsHierarchyItem.FormatForDisplay(values.GetValue(column.DataName).ToString(), 200));
break;
//-----------------------------------------------------------
// Status.
case SaCodeType.Column.Status_Desc:
case SaCodeType.Column.Status_Ind:
{
String statusCode = values.GetValue(column.DataName).ToString();
SaStatus status = SacsSystem.GetStatusForCode(statusCode);
if (column.Id == (int)SaCodeType.Column.Status_Desc)
{
String statusDesc = SacsSystem.GetStatusDescForCode(statusCode);
value = new DataValue(statusDesc);
}
else if (column.Id == (int)SaCodeType.Column.Status_Ind)
{
DataValue.TriStateType triState = DataValue.TriStateType.Unknown;
if (status == SaStatus.Current || status == SaStatus.Active)
triState = DataValue.TriStateType.Green;
value = new DataValue(triState);
}
}
break;
//-----------------------------------------------------------
}
return value;
}
//*******************************************************************
// C o d e s
//*******************************************************************
// Retrieve individual codes.
public abstract bool CodeExistsById (SysId id);
public abstract SaCode GetCode (SaCode.Params prmCode);
public abstract SaCode GetCodeById (SysId id);
public abstract SaCode GetCodeByCodeTypeCode (String codeTypeCode, String code);
// Create code.
public abstract SaCode CreateCode (SysId codeTypeId, String code);
//-------------------------------------------------------------------
// Validate code data.
public bool IsValidCode (SaCode code)
{
ClearErrorState();
if (code == null) return SetErrorState(ErrorMessage.ItemNotSpecified);
return IsValidCodeData(code.GetData(), new SaCode.ErrField());
}
public virtual bool IsValidCodeData (SaCode.Data codeData, SaCode.ErrField errField)
{
ClearErrorState();
if (!SysId.IsValid(codeData.CodeTypeId))
{
errField.CodeType = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState("code type must be specified");
}
if (!IsValidValue(codeData.Code, "code", (long)SaCodeTypeDbDfn.GetColTextLen(SaCodeTypeDbDfn.ColumnIdx.SaCode_Code), true))
{
errField.Code = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
if (!IsValidValue(codeData.Description, "description", (long)SaCodeTypeDbDfn.GetColTextLen(SaCodeTypeDbDfn.ColumnIdx.SaCode_Description), true))
{
errField.Description = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
if (codeData.Sequence < 0)
{
errField.Sequence = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState("sequence must be positive");
}
if (!IsValidValue(codeData.DataValue, "data value", (long)SaCodeTypeDbDfn.GetColTextLen(SaCodeTypeDbDfn.ColumnIdx.SaCode_Data)))
{
errField.DataValue = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
// Common properties.
if (!IsValidValue(codeData.Notes, "notes", (long)SaCodeTypeDbDfn.GetColTextLen(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_Notes)))
{
errField.Notes = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
if (!SaCode.ValidStatuses.Contains(codeData.Status))
{
errField.Status = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState(ErrorMessage.InvalidStatus);
}
return true;
}
//-------------------------------------------------------------------
// Code database interface.
public abstract bool InsertCode (SaCode code, DbUpdateInfo dbUpdateInfo);
public abstract bool UpdateCode (SaCode code, DbUpdateInfo dbUpdateInfo);
public virtual bool CodeCanBeDeleted (SaCode code)
{
ClearErrorState();
return true;
}
public abstract bool DeleteCode (SaCode code, DbUpdateInfo dbUpdateInfo);
//-------------------------------------------------------------------
// Code list.
public abstract ColumnDfn GetCodeColumnDfn (SaCode.Column columnId, String caption = "", int width = 0);
public abstract List<String> GetCodeQueryFilter (SaCode.Params prm);
public abstract long GetCodeCount (SaCode.Params prm);
public abstract List<SaCode> GetCodeList (SaCode.Params prm);
public abstract Task<List<SaCode>> GetCodeListAsync (SaCode.Params prm);
public abstract DbResultSet ExecuteCodeQuery (List<ColumnDfn> columns, SaCode.Params prm);
public abstract Task<DbResultSet> ExecuteCodeQueryAsync (List<ColumnDfn> columns, SaCode.Params prm);
//-------------------------------------------------------------------
/// <summary>
/// Extract and format result set data value where specific handling is required.
/// <para>Where response is an invalid data value, format value using column.FormatValue(values).</para>
/// </summary>
public virtual DataValue GetCodeDataValue (ColumnDfn column, NamedValue.Map values)
{
DataValue value = new DataValue();
SaCode.Column colId = (SaCode.Column)column.Id;
switch (colId)
{
//-----------------------------------------------------------
// Context Item description.
case SaCode.Column.Flag1_Y:
case SaCode.Column.Flag1_Tick:
case SaCode.Column.Flag2_Y:
case SaCode.Column.Flag2_Tick:
{
ushort flags = (ushort)values.GetValue(column.DataName).AsLong();
bool flag1 = SacsHierarchyItem.GetFlag(flags, 1);
bool flag2 = SacsHierarchyItem.GetFlag(flags, 2);
if (column.Id == (int)SaCode.Column.Flag1_Y)
value = new DataValue(flag1 ? "Y" : "");
else if (column.Id == (int)SaCode.Column.Flag1_Tick)
value = new DataValue(flag1);
else if (column.Id == (int)SaCode.Column.Flag2_Y)
value = new DataValue(flag2 ? "Y" : "");
else if (column.Id == (int)SaCode.Column.Flag2_Tick)
value = new DataValue(flag2);
}
break;
//-----------------------------------------------------------
// Notes.
case SaCode.Column.Notes:
value = new DataValue(SacsHierarchyItem.FormatForDisplay(values.GetValue(column.DataName).ToString(), 200));
break;
//-----------------------------------------------------------
// Status.
case SaCode.Column.Status_Desc:
case SaCode.Column.Status_Ind:
{
String statusValue = values.GetValue(column.DataName).ToString();
SaStatus status = SacsSystem.GetStatusForCode(statusValue);
if (column.Id == (int)SaCode.Column.Status_Desc)
value = new DataValue(SacsSystem.GetStatusDescForCode(statusValue));
else if (column.Id == (int)SaCode.Column.Status_Ind)
value = new DataValue(SaCode.GetStatusTriState(status));
}
break;
//-----------------------------------------------------------
}
return value;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+123
View File
@@ -0,0 +1,123 @@
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
public class CodeDesc
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
public CodeDesc (String code, String desc, String comment = "")
{
Code = StringUtil.EmptyOnNull(code);
Desc = StringUtil.EmptyOnNull(desc);
Comment = StringUtil.EmptyOnNull(comment);
}
//-------------------------------------------------------------------
// Properties.
public String Code { get; }
public String Desc { get; }
public String Comment { get; }
//-------------------------------------------------------------------
}
//=======================================================================
public static class CodeDescList
{
//-------------------------------------------------------------------
// Population.
public static void Add (this List<CodeDesc> values, String code, string desc, String comment = "")
{
values.Add(new CodeDesc(code, desc, comment));
}
public static void Insert (this List<CodeDesc> values, int idx, String code, string desc, String comment = "")
{
values.Insert (idx, new CodeDesc(code, desc, comment));
}
//-------------------------------------------------------------------
// Sorting.
public static void SortByCode (this List<CodeDesc> values)
{
values.Sort(
delegate (CodeDesc codeDesc1, CodeDesc codeDesc2)
{
return codeDesc1.Code.CompareTo(codeDesc2.Code);
}
);
}
public static void SortByDesc (this List<CodeDesc> values)
{
values.Sort(
delegate (CodeDesc codeDesc1, CodeDesc codeDesc2)
{
return codeDesc1.Desc.CompareTo(codeDesc2.Desc);
}
);
}
//-------------------------------------------------------------------
// Conversion.
public static List<String> GetCodeList (this List<CodeDesc> values)
{
List<String> codes = new List<string>();
foreach (var value in values)
if (!codes.Contains(value.Code)) codes.Add(value.Code);
return codes;
}
public static List<String> GetDescList (this List<CodeDesc> values)
{
List<String> descs = new List<string>();
foreach (var value in values)
if (!descs.Contains(value.Desc)) descs.Add(value.Desc);
return descs;
}
//-------------------------------------------------------------------
// Lookup.
public static bool FindCode (this List<CodeDesc> values, String code)
{
return values.Exists(item => item.Code.EqualsCI(code));
}
public static CodeDesc GetByCode (this List<CodeDesc> values, String code)
{
return values.Find(item => item.Code.EqualsCI(code));
}
public static CodeDesc GetByNumericCode (this List<CodeDesc> values, String code)
{
if (!StringUtil.IsNumeric(code)) return null;
return values.Find(item => StringUtil.IsNumeric(item.Code) && StringUtil.ToInt(item.Code) == StringUtil.ToInt(code));
}
//-------------------------------------------------------------------
// Lookup code.
/// <summary>Return code matching supplied code (case-insenstive) - to ensure case matches list.</summary>
public static String GetCodeForCode (this List<CodeDesc> values, String code, String defaultValue = "")
{
foreach (var value in values)
if (value.Code.EqualsCI(code)) return value.Code;
return StringUtil.EmptyOnNull(defaultValue);
}
/// <summary>Return code matching supplied description (case-insenstive).</summary>
public static String GetCodeForDesc (this List<CodeDesc> values, String desc, String defaultValue = "")
{
foreach (var value in values)
if (value.Desc.EqualsCI(desc)) return value.Code;
return StringUtil.EmptyOnNull(defaultValue);
}
//-------------------------------------------------------------------
// Lookup desc.
public static String GetDescForCode (this List<CodeDesc> values, String code, String defaultValue = "")
{
foreach (var value in values)
if (value.Code.EqualsCI(code)) return value.Desc;
return StringUtil.EmptyOnNull(defaultValue);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+66
View File
@@ -0,0 +1,66 @@
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
/// <summary>
/// Code parameter value with flag to indicate whether the value
/// is included (valid as a filter) or excluded (explicitly not matched).
/// </summary>
public class CodeParam
{
//-------------------------------------------------------------------
// Construction.
public CodeParam (String code, bool including)
{
Code = StringUtil.EmptyOnNull(code);
Including = including;
}
//-------------------------------------------------------------------
// Properties.
/// <summary>Code parameter.</summary>
public String Code { get; }
/// <summary>Whether the value is included (valid as a filter) or excluded (explicitly not matched).</summary>
public bool Including { get; }
//-------------------------------------------------------------------
/// <summary>Determine if the two code params 'match' - taking code and 'sense' flag into account.</summary>
public bool Matches (CodeParam cp)
{
// Values match if either code is blank:
if (StringUtil.IsEmpty(Code) || StringUtil.IsEmpty(cp.Code)) return true;
// Values match if both 'senses' are negative:
if (!Including && !cp.Including) return true;
// Values match if both 'senses' are positive and the codes are the same:
if (Including && cp.Including) return Code.EqualsCI(cp.Code);
// Values match if 'senses' and codes are different:
if (Including != cp.Including) return !Code.EqualsCI(cp.Code);
return false;
}
//-------------------------------------------------------------------
/// <summary>Compare code params - first on code then on flag.</summary>
public int Compare (CodeParam cp)
{
if (Code.EqualsCI(cp.Code))
{
// Compare 'Including' flags:
if (!Including && cp.Including) return 1;
if (Including && !cp.Including) return -1;
return 0;
}
return Code.CompareTo(cp.Code);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+428
View File
@@ -0,0 +1,428 @@
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
// Definitions.
public enum ColumnAlignment { Default, Left, Center, Right }
public enum ColumnBoolFormat
{
/// <summary>Text: 'Y' (true) or 'N' (false).</summary>
YorN,
/// <summary>Text: 'Y' (true).</summary>
Y,
/// <summary>Text: 'N' (false).</summary>
N,
/// <summary>Text: '1' (true) or '0' (false).</summary>
OneOrZero,
/// <summary>Text: 'True' or 'False'.</summary>
TrueFalse,
/// <summary>Graphic icons: Tick (true) or Cross (false).</summary>
TickCross,
/// <summary>Graphic icon: Tick (true).</summary>
Tick,
/// <summary>Graphic icon: Cross (false).</summary>
Cross,
/// <summary>Graphic icons (inverted): Tick (false) or Cross (true).</summary>
TickCross_Inv,
/// <summary>Graphic icon (inverted): Tick (false).</summary>
Tick_Inv,
/// <summary>Graphic icon (inverted): Cross (true).</summary>
Cross_Inv
}
public enum ColumnTriStateFormat
{
/// <summary>Text: R, A, G etc.</summary>
RAG,
/// <summary>Text: 1, 2, 3 etc.</summary>
Numeric,
/// <summary>Graphic icons.</summary>
Lights
}
//=======================================================================
public class ColumnDfn
{
//-------------------------------------------------------------------
// Definitions.
public enum ColumnSortMode
{
Off, On, AscendOnly, DescendOnly
}
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public ColumnDfn
(int id, String caption, int width = 0, int dbColumn = 0,
String dataName = "", String sortDataName = "",
DataValue.DataType dataType = DataValue.DataType.String, ColumnAlignment alignment = ColumnAlignment.Default)
{
this.Id = id;
this.Caption = caption;
this.Width = width;
this.DbColumn = dbColumn;
this.DataName = dataName;
this.SortDataName = sortDataName;
this.DataType = dataType;
this.Alignment = alignment;
}
//-------------------------------------------------------------------
// Properties.
public int Id { get; set; } = 0;
/// <summary>Database column identity (DbDfn.ColumnIdx etc).</summary>
public int DbColumn { get; set; } = 0;
/// <summary>Column code - eg. for configuration options.</summary>
public String Code { get; set; } = String.Empty;
/// <summary>Column header text.</summary>
public String Caption { get; set; } = String.Empty;
/// <summary>Column width (device dependent).</summary>
public int Width { get; set; } = 0;
public ColumnAlignment Alignment { get; set; } = ColumnAlignment.Default;
/// <summary>Data value name (may be an alias).</summary>
public String DataName { get; set; } = String.Empty;
public byte NumDecimals { get; set; } = 2;
public char NumThousands { get; set; } = ',';
/// <summary>Default: Zero values hidden.</summary>
public bool ShowZeroValues { get; set; } = false;
public ColumnBoolFormat BoolFormat { get; set; } = ColumnBoolFormat.YorN;
public ColumnTriStateFormat TriStateFormat { get; set; } = ColumnTriStateFormat.RAG;
/// <summary>Null date - values on or before are considered invalid.</summary>
public DateTime NullDate { get; set; } = DateTime.MinValue;
public String DateFormat { get; set; } = "d-MMM-yy";
public String DateTimeSeparator { get; set; } = " ";
public String TimeFormat { get; set; } = "HH:mm";
public DataValue.DataType DataType { get; set; } = DataValue.DataType.String;
public bool Resizable { get; set; } = true;
public bool Hidden { get; set; } = false;
public bool Frozen { get; set; } = false;
// Drop-down configuration.
public List<CodeDesc> Values { get; } = new List<CodeDesc>();
/// <summary>User must pick from the supplied list (default: true). Non-exclusive lists (editable) have same value and display members.</summary>
public bool ExclusiveValues { get; set; } = true;
/// <summary>User entered values (Exclusive=false) are converted if corresponding value found in list.</summary>
public bool MatchCase { get; set; } = true;
// Edit configuration:
public int MaxLength { get; set; } = 0;
public bool ReadOnly { get; set; } = false;
public bool Uppercase { get; set; } = false;
public bool Lowercase { get; set; } = false;
public bool Numeric { get; set; } = false;
public bool Password { get; set; } = false;
// Sort attributes.
/// <summary>Sort mode. Default: 'On'.</summary>
public ColumnSortMode SortMode { get; set; } = ColumnSortMode.On;
/// <summary>Data value name for sort (fully qualified).</summary>
public String SortDataName { get; set; } = String.Empty;
public bool SortAscending { get; set; } = true;
//-------------------------------------------------------------------
// Population.
public void Set
(String caption, int width, int dbColumn = 0,
String dataName = "", String sortDataName = "",
DataValue.DataType dataType = DataValue.DataType.String, ColumnAlignment alignment = ColumnAlignment.Default)
{
this.Caption = caption;
this.Width = width;
this.DbColumn = dbColumn;
this.DataName = dataName;
this.SortDataName = sortDataName;
this.DataType = dataType;
this.Alignment = alignment;
}
//-------------------------------------------------------------------
// Formatting.
public String FormatValue (NamedValue.Map values)
{
String value = String.Empty;
if (DataName.IsEmpty()) return value;
DataValue dataValue = values.GetValue(DataName);
if (dataValue == null || !dataValue.IsValid()) return value;
return FormatValue(dataValue);
}
public String FormatValue (DataValue dataValue)
{
String value = String.Empty;
if (dataValue == null || !dataValue.IsValid()) return value;
switch (DataType)
{
case DataValue.DataType.Date:
if (dataValue.IsDate())
{
DateTime date = dataValue.AsDate();
if (DateUtil.IsValid(date, NullDate))
value = date.Format(DateFormat);
}
else value = dataValue.ToString();
break;
case DataValue.DataType.Time:
if (dataValue.IsTime())
{
DateTime time = dataValue.AsTime();
if (DateUtil.IsValid(time, NullDate))
value = time.Format(TimeFormat);
}
else value = dataValue.ToString();
break;
case DataValue.DataType.DateTime:
if (dataValue.IsDateTime())
{
DateTime dateTime = dataValue.AsDateTime();
if (DateUtil.IsValid(dateTime, NullDate))
value = dateTime.Format(DateFormat + DateTimeSeparator + TimeFormat);
}
else value = dataValue.ToString();
break;
case DataValue.DataType.Long:
if (dataValue.IsNumeric())
{
long longValue = 0;
if (dataValue.GetDataType() == DataValue.DataType.Long) longValue = dataValue.AsLong();
else longValue = (long)dataValue.ToNumeric();
if (longValue != 0 || ShowZeroValues)
value = longValue.ToString();
}
else value = dataValue.ToString();
break;
case DataValue.DataType.Double:
if (dataValue.IsNumeric())
{
double doubleValue = dataValue.ToNumeric();
if (doubleValue != 0.0 || ShowZeroValues)
value = StringUtil.FormatNumeric(doubleValue, NumDecimals, 0, NumThousands);
}
else value = dataValue.ToString();
break;
case DataValue.DataType.Decimal:
if (dataValue.IsNumeric())
{
decimal decimalValue = 0.0m;
if (dataValue.GetDataType() == DataValue.DataType.Decimal) decimalValue = dataValue.AsDecimal();
else decimalValue = (decimal)dataValue.ToNumeric();
if (decimalValue != 0.0m || ShowZeroValues)
value = StringUtil.FormatNumeric(decimalValue, NumDecimals, 0, NumThousands);
}
else value = dataValue.ToString();
break;
case DataValue.DataType.Bool:
switch (BoolFormat)
{
case ColumnBoolFormat.YorN:
value = (dataValue.ToBool() ? "Y" : "N");
break;
case ColumnBoolFormat.Y:
value = (dataValue.ToBool() ? "Y" : "");
break;
case ColumnBoolFormat.N:
value = (dataValue.ToBool() ? "" : "N");
break;
case ColumnBoolFormat.TrueFalse:
value = (dataValue.ToBool() ? "True" : "False");
break;
default:
value = (dataValue.ToBool() ? "1" : "0");
break;
}
break;
case DataValue.DataType.TriState:
switch (TriStateFormat)
{
case ColumnTriStateFormat.RAG:
if (dataValue.AsTriState() == DataValue.TriStateType.Red)
value = "R";
else if (dataValue.AsTriState() == DataValue.TriStateType.Amber)
value = "A";
else if (dataValue.AsTriState() == DataValue.TriStateType.Green)
value = "G";
else if (dataValue.AsTriState() == DataValue.TriStateType.Blue)
value = "B";
else if (dataValue.AsTriState() == DataValue.TriStateType.Purple)
value = "P";
else if (dataValue.AsTriState() == DataValue.TriStateType.Yellow)
value = "Y";
else if (dataValue.AsTriState() == DataValue.TriStateType.Unknown)
value = "";
break;
default:
if (dataValue.AsTriState() == DataValue.TriStateType.Red)
value = "1";
else if (dataValue.AsTriState() == DataValue.TriStateType.Amber)
value = "2";
else if (dataValue.AsTriState() == DataValue.TriStateType.Green)
value = "3";
else if (dataValue.AsTriState() == DataValue.TriStateType.Blue)
value = "4";
else if (dataValue.AsTriState() == DataValue.TriStateType.Purple)
value = "5";
else if (dataValue.AsTriState() == DataValue.TriStateType.Yellow)
value = "6";
else if (dataValue.AsTriState() == DataValue.TriStateType.Unknown)
value = "";
break;
}
break;
default:
value = dataValue.ToString();
break;
}
return value.TrimEnd(' ');
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class ColumnDfnList
{
//-------------------------------------------------------------------
// C o n t a i n s
//-------------------------------------------------------------------
/// <summary>Determine whether list contains column with Id matching specified enum value (integer comparison).</summary>
public static bool HasId<T> (this List<ColumnDfn> values, T id) where T : struct, IConvertible
{
if (!(id is Enum))
throw new ArgumentException("HasId parameter must be an enumerated type. Type supplied: " + id.GetType().Name);
return values.HasId((int)(id as IConvertible));
}
/// <summary>Determine whether list contains column with Id matching specified integer value.</summary>
public static bool HasId (this List<ColumnDfn> values, int id)
{
foreach (var column in values)
if (column.Id == id) return true;
return false;
}
/// <summary>Determine whether list contains column with Db Column matching specified enum value (integer comparison).</summary>
public static bool HasDbColumn<T> (this List<ColumnDfn> values, T id) where T : struct, IConvertible
{
if (!(id is Enum))
throw new ArgumentException("HasDbColumn parameter must be an enumerated type. Type supplied: " + id.GetType().Name);
return values.HasDbColumn((int)(id as IConvertible));
}
/// <summary>Determine whether list contains column with Db Column matching specified integer value.</summary>
public static bool HasDbColumn (this List<ColumnDfn> values, int dbColumn)
{
foreach (var column in values)
if (column.DbColumn == dbColumn) return true;
return false;
}
//-------------------------------------------------------------------
// Columns in list.
public static int GetPosForId<T> (this List<ColumnDfn> values, T id) where T : struct, IConvertible
{
if (!(id is Enum))
throw new ArgumentException("HasId parameter must be an enumerated type. Type supplied: " + id.GetType().Name);
return values.GetPosForId((int)(id as IConvertible));
}
public static int GetPosForId (this List<ColumnDfn> values, int id)
{
for (int pos = 0; pos < values.Count; pos++)
if (values[pos].Id == id) return pos + 1;
return 0;
}
public static List<ColumnDfn> GetColumnsWithNonZeroWidth (this List<ColumnDfn> values)
{
return values.FindAll(item => item.Width > 0);
}
//-------------------------------------------------------------------
// C o l u m n W i d t h s
//-------------------------------------------------------------------
// Combined width of all columns.
/// <summary>Get the width of all non-zero width columns separated by the specified spacing</summary>
public static int GetFullWidth (this List<ColumnDfn> values, int columnSpacing)
{
List<ColumnDfn> columnsNonZeroWidth = values.GetColumnsWithNonZeroWidth();
if (columnsNonZeroWidth.Count < 1) return 0;
int width = 0;
foreach (var column in values)
width += column.Width;
return width + (columnSpacing * (columnsNonZeroWidth.Count - 1));
}
// Column widths are adjusted to keep their relative width constant.
/// <summary>Expand/contract non-zero width columns to fill the specified width with the specified space in between each.</summary>
public static bool FillWidth (this List<ColumnDfn> values, long width, int columnSpacing)
{
List<ColumnDfn> columnsNonZeroWidth = values.GetColumnsWithNonZeroWidth();
if (columnsNonZeroWidth.Count < 1) return false;
long fullWidthNoSpacing = values.GetFullWidth(0);
if (fullWidthNoSpacing < 1) return false;
double columnWidthFactor = ((double)width - (columnSpacing * (columnsNonZeroWidth.Count - 1))) / fullWidthNoSpacing;
foreach (ColumnDfn column in columnsNonZeroWidth)
column.Width = Math.Max(1, (int)Math.Ceiling(column.Width * columnWidthFactor));
return true;
}
/// <summary>Calculate the left position for each non-zero width column with the specified spacing.</summary>
public static int[] GetColumnLeftPositions (this List<ColumnDfn> values, int leftPosition, int columnSpacing)
{
List<ColumnDfn> columnsNonZeroWidth = values.GetColumnsWithNonZeroWidth();
if (columnsNonZeroWidth.Count < 1) return new int[0];
int[] columnPositions = new int[columnsNonZeroWidth.Count];
int columnLeftPos = leftPosition;
for (int columnIdx = 0; columnIdx < columnsNonZeroWidth.Count; columnIdx++)
{
ColumnDfn column = columnsNonZeroWidth[columnIdx];
columnPositions[columnIdx] = columnLeftPos;
columnLeftPos += column.Width + columnSpacing;
}
return columnPositions;
}
/// <summary>
/// Calculate the horizontal print position for each non-zero width column with the specified spacing.
/// <para>NB Print position will take alignment (left/centre/right) into consideration.</para>
/// </summary>
public static int[] GetColumnPrintPositions (this List<ColumnDfn> values, int leftPosition, int columnSpacing)
{
List<ColumnDfn> columnsNonZeroWidth = values.GetColumnsWithNonZeroWidth();
if (columnsNonZeroWidth.Count < 1) return new int[0];
int[] columnPositions = new int[columnsNonZeroWidth.Count];
int columnLeftPos = leftPosition;
for (int columnIdx = 0; columnIdx < columnsNonZeroWidth.Count; columnIdx++)
{
ColumnDfn column = columnsNonZeroWidth[columnIdx];
int columnPos = columnLeftPos;
if (column.Alignment == ColumnAlignment.Center)
columnPos = columnLeftPos + (column.Width / 2);
else if (column.Alignment == ColumnAlignment.Right)
columnPos = columnLeftPos + column.Width;
columnPositions[columnIdx] = columnPos;
columnLeftPos += column.Width + columnSpacing;
}
return columnPositions;
}
//-------------------------------------------------------------------
// E x t r a c t C o n t e n t s
//-------------------------------------------------------------------
/// <summary>Compile list of captions from the columns.</summary>
public static List<String> GetCaptions (this List<ColumnDfn> values)
{
List<String> captions = new List<String>();
foreach (ColumnDfn column in values)
captions.Add(StringUtil.EmptyOnNull(column.Caption));
return captions;
}
/// <summary>
/// Convert column list to strings representing sort columns.
/// <para>NB Column name is populated as DataName and may not be the same as used in a database query in all cases.</para>
/// </summary>
public static List<String> GetSortColumns (this List<ColumnDfn> values)
{
List<String> sortColumns = new List<String>();
foreach (ColumnDfn column in values)
sortColumns.Add(column.DataName + (column.SortAscending ? " ASC" : " DESC"));
return sortColumns;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+120
View File
@@ -0,0 +1,120 @@
using Sarand.Net.Standard.Base.Core;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sarand.Net.Standard.Base.Bus
{
//=======================================================================
public class DataItem
{
//-------------------------------------------------------------------
// Data members.
private readonly NamedValue.Map values = new NamedValue.Map();
private readonly List<DataItem> childItems = new List<DataItem>();
//-------------------------------------------------------------------
// Construction.
public DataItem (int itemType = 0)
{
this.ItemType = itemType;
}
public DataItem (NamedValue.Map values, int itemType = 0)
{
this.ItemType = itemType;
}
//-------------------------------------------------------------------
// Properties.
public int ItemType { get; }
//-------------------------------------------------------------------
// Values.
public bool HasValue (String name)
{
return values.HasValue(name);
}
public DataValue.DataType GetValueType (String name)
{
return GetValue(name)?.GetDataType() ?? DataValue.DataType.Unknown;
}
public DataValue GetValue (String name)
{
return values.GetValue(name);
}
public NamedValue.Map GetValues ()
{
return new NamedValue.Map(values);
}
public bool SetValue (String name, DataValue value)
{
values.AddValue(name, value);
return HasValue(name);
}
public bool SetValue (NamedValue value)
{
return SetValue(value.Name, value.Value);
}
public bool SetValues (NamedValue.Map values)
{
return this.values.AddValues(values);
}
public bool SetValues (ICollection<NamedValue> values)
{
return this.values.AddValues(values);
}
public bool RenameValue (String nameOld, String nameNew)
{
return values.RenameValue(nameOld, nameNew);
}
//-------------------------------------------------------------------
// Child items.
public long GetChildItemCount ()
{
return childItems.Count;
}
public DataItem GetChildItemByPos (int itemIdx)
{
if (itemIdx <= childItems.Count)
return childItems[itemIdx];
return null;
}
public DataItem GetChildItemByType (int itemType)
{
return childItems.Find(item => item.ItemType == itemType);
}
public List<DataItem> GetChildItemsByType (int itemType)
{
List<DataItem> items = new List<DataItem>();
foreach (DataItem item in childItems)
{
if (item != null && item.ItemType == itemType)
items.Add(item);
}
return items;
}
// Update child items.
public bool ClearChildItems ()
{
childItems.Clear();
return true;
}
public bool AddChildItem (DataItem item)
{
if (item != null)
childItems.Add(item);
return item != null;
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class SacsDataItemList
{
}
//=======================================================================
}
+449
View File
@@ -0,0 +1,449 @@
using System;
using System.Collections.Generic;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
public class DataValue
{
//-------------------------------------------------------------------
// Definitions.
public enum DataType { Unknown = 0, Bool, TriState, Long, Double, Decimal, String, Date, Time, DateTime, Blob };
public enum TriStateType { Unknown = 0, Red, Amber, Green, Blue, Purple, Yellow };
//-------------------------------------------------------------------
// Data members.
private DataType dataType;
private Object value;
//-------------------------------------------------------------------
// Construction.
public DataValue () { }
public DataValue (DataValue dataValue)
{
if (dataValue != null)
{
dataType = dataValue.GetDataType();
value = dataValue.value;
}
}
public DataValue (bool value)
{
this.value = value;
this.dataType = DataType.Bool;
}
public DataValue (TriStateType value)
{
this.value = value;
this.dataType = DataType.TriState;
}
public DataValue (long value)
{
this.value = value;
this.dataType = DataType.Long;
}
public DataValue (double value)
{
this.value = value;
this.dataType = DataType.Double;
}
public DataValue (decimal value)
{
this.value = value;
this.dataType = DataType.Decimal;
}
public DataValue (String value)
{
this.value = value;
this.dataType = DataType.String;
}
public DataValue (DateTime value, DataType type = DataType.DateTime)
{
this.value = value;
switch (type)
{
case DataType.Date:
case DataType.Time:
case DataType.DateTime:
this.dataType = type;
break;
default:
this.dataType = DataType.DateTime;
break;
}
}
public DataValue (Byte[] value)
{
this.value = value;
this.dataType = DataType.Blob;
}
//-------------------------------------------------------------------
// Properties.
public static readonly DataValue NullValue = new DataValue();
public DataType GetDataType ()
{
return dataType;
}
public static String GetTypeDesc (DataType type)
{
// Converts type to string.
switch (type)
{
case DataType.Bool: return "Bool";
case DataType.TriState: return "TriState";
case DataType.Long: return "Long";
case DataType.Double: return "Double";
case DataType.Decimal: return "Decimal";
case DataType.String: return "String";
case DataType.Date: return "Date";
case DataType.Time: return "Time";
case DataType.DateTime: return "DateTime";
case DataType.Blob: return "Blob";
}
return "<Unknown>";
}
public String GetTypeDesc ()
{
return GetTypeDesc(dataType);
}
public bool IsValid ()
{
return (dataType != DataType.Unknown);
}
public bool IsNumeric ()
{
return (dataType == DataType.Long || dataType == DataType.Double || dataType == DataType.Decimal);
}
public bool IsDate ()
{
return (dataType == DataType.Date || dataType == DataType.DateTime);
}
public bool IsTime ()
{
return (dataType == DataType.Time || dataType == DataType.DateTime);
}
public bool IsDateTime ()
{
return (dataType == DataType.Date || dataType == DataType.Time || dataType == DataType.DateTime);
}
public bool IsBlob ()
{
return (dataType == DataType.Blob);
}
//-------------------------------------------------------------------
// Conversion.
public override String ToString ()
{
if (value == null) return String.Empty;
// Forces any data type to string.
switch (dataType)
{
case DataType.Bool: return ((bool)value ? "Y" : "N");
case DataType.TriState: return ((TriStateType)value).ToString();
case DataType.Long: return ((long)value).ToString();
case DataType.Double: return ((double)value).ToString();
case DataType.Decimal: return ((decimal)value).ToString();
case DataType.String: return (String)value;
case DataType.Date: return ((DateTime)value).ToString("yyyyMMdd");
case DataType.Time: return ((DateTime)value).ToString("HH:mm:ss");
case DataType.DateTime: return ((DateTime)value).ToString("yyyyMMddHHmmss");
}
return String.Empty;
}
public double ToNumeric ()
{
if (value == null) return 0.0;
// Convert any type to double numeric.
switch (dataType)
{
case DataType.Bool: return ((bool)value ? 1 : 0);
case DataType.TriState: return (int)(TriStateType)value;
case DataType.Long: return (long)value;
case DataType.Double: return (double)value;
case DataType.Decimal: return (double)(decimal)value;
case DataType.String: return StringUtil.ToDouble((String)value);
case DataType.Date: return ((DateTime)value).Ticks;
case DataType.Time: return ((DateTime)value).Ticks;
case DataType.DateTime: return ((DateTime)value).Ticks;
}
return 0.0;
}
public bool ToBool ()
{
if (value == null) return false;
// Numeric <> 0, String: 'Yes', 'True', numeric value.
switch (dataType)
{
case DataType.Bool: return (bool)value;
case DataType.TriState: return (TriStateType)value != TriStateType.Unknown;
case DataType.Long: return (long)value != 0;
case DataType.Double: return (double)value != 0.0;
case DataType.Decimal: return (decimal)value != 0.0m;
case DataType.String: return ((String)value).ToBool();
case DataType.Date: return ((DateTime)value).Ticks != 0;
case DataType.Time: return ((DateTime)value).Ticks != 0;
case DataType.DateTime: return ((DateTime)value).Ticks != 0;
}
return false;
}
public bool AsBool ()
{
// Convert to specific type of data - fails if type is not correct:
if (dataType == DataType.Bool) return (bool)value;
if (dataType == DataType.Unknown) return false;
#if DEBUG
SaLogger.Debug(GetType().Name + ".AsBool Incorrect data type: " + dataType.ToString());
#endif
return false;
}
public TriStateType AsTriState ()
{
if (dataType == DataType.TriState) return (TriStateType)value;
if (dataType == DataType.Unknown) return TriStateType.Unknown;
if (SacsSystem.DebugMode) SaLogger.Debug(GetType().Name + ".AsTriState Incorrect data type: " + dataType.ToString());
return TriStateType.Unknown;
}
public long AsLong ()
{
if (dataType == DataType.Long) return (long)value;
if (dataType == DataType.Unknown) return 0;
if (SacsSystem.DebugMode) SaLogger.Debug(GetType().Name + ".AsLong Incorrect data type: " + dataType.ToString());
return 0;
}
public double AsDouble ()
{
if (dataType == DataType.Double) return (double)value;
if (dataType == DataType.Unknown) return 0.0;
// Aug-2018 Temporarily allow decimal values to be returned as double.
if (dataType == DataType.Decimal)
{
SaLogger.Warn(GetType().Name + ".AsDouble Data type: " + dataType.ToString());
return (double)(decimal)value;
}
if (SacsSystem.DebugMode) SaLogger.Debug(GetType().Name + ".AsDouble Incorrect data type: " + dataType.ToString());
return 0.0;
}
public decimal AsDecimal ()
{
if (dataType == DataType.Decimal) return (decimal)value;
if (dataType == DataType.Unknown) return 0.00m;
if (SacsSystem.DebugMode) SaLogger.Debug(GetType().Name + ".AsDecimal Incorrect data type: " + dataType.ToString());
return 0.00m;
}
public String AsString ()
{
if (dataType == DataType.String) return (String)value;
if (dataType == DataType.Unknown) return "";
if (SacsSystem.DebugMode) SaLogger.Debug(GetType().Name + ".AsString Incorrect data type: " + dataType.ToString());
return "";
}
public DateTime AsDateTime ()
{
// NB DateTime.Ticks is NotFiniteNumberException of 100-nanosecond intervals since beginning of 1st January 0001.
if (dataType == DataType.Date || dataType == DataType.Time || dataType == DataType.DateTime) return (DateTime)value;
if (dataType == DataType.Unknown) return DateTime.MinValue;
if (SacsSystem.DebugMode) SaLogger.Debug(GetType().Name + ".AsDateTime Incorrect data type: " + dataType.ToString());
return DateTime.MinValue;
}
public DateTime AsDate ()
{
if (dataType == DataType.Date || dataType == DataType.DateTime) return ((DateTime)value).Date;
if (dataType == DataType.Unknown) return DateTime.MinValue;
if (SacsSystem.DebugMode) SaLogger.Debug(GetType().Name + ".AsDate Incorrect data type: " + dataType.ToString());
return DateTime.MinValue;
}
public DateTime AsTime ()
{
if (dataType == DataType.Time || dataType == DataType.DateTime) return (DateTime)value;
if (dataType == DataType.Unknown) return DateTime.MinValue;
if (SacsSystem.DebugMode) SaLogger.Debug(GetType().Name + ".AsTime Incorrect data type: " + dataType.ToString());
return DateTime.MinValue;
}
public Byte[] AsBlob ()
{
if (dataType == DataType.Blob) return (Byte[])value;
if (dataType == DataType.Unknown) return null;
if (SacsSystem.DebugMode) SaLogger.Debug(GetType().Name + ".AsBlob Incorrect data type: " + dataType.ToString());
return null;
}
//-------------------------------------------------------------------
// Comparison.
public bool Equals (DataValue value)
{
return false;
}
public int CompareTo (DataValue value)
{
return -1;
}
//-------------------------------------------------------------------
// Utilities.
public void Clear ()
{
dataType = DataType.Unknown;
value = null;
}
//-------------------------------------------------------------------
// Arithmeic.
public void Increment (DataValue value)
{
if (dataType == value.GetDataType())
{
switch (dataType)
{
case DataType.Long:
this.value = (long)(this.value) + value.AsLong();
break;
case DataType.Double:
this.value = (double)(this.value) + value.AsDouble();
break;
case DataType.Decimal:
this.value = (decimal)(this.value) + value.AsDecimal();
break;
}
}
}
public void Decrement (DataValue value)
{
if (dataType == value.GetDataType())
{
switch (dataType)
{
case DataType.Long:
this.value = (long)(this.value) - value.AsLong();
break;
case DataType.Double:
this.value = (double)(this.value) - value.AsDouble();
break;
case DataType.Decimal:
this.value = (decimal)(this.value) - value.AsDecimal();
break;
}
}
}
public void MultiplyBy (DataValue value)
{
if (dataType == value.GetDataType())
{
switch (dataType)
{
case DataType.Long:
this.value = (long)(this.value) * value.AsLong();
break;
case DataType.Double:
this.value = (double)(this.value) * value.AsDouble();
break;
case DataType.Decimal:
this.value = (decimal)(this.value) * value.AsDecimal();
break;
}
}
}
public void DivideBy (DataValue value)
{
if (dataType == value.GetDataType())
{
switch (dataType)
{
case DataType.Long:
this.value = (long)(this.value) / value.AsLong();
break;
case DataType.Double:
this.value = (double)(this.value) / value.AsDouble();
break;
case DataType.Decimal:
this.value = (decimal)(this.value) / value.AsDecimal();
break;
}
}
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class DataValueList
{
//-------------------------------------------------------------------
// Conversion.
/// <summary>Convert all data values to strings.</summary>
public static List<String> ToStringList (this List<DataValue> values)
{
List<String> stringValues = new List<String>();
foreach (var dataValue in values)
stringValues.Add(values.ToString());
return stringValues;
}
/// <summary>
/// Convert the data values to raw objects.
/// <para>eg. For population to Excel spreadsheet cells.</para>
/// </summary>
public static List<Object> ToObjectList (this List<DataValue> values)
{
List<Object> cellValues = new List<Object>();
foreach (DataValue value in values)
{
if (value.GetDataType() == DataValue.DataType.Bool)
cellValues.Add(value.AsBool());
else if (value.GetDataType() == DataValue.DataType.Long)
cellValues.Add(value.AsLong());
else if (value.GetDataType() == DataValue.DataType.Double)
cellValues.Add(value.AsDouble());
else if (value.GetDataType() == DataValue.DataType.Decimal)
cellValues.Add(value.AsDecimal());
else if (value.IsDateTime())
cellValues.Add(value.AsDateTime());
else
// Default - String value.
cellValues.Add(value.ToString());
}
return cellValues;
}
//-------------------------------------------------------------------
// Numeric - only with compatible numeric values.
/// <summary>Minimum numeric value in the list.</summary>
public static DataValue GetMinValue (this List<DataValue> values)
{
DataValue minValue = null;
foreach (var dataValue in values)
{
if (dataValue.IsNumeric())
{
if (minValue == null)
minValue = new DataValue(dataValue.ToNumeric());
else if (dataValue.ToNumeric() < minValue.ToNumeric())
minValue = new DataValue(dataValue.ToNumeric());
}
}
return minValue;
}
/// <summary>Maximum numeric value in the list.</summary>
public static DataValue GetMaxValue (this List<DataValue> values)
{
DataValue maxValue = null;
foreach (var dataValue in values)
{
if (dataValue.IsNumeric())
{
if (maxValue == null)
maxValue = new DataValue(dataValue.ToNumeric());
else if (dataValue.ToNumeric() > maxValue.ToNumeric())
maxValue = new DataValue(dataValue.ToNumeric());
}
}
return maxValue;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,59 @@
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
public class ElapsedTimeLogger
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public ElapsedTimeLogger (String logPrefix, String logSuffix = "")
{
LogPrefix = logPrefix;
LogSuffix = logSuffix;
StartedAt = DateTime.Now;
}
//-------------------------------------------------------------------
// Properties.
public String LogPrefix { get; }
public String LogSuffix { get; }
public DateTime StartedAt { get; private set; }
//-------------------------------------------------------------------
// Utilities.
public void Restart ()
{
StartedAt = DateTime.Now;
}
//-------------------------------------------------------------------
// Logging.
public void LogElapsedSecs (bool restart = false)
{
LogElapsedSecs("", restart);
}
public void LogElapsedSecs (String logSuffix, bool restart = false)
{
LogElapsedSecs("", logSuffix, restart);
}
public void LogElapsedSecs (String logPrefix, String logSuffix, bool restart = false)
{
logPrefix = StringUtil.OnEmpty(logPrefix, LogPrefix);
logSuffix = StringUtil.OnEmpty(logSuffix, LogSuffix);
TimeSpan elapsed = DateTime.Now - StartedAt;
SaLogger.Info(StringUtil.TrimAll(logPrefix + " Elapsed: " + elapsed.TotalSeconds.ToString("0.000") + " secs. " + logSuffix));
if (restart) Restart();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+42
View File
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
public class FlagDfn
{
//---------------------------------------------------------------
// Construction.
public FlagDfn (Byte flagPos, String caption, String columnHeading)
{
FlagPos = flagPos;
Caption = caption;
ColumnHeading = columnHeading;
}
//---------------------------------------------------------------
// Properties.
public Byte FlagPos { get; private set; } = 0;
public String Caption { get; private set; } = "";
public String ColumnHeading { get; private set; } = "";
//---------------------------------------------------------------
}
//=======================================================================
public static class FlagDfnList
{
public static void Add (this List<FlagDfn> values, Byte flagPos, String caption, String columnHeading)
{
values.Add(new FlagDfn(flagPos, caption, columnHeading));
}
public static FlagDfn GetFlagByPos (this List<FlagDfn> values, ushort flagPos)
{
foreach (FlagDfn flagDfn in values)
if (flagDfn.FlagPos == flagPos)
return flagDfn;
return null;
}
}
//=======================================================================
}
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
public interface ISacsMessageConsole
{
//-------------------------------------------------------------------
// Message Boxes.
/// <summary>Information message.</summary>
void ShowMessageInfo (String message, String title = "");
/// <summary>Warning message. Always returns false.</summary>
bool ShowMessageWarning (String message, String title = "");
/// <summary>Error message. Always returns false.</summary>
bool ShowMessageError (String message, String title = "");
/// <summary>Yes/No confirm message. Returns true if 'Yes' clicked.</summary>
bool ShowMessageConfirm (String message, String title = "");
//-------------------------------------------------------------------
}
//=======================================================================
}
+264
View File
@@ -0,0 +1,264 @@
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
public class NamedValue
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
private NamedValue (String name, DataValue value)
{
Name = StringUtil.EmptyOnNull(name);
Value = value ?? new DataValue();
}
//-------------------------------------------------------------------
// Properties.
public String Name { get; private set; }
public DataValue Value { get; private set; }
//-------------------------------------------------------------------
/// <summary>Associative array of named values, case insensitive.</summary>
public class Map
{
//---------------------------------------------------------------
// Data members.
private readonly Dictionary<String,DataValue> values = new Dictionary<String, DataValue>(StringComparer.OrdinalIgnoreCase);
//---------------------------------------------------------------
// Construction.
public Map (Dictionary<String, DataValue> values = null)
{
if (values != null)
this.values.AddRange(values);
}
public Map (NamedValue.Map values)
{
if (values != null)
this.values.AddRange(values.values);
}
//---------------------------------------------------------------
// Properties.
/// <summary>Optional identifier for the set of values.</summary>
public String Id { get; set; } = String.Empty;
/// <summary>Retrieve a copy of the value list.</summary>
public Dictionary<String, DataValue> Values { get { return new Dictionary<String, DataValue>(values); } }
/// <summary>Number of values in collection.</summary>
public int Count { get { return values.Count; } }
//---------------------------------------------------------------
// Update list.
public void Clear ()
{
values.Clear();
}
public bool AddValue (String name, DataValue value)
{
if (String.IsNullOrEmpty(name) || value == null)
{
if (SacsSystem.DebugMode) SaLogger.Debug(GetType().Name + ".AddValue Invalid named value. Name: " + StringUtil.EmptyOnNull(name) + ". Value: " + (value != null ? value.GetDataType().ToString() : "null"));
return false;
}
values[name] = value;
return true;
}
public bool AddValues (NamedValue.Map values)
{
return AddValues(values.ToNamedValueList());
}
public bool AddValues (ICollection<NamedValue> values)
{
bool addValues = true;
foreach (NamedValue value in values)
{
if (!AddValue(value.Name, value.Value))
addValues = false;
}
return addValues;
}
/// <summary>Remove the named value from the list if present.</summary>
public bool RemoveValue (String name)
{
if (!HasValue(name)) return false;
return values.Remove(name);
}
/// <summary>Change the name of a value in the list.</summary>
public bool RenameValue (String nameOld, String nameNew)
{
if (StringUtil.IsEmpty(nameOld) || !HasValue(nameOld) || StringUtil.IsEmpty(nameNew) || nameOld.EqualsCI(nameNew)) return false;
DataValue value = GetValue(nameOld);
RemoveValue(nameOld);
return AddValue(nameNew, value);
}
//---------------------------------------------------------------
/// <summary>Does the list include a value with the specified name.</summary>
public bool HasValue (String name)
{
return values.HasValue(name);
}
//---------------------------------------------------------------
/// <summary>Retrieve the value with the specified name.</summary>
public DataValue GetValue (String name)
{
return values.GetValue(name);
}
/// <summary>Retrieve the named value as a String.</summary>
public String GetString (String name, String defaultValue = "")
{
return values.GetString(name, defaultValue);
}
/// <summary>Retrieve the named value as a boolean.</summary>
public bool GetBool (String name, bool defaultValue = false)
{
return values.GetBool(name, defaultValue);
}
/// <summary>Retrieve the named value as a long integer.</summary>
public long GetLong (String name, long defaultValue = 0)
{
return values.GetLong(name, defaultValue);
}
/// <summary>Retrieve the named value as a double.</summary>
public double GetDouble (String name, double defaultValue = 0)
{
return values.GetDouble(name, defaultValue);
}
/// <summary>Retrieve the named value as a decimal.</summary>
public decimal GetDecimal (String name, decimal defaultValue = 0)
{
return values.GetDecimal(name, defaultValue);
}
/// <summary>Retrieve the named value as a DateTime.</summary>
public DateTime GetDateTime (String name)
{
return values.GetDateTime(name);
}
/// <summary>Retrieve the named value as a date.</summary>
public DateTime GetDate (String name)
{
return values.GetDate(name);
}
/// <summary>Retrieve the named value as a time.</summary>
public DateTime GetTime (String name)
{
return values.GetTime(name);
}
//---------------------------------------------------------------
// Conversion.
//---------------------------------------------------------------
public List<NamedValue> ToNamedValueList ()
{
List<NamedValue> values = new List<NamedValue>();
foreach (String name in this.values.Keys)
values.Add(new NamedValue(name, this.values.GetValue(name)));
return values;
}
/// <summary>Concatenate the values with the specified delimiters.</summary>
public String Concat (String entryDelim = ".", String valueDelim = "=")
{
return values.Concat(entryDelim, valueDelim);
}
//---------------------------------------------------------------
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class NamedValueDictionary
{
//-------------------------------------------------------------------
/// <summary>Does the list include a value with the specified name, case insensitive.</summary>
public static bool HasValue (this Dictionary<String, DataValue> values, String name)
{
if (String.IsNullOrEmpty(name)) return false;
return values.ContainsKey(name);
}
//-------------------------------------------------------------------
/// <summary>Retrieve the value with the specified name.</summary>
public static DataValue GetValue (this Dictionary<String, DataValue> values, String name)
{
if (StringUtil.IsEmpty(name)) return DataValue.NullValue;
if (values.ContainsKey(name))
return values[name];
return DataValue.NullValue;
}
/// <summary>Retrieve the named value as a String.</summary>
public static String GetString (this Dictionary<String, DataValue> values, String name, String defaultValue = "")
{
if (values.HasValue(name)) return values.GetValue(name).ToString();
return defaultValue;
}
/// <summary>Retrieve the named value as a boolean.</summary>
public static bool GetBool (this Dictionary<String, DataValue> values, String name, bool defaultValue = false)
{
if (values.HasValue(name)) return values.GetValue(name).AsBool();
return defaultValue;
}
/// <summary>Retrieve the named value as a long integer.</summary>
public static long GetLong (this Dictionary<String, DataValue> values, String name, long defaultValue = 0)
{
if (values.HasValue(name)) return values.GetValue(name).AsLong();
return defaultValue;
}
/// <summary>Retrieve the named value as a double.</summary>
public static double GetDouble (this Dictionary<String, DataValue> values, String name, double defaultValue = 0)
{
if (values.HasValue(name)) return values.GetValue(name).AsDouble();
return defaultValue;
}
/// <summary>Retrieve the named value as a decimal.</summary>
public static decimal GetDecimal (this Dictionary<String, DataValue> values, String name, decimal defaultValue = 0)
{
if (values.HasValue(name)) return values.GetValue(name).AsDecimal();
return defaultValue;
}
/// <summary>Retrieve the named value as a DateTime.</summary>
public static DateTime GetDateTime (this Dictionary<String, DataValue> values, String name)
{
if (values.HasValue(name)) return values.GetValue(name).AsDateTime();
return DateTime.MinValue;
}
/// <summary>Retrieve the named value as a date.</summary>
public static DateTime GetDate (this Dictionary<String, DataValue> values, String name)
{
if (values.HasValue(name)) return values.GetValue(name).AsDate();
return DateTime.MinValue;
}
/// <summary>Retrieve the named value as a time.</summary>
public static DateTime GetTime (this Dictionary<String, DataValue> values, String name)
{
if (values.HasValue(name)) return values.GetValue(name).AsTime();
return DateTime.MinValue;
}
//-------------------------------------------------------------------
// Conversion.
/// <summary>Concatenate the values with the specified delimiters.</summary>
public static String Concat (this Dictionary<String, DataValue> values, String entryDelim = ".", String valueDelim = "=")
{
List<String> dataValues = new List<string>();
foreach (String name in values.Keys)
dataValues.Add(name + valueDelim + values[name]);
return dataValues.Concat(entryDelim);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+133
View File
@@ -0,0 +1,133 @@
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.IO;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
public class Property
{
//-------------------------------------------------------------------
// Data members.
private String section = "", name = "", value = "";
//-------------------------------------------------------------------
// Construction.
public Property (String section, String name, String value)
{
this.section = section;
this.name = name;
this.value = value;
}
//-------------------------------------------------------------------
// Comparison.
public bool Matches (String section, String name)
{
return (StringUtil.EqualsCI(this.section, section) && StringUtil.EqualsCI(this.name, name));
}
//-------------------------------------------------------------------
// Attributes.
public String GetSection ()
{
return section;
}
public String GetName ()
{
return name;
}
public String GetValue ()
{
return value;
}
//-------------------------------------------------------------------
}
//=======================================================================
public class PropertySet
{
//-------------------------------------------------------------------
// Data members.
private List<Property> properties = new List<Property>();
//-------------------------------------------------------------------
// Construction.
public PropertySet ()
{
}
//-------------------------------------------------------------------
/// <summary>
/// Add property to set.
/// </summary>
public void AddProperty (String section, String name, String value)
{
properties.Add(new Property(section, name, value));
}
//-------------------------------------------------------------------
/// <summary>
/// Retrieve value from property set.
/// </summary>
public String GetPropertyValue (String section, String name)
{
foreach (var property in properties)
{
if (property.Matches(section, name))
return property.GetValue();
}
return "";
}
//-------------------------------------------------------------------
// Retrieve peoperties from file.
public static PropertySet GetPropertiesFromFile (String filepath)
{
if (StringUtil.IsEmpty(filepath))
{
SaLogger.Warn("PropertySet.GetPropertiesFromFile Blank file path specified");
return null;
}
if (!FileUtil.IsFile(filepath))
{
SaLogger.Warn("PropertySet.GetPropertiesFromFile Invalid file specified: '" + filepath + "'");
return null;
}
PropertySet propertySet = new PropertySet();
String section = "";
foreach (var row in FileUtil.ReadAllFileLines(filepath))
{
String data = row.Trim();
// Blank lines:
if (String.IsNullOrEmpty(data))
continue;
// Check for comment:
else if (data.Left(1) == ";" || data.Left(2) == "//")
continue;
// Check for [Section]:
else if (data[0] == '[')
{
section = data.Substring(1).Split(']')[0];
}
else
{
// Otherwise it is a named value:
propertySet.AddProperty(section, data.Split('=')[0], string.Join("=", data.Split('=').Skip(1).ToArray()));
}
}
return propertySet;
}
//-------------------------------------------------------------------
// Utilities.
public void Dump ()
{
System.Diagnostics.Debug.WriteLine("PropertySet dump:");
foreach (var property in properties)
{
string info = property.GetSection();
if (!String.IsNullOrEmpty(info)) info += ".";
info += property.GetName() + "=" + property.GetValue();
System.Diagnostics.Debug.WriteLine("\t" + info);
}
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+70
View File
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Input;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
/// <summary>
/// Code contract for commands - menu options, button actions, keyboard accelerators etc.
/// <para></para>
/// </summary>
public class SacsCommand : ICommand
{
//-------------------------------------------------------------------
// Definitions.
public delegate bool CanExecuteFn (object commandParam);
public delegate void ExecuteFn (object commandParam);
//-------------------------------------------------------------------
// Data members.
private readonly ExecuteFn executeFn;
private readonly CanExecuteFn canExecuteFn;
//-------------------------------------------------------------------
// Construction.
public SacsCommand (ExecuteFn executeFn, CanExecuteFn canExecuteFn = null)
{
this.executeFn = executeFn;
this.canExecuteFn = canExecuteFn;
}
//-------------------------------------------------------------------
// Properties.
public String CommandId { get; set; }
public String Caption { get; set; }
//-------------------------------------------------------------------
/// <summary>
/// CanExecuteChanged event handler.
/// <para>Unclear when this fires as Command won't know when state changes.</para>
/// </summary>
public event EventHandler CanExecuteChanged;
protected void FireCanExecuteChanged ()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
//-------------------------------------------------------------------
/// <summary>Determine whether this command can be executed.</summary>
public virtual bool CanExecute (object commandParam)
{
return canExecuteFn?.Invoke(commandParam) ?? false;
}
//-------------------------------------------------------------------
/// <summary>
/// Execute command.
/// <para>Only invokes command method if comand can be executed.</para>
/// </summary>
public virtual void Execute (object commandParam)
{
if (!CanExecute(commandParam)) return;
executeFn?.Invoke(commandParam);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+168
View File
@@ -0,0 +1,168 @@
using System;
namespace Sarand.Net.Standard.Base.Core
{
//=====================================================================
// System Code types (static lists).
// Defined by the underlying system and available for use throughout any application.
// Only maintainable by updates to the software, ie. not held on the database.
public enum SaSysCodeType
{
Unknown = 0,
//ItemType,
//ParamCategory,
//Priority,
MessageLevel,
InterfaceCategory,
InterfaceProcessDirection,
};
//=====================================================================
// Status.
public enum SaStatus
{
Unknown = 0,
Current = 10,
Redundant,
Superseded,
Discontinued,
Provisional,
Confirmed,
Enabled,
Disabled,
Created,
Updated,
Reinstated,
Deleted,
Required,
Awaiting,
Preparation,
Raised,
InProgress,
Active,
Inactive,
OnHold,
Imported,
Exported,
Open,
Closed,
Completed,
Invoiced,
Cancelled,
Successful,
Failed,
Actioned,
Authorised,
Accepted,
Rejected,
Submitted,
Queried,
Granted,
Denied,
Logged,
}
//=====================================================================
// Data Item Type.
public enum SaDataItemType
{
Unknown = 0,
// Application data types should start from 1.
// Base number for non-standard data types.
FirstNonStandard = 10000,
// Values are tied to the database - do not change.
CodeType = Int16.MaxValue - 80,
Code = CodeType + 1,
Language = Int16.MaxValue - 60,
Region = Language + 1,
Currency = Region + 1,
TaxCode = Currency + 1,
UnitMeasure = TaxCode + 1,
ItemOption = Int16.MaxValue - 40,
DiaryEntry = Int16.MaxValue - 30,
DiaryLink = DiaryEntry + 1,
Interface = Int16.MaxValue - 20,
SecurityItem = Int16.MaxValue - 10,
SecurityLink = Int16.MaxValue - 11,
};
//=====================================================================
public enum SaMessageLevel
{
Unknown = 0,
None = Unknown,
Fatal = 10,
Exception,
Error,
Warning,
Info,
Debug,
Trace,
}
//=====================================================================
// I n t e r f a c e s
//=====================================================================
// Interface category.
public enum SaInterfaceCategory
{
// Do not change - values are tied to database.
Unknown = 0,
Website = 10,
Accounts = 20, /* Recognised financial systems, eg. Sage Line 50, Quickbooks. */
System = 30,
File = 40,
};
// Interface direction.
public enum SaInterfaceProcessDirection
{
// Do not change - values are tied to database.
Unknown = 0,
Inbound = 10,
Outbound = 20,
Both = 30,
};
//=====================================================================
public static class Sacs
{
//-------------------------------------------------------------------
/// <summary>Mime types.</summary>
public enum MimeType
{
Unknown = 0,
// Data.
Text,
Csv,
Xml,
Zip,
Bin,
// Web.
Html,
Css,
Javascript,
Json,
// Images.
Png,
Jpeg,
Bmp,
Gif,
Tif,
// Documents.
Pdf,
// Text documents.
Rtf,
MsDoc,
MsDocx,
OpenDocText,
// Spreadsheets.
MsXls,
MsXlsx,
OpenDocSpreadsheet,
}
//-------------------------------------------------------------------
}
//=====================================================================
}
@@ -0,0 +1,52 @@
using System;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
public abstract class SacsDisposable : IDisposable
{
//-------------------------------------------------------------------
// Data memebrs.
private bool disposed = false;
//-------------------------------------------------------------------
// Construction.
protected SacsDisposable ()
{
}
//-------------------------------------------------------------------
// Disposal.
~SacsDisposable ()
{
Dispose(false);
}
public void Dispose ()
{
if (disposed) return;
Dispose(true);
disposed = true;
GC.SuppressFinalize(this);
}
protected abstract void Dispose (bool disposing);
//-------------------------------------------------------------------
// Properties.
public bool Disposed { get { return disposed; } }
//-------------------------------------------------------------------
// Utilities.
//-------------------------------------------------------------------
}
//=======================================================================
public static class DisposableUtil
{
//-------------------------------------------------------------------
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,22 @@
using Sarand.Net.Standard.Base.Utils;
using System;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
public class SacsErrorState
{
//-------------------------------------------------------------------
// Construction.
public SacsErrorState (string message)
{
Message = StringUtil.EmptyOnNull(message).ReplaceAll("\r\n","\n").ReplaceAll("\n"," ").TrimAll(new char[] { ' ', '\t', '\n', '\r' }, " ");
}
//-------------------------------------------------------------------
// Properties.
public String Message { get; private set; } = String.Empty;
//-------------------------------------------------------------------
}
//=======================================================================
}
+34
View File
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
public class SacsMessage
{
//-------------------------------------------------------------------
// Construction.
public SacsMessage (String message, SaMessageLevel messageLevel = SaMessageLevel.Info)
{
Message = message;
MessageLevel = messageLevel;
}
//-------------------------------------------------------------------
// Properties.
public SaMessageLevel MessageLevel { get; } = SaMessageLevel.Info;
public String Message { get; }
//-------------------------------------------------------------------
// Utilities.
/// <summary>Automatic conversion from string to info message.</summary>
public static implicit operator SacsMessage (String message)
{
return new SacsMessage(message);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,58 @@
using Sarand.Net.Standard.Base.Utils;
using System;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
/// <summary>State Item interface.</summary>
public interface ISacsStateItem
{
bool HasErrorState ();
SacsErrorState ErrorState { get; }
String ErrorStateMessage { get; }
bool SetErrorState (String message);
void ClearErrorState ();
}
//=======================================================================
/// <summary>
/// State Item - allows business component to record and report error message.
/// </summary>
public class SacsStateItem : ISacsStateItem
{
//-------------------------------------------------------------------
// Data items.
//-------------------------------------------------------------------
// Protected construction - use derived classes.
protected SacsStateItem () { }
//-------------------------------------------------------------------
// Poperties.
public SacsErrorState ErrorState { get; private set; }
public bool HasErrorState ()
{
return ErrorState != null;
}
public String ErrorStateMessage
{
get { return (ErrorState != null ? ErrorState.Message : String.Empty); }
}
// Update properties.
public virtual bool SetErrorState (String message)
{
if (StringUtil.IsEmpty(StringUtil.EmptyOnNull(message).Trim()))
throw new NotSupportedException(GetType().Name + ".SetErrorState Blank error message");
this.ErrorState = new SacsErrorState(message);
return false;
}
public void ClearErrorState ()
{
ErrorState = null;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,57 @@
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
/// <summary>
/// Stack (LIFO) with limit on length.
/// </summary>
/// <typeparam name="ITEM"></typeparam>
public class StackLimited<ITEM>
{
//-------------------------------------------------------------------
// Data members.
private int limit = 0;
private readonly LinkedList<ITEM> items = new LinkedList<ITEM>();
//-------------------------------------------------------------------
// Construction.
public StackLimited (int limit = 0)
{
this.limit = limit;
}
//-------------------------------------------------------------------
// Utilities.
public int Count
{
get { return items.Count; }
}
public void Clear ()
{
items.Clear();
}
//-------------------------------------------------------------------
/// <summary>
/// Add item to the stack and ensure limit isn't exceeded.
/// </summary>
/// <param name="item"></param>
public void Push (ITEM item)
{
items.AddFirst(item);
while (limit > 0 && items.Count > limit)
items.RemoveLast();
}
//-------------------------------------------------------------------
/// <summary>
/// Retrieve and remove last item added to the stack.
/// </summary>
/// <returns></returns>
public ITEM Pop ()
{
ITEM item = items.First.Value;
items.RemoveFirst();
return item;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+108
View File
@@ -0,0 +1,108 @@
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
public class StringPair : Tuple<String, String>
{
//-------------------------------------------------------------------
// Definitions.
public enum Value
{
Value1,
Value2,
}
//-------------------------------------------------------------------
// Construction.
public StringPair (String value1, String value2) :
base(StringUtil.EmptyOnNull(value1), StringUtil.EmptyOnNull(value2))
{
}
//-------------------------------------------------------------------
// Concatenation.
public String Concat (String delimiter, bool delimBlanks = true)
{
String value = StringUtil.EmptyOnNull(Item1);
if (!String.IsNullOrEmpty(Item2) || delimBlanks)
value += StringUtil.EmptyOnNull(delimiter);
value += StringUtil.EmptyOnNull(Item2);
return value;
}
//-------------------------------------------------------------------
// Operators.
public static implicit operator StringPair (String value1)
{
return new StringPair(value1, String.Empty);
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class StringPairList
{
//-------------------------------------------------------------------
public static List<StringPair> CreateFrom (List<String> valuesToAdd, bool duplicateAsS2 = false)
{
List<StringPair> values = new List<StringPair>();
foreach (String valueToAdd in valuesToAdd)
values.Add(new StringPair(valueToAdd, (duplicateAsS2 ? valueToAdd : "")));
return values;
}
public static List<StringPair> CreateFrom (List<String> s1Values, List<String> s2Values)
{
List<StringPair> values = new List<StringPair>();
for (int valueIdx = 0; valueIdx < (Math.Min(s1Values.Count,s2Values.Count)); valueIdx++)
{
values.Add(new StringPair(s1Values[valueIdx], s2Values[valueIdx]));
}
return values;
}
//-------------------------------------------------------------------
public static void Add (this List<StringPair> values, String s1, String s2 = "")
{
values.Add(new StringPair(s1, s2));
}
public static void AddRange (this List<StringPair> values, List<String> valuesToAdd, bool duplicateAsS2 = false)
{
values.AddRange(CreateFrom(valuesToAdd, duplicateAsS2));
}
//-------------------------------------------------------------------
public static List<String> Concat (this List<StringPair> values, String delimiter, bool delimitBlanks = true)
{
List<String> data = new List<string>();
foreach (var pair in values)
{
String value = pair.Item1;
if (!String.IsNullOrEmpty(pair.Item2) || delimitBlanks)
value += delimiter;
value += pair.Item2;
data.Add(value);
}
return data;
}
//-------------------------------------------------------------------
public static List<String> GetValues (this List<StringPair> values, StringPair.Value valueType = StringPair.Value.Value1)
{
List<String> data = new List<string>();
foreach (var pair in values)
{
String value = pair.Item1;
if (valueType == StringPair.Value.Value2)
value = pair.Item2;
data.Add(value);
}
return data;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+213
View File
@@ -0,0 +1,213 @@
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.Core
{
//=======================================================================
public class TypeCodeDesc : CodeDesc
{
//-------------------------------------------------------------------
// Construction.
public TypeCodeDesc (int type, String code, String desc, String comment = "") :
base(code, desc, comment)
{
Type = type;
}
//-------------------------------------------------------------------
// Properties.
public int Type { get; }
//-------------------------------------------------------------------
}
//=======================================================================
public static class TypeCodeDescList
{
//-------------------------------------------------------------------
// P o p u l a t i o n
//-------------------------------------------------------------------
public static void Add (this List<TypeCodeDesc> values, int type, String code, string desc, string comment = "")
{
values.Add(new TypeCodeDesc(type, code, desc, comment));
}
// Generic version - to supply type as enum.
public static void Add<T> (this List<TypeCodeDesc> values, T type, String code, string desc, string comment = "") where T : struct, IConvertible
{
if (!(type is Enum))
throw new ArgumentException("TypeCodeDescList.Add Type parameter must be an enumerated type. Type supplied: " + type.GetType().Name);
values.Add(new TypeCodeDesc((int)(type as IConvertible), code, desc, comment));
}
public static void Insert (this List<TypeCodeDesc> values, int index, int type, String code, string desc, string comment = "")
{
values.Insert(index, new TypeCodeDesc(type, code, desc, comment));
}
// Generic version - to supply type as enum.
public static void Insert<T> (this List<TypeCodeDesc> values, int index, T type, String code, string desc, string comment = "") where T : struct, IConvertible
{
if (!(type is Enum))
throw new ArgumentException("TypeCodeDescList.Insert Type parameter must be an enumerated type. Type supplied: " + type.GetType().Name);
values.Insert(index, new TypeCodeDesc((int)(type as IConvertible), code, desc, comment));
}
//-------------------------------------------------------------------
// C o n t a i n s
//-------------------------------------------------------------------
[Obsolete("Deprecated - use ContainsType() instead.")]
public static bool FindType (this List<TypeCodeDesc> values, int type)
{
foreach (var value in values)
{
if (value.Type == type) return true;
}
return false;
}
/// <summary>Does the line include an entry for this type.</summary>
public static bool ContainsType (this List<TypeCodeDesc> values, int type)
{
foreach (var value in values)
{
if (value.Type == type) return true;
}
return false;
}
/// <summary>Does the line include an entry for this (enum) type.</summary>
public static bool ContainsType<T> (this List<TypeCodeDesc> values, T type) where T : struct, IConvertible
{
if (!(type is Enum))
throw new ArgumentException("TypeCodeDescList.FindType parameter must be an enumerated type. Type supplied: " + type.GetType().Name);
return values.ContainsType((int)(type as IConvertible));
}
//-------------------------------------------------------------------
[Obsolete("Deprecated - use ContainsCode() instead.")]
public static bool FindCode (this List<TypeCodeDesc> values, String code)
{
foreach (var value in values)
{
if (value.Code.EqualsCI(code)) return true;
}
return false;
}
/// <summary>Does the line include an entry for this case-insensitive code.</summary>
public static bool ContainsCode (this List<TypeCodeDesc> values, String code)
{
foreach (var value in values)
{
if (value.Code.EqualsCI(code)) return true;
}
return false;
}
//-------------------------------------------------------------------
// L o o k u p
//-------------------------------------------------------------------
/// <summary>Retrieve the entry associated with this type.</summary>
public static TypeCodeDesc GetEntryForType (this List<TypeCodeDesc> values, int type)
{
return values.Find(item => item.Type == type);
}
/// <summary>Retrieve the entry associated with this (enum) type.</summary>
public static TypeCodeDesc GetEntryForType<T> (this List<TypeCodeDesc> values, T type) where T : struct, IConvertible
{
if (!(type is Enum))
throw new ArgumentException("TypeCodeDescList.GetEntryForType parameter must be an enumerated type. Type supplied: " + type.GetType().Name);
return values.GetEntryForType((int)(type as IConvertible));
}
//-------------------------------------------------------------------
// T y p e L o o k u p
//-------------------------------------------------------------------
/// <summary>Retrieve the type associated with this case-insensitive code.</summary>
public static int GetTypeForCode (this List<TypeCodeDesc> values, String code, int defaultValue = 0, bool caseSensitive = false)
{
foreach (var value in values)
{
if (caseSensitive && value.Code.Equals(code)) return value.Type;
if (!caseSensitive && value.Code.EqualsCI(code)) return value.Type;
}
return defaultValue;
}
/// <summary>Retrieve the type associated with this case-insensitive description.</summary>
public static int GetTypeForDesc (this List<TypeCodeDesc> values, String desc, int defaultValue = 0, bool caseSensitive = false)
{
foreach (var value in values)
{
if (caseSensitive && value.Desc.Equals(desc)) return value.Type;
if (!caseSensitive && value.Desc.EqualsCI(desc)) return value.Type;
}
return defaultValue;
}
//-------------------------------------------------------------------
// C o d e L o o k u p
//-------------------------------------------------------------------
/// <summary>Retrieve the code associated with this type.</summary>
public static String GetCodeForType (this List<TypeCodeDesc> values, int type, String defaultValue = "")
{
return values.Find(item => item.Type == type)?.Code ?? defaultValue;
}
/// <summary>Retrieve the code associated with this (enum) type.</summary>
public static String GetCodeForType<T> (this List<TypeCodeDesc> values, T type, String defaultValue = "") where T : struct, IConvertible
{
if (!(type is Enum))
throw new ArgumentException("TypeCodeDescList.GetCodeForType parameter must be an enumerated type. Type supplied: " + type.GetType().Name);
return values.GetCodeForType((int)(type as IConvertible), defaultValue);
}
//-------------------------------------------------------------------
// D e s c r i p t i o n L o o k u p
//-------------------------------------------------------------------
/// <summary>Retrieve the description associated with this type.</summary>
public static String GetDescForType (this List<TypeCodeDesc> values, int type, String defaultValue = "")
{
return values.Find(item => item.Type == type)?.Desc ?? defaultValue;
}
/// <summary>Retrieve the description associated with this (enum) type.</summary>
public static String GetDescForType<T> (this List<TypeCodeDesc> values, T type, String defaultValue = "") where T : struct, IConvertible
{
if (!(type is Enum))
throw new ArgumentException("TypeCodeDescList.GetDescForType parameter must be an enumerated type. Type supplied: " + type.GetType().Name);
return values.GetDescForType((int)(type as IConvertible), defaultValue);
}
/// <summary>Retrieve the description associated with this case-insensitive code.</summary>
public static String GetDescForCode (this List<TypeCodeDesc> values, String code, String defaultValue = "")
{
foreach (var value in values)
if (value.Code.EqualsCI(code)) return value.Desc;
return defaultValue;
}
//-------------------------------------------------------------------
// C o d e D e s c r i p t i o n L o o k u p
//-------------------------------------------------------------------
/// <summary>Retrieve the code and description associated with this type.</summary>
public static CodeDesc GetCodeDescForType (this List<TypeCodeDesc> values, int type)
{
foreach (var value in values)
if (value.Type == type) return new CodeDesc(value.Code, value.Desc);
return null;
}
/// <summary>Retrieve the code and description associated with this (enum) type.</summary>
public static CodeDesc GetCodeDescForType<T> (this List<TypeCodeDesc> values, T type) where T : struct, IConvertible
{
if (!(type is Enum))
throw new ArgumentException("TypeCodeDescList.GetCodeDescForType parameter must be an enumerated type. Type supplied: " + type.GetType().Name);
return values.GetCodeDescForType((int)(type as IConvertible));
}
//-------------------------------------------------------------------
// C o n v e r s i o n
//-------------------------------------------------------------------
/// <summary>Convert to a code & description list.</summary>
public static List<CodeDesc> ToCodeDescList (this List<TypeCodeDesc> values)
{
List<CodeDesc> codeDescs = new List<CodeDesc>();
foreach (var value in values)
codeDescs.Add(value.Code, value.Desc);
return codeDescs;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,117 @@
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Sarand.Net.Standard.Base.Data
{
//=======================================================================
public static class SacsAesIvCrypter
{
//-------------------------------------------------------------------
// Properties.
public static String AesIvDelimiter { get; } = ";";
public static String AesIvPrefix { get; } = "AESIV:";
//-------------------------------------------------------------------
// Ref. https://xamarinhelp.com/cryptography-in-xamarin-forms/
// For reversible encryption, AES is generally the recommended standard.
// AES 256 requires a 32 byte key to encrypt.
// Either generate a 32 byte key or use any string and convert it into a key with CreateKey.
// NB The salt will remain the same here for each key generated - it does not need to be randomized as with hashing.
//-------------------------------------------------------------------
private static byte[] CreateKey (string password, int keyBytes = 32)
{
byte[] salt = new byte[] { 80, 70, 60, 50, 40, 30, 20, 10 };
int iterations = 300;
DeriveBytes keyGenerator = new Rfc2898DeriveBytes(password, salt, iterations);
return keyGenerator.GetBytes(keyBytes);
}
//-------------------------------------------------------------------
// Encrypt String.
// Return the encrypted text with the IV appended to the end.
// In AES, the IV is a randomizer, making sure that no two identical messages ever give the same encrypted output.
// The IV can be public and does not need to be hidden.
//-------------------------------------------------------------------
public static string Encrypt (this string clearValue, string encryptionKey)
{
using (Aes aes = Aes.Create())
{
aes.Key = CreateKey(encryptionKey);
byte[] aesIV = aes.IV;
byte[] encrypted = Encrypt(clearValue, aes.Key, aesIV);
return Convert.ToBase64String(encrypted) + AesIvDelimiter + Convert.ToBase64String(aesIV);
}
}
//-------------------------------------------------------------------
public static byte[] Encrypt (string plainText, byte[] key, byte[] iv)
{
if (plainText == null || plainText.Length <= 0) throw new ArgumentNullException($"{nameof(plainText)}");
if (key == null || key.Length <= 0) throw new ArgumentNullException($"{nameof(key)}");
if (iv == null || iv.Length <= 0) throw new ArgumentNullException($"{nameof(iv)}");
byte[] encrypted;
using (Aes aes = Aes.Create())
{
aes.Key = key;
aes.IV = iv;
using (MemoryStream memoryStream = new MemoryStream())
{
using (ICryptoTransform encryptor = aes.CreateEncryptor())
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
using (StreamWriter streamWriter = new StreamWriter(cryptoStream))
{
streamWriter.Write(plainText);
}
encrypted = memoryStream.ToArray();
}
}
return encrypted;
}
//-------------------------------------------------------------------
// Decrypt String.
// Decryption requires the encrypted value and the secret key.
// This function is directly related to the above encryption process and will
// assume that the IV is at the end of the string passed in.
// To decrypt a message encrypted by another function, the IV will have to be passed explicitly.
//-------------------------------------------------------------------
public static string Decrypt (this string encryptedValue, string encryptionKey)
{
String[] valueParts = encryptedValue.Split(new String[] { AesIvDelimiter }, StringSplitOptions.None);
if (valueParts.Length != 2)
{
SaLogger.Warn("SacsAesIvCrypter.Decrypt Missing IV - assuming not encrypted");
return encryptedValue;
//throw new CryptographicException("Decrypt failure: Missing IV");
}
return Decrypt(Convert.FromBase64String(valueParts[0]), CreateKey(encryptionKey), Convert.FromBase64String(valueParts[1]));
}
//-------------------------------------------------------------------
public static string Decrypt (byte[] cipherText, byte[] key, byte[] iv)
{
if (cipherText == null || cipherText.Length <= 0) throw new ArgumentNullException($"{nameof(cipherText)}");
if (key == null || key.Length <= 0) throw new ArgumentNullException($"{nameof(key)}");
if (iv == null || iv.Length <= 0) throw new ArgumentNullException($"{nameof(iv)}");
string plaintext = "";
using (Aes aes = Aes.Create())
{
aes.Key = key;
aes.IV = iv;
using (MemoryStream memoryStream = new MemoryStream(cipherText))
using (ICryptoTransform decryptor = aes.CreateDecryptor())
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
using (StreamReader streamReader = new StreamReader(cryptoStream))
plaintext = streamReader.ReadToEnd();
}
return plaintext;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sarand.Net.Standard.Base.Data
{
//=======================================================================
/// <summary>
/// Very basic CSV data handling.
/// <para>For more advanced CSV features, see SaCsvHelper in SacsWinOffice.</para>
/// </summary>
public static class SacsCsvHandler
{
//-------------------------------------------------------------------
// Properties.
//-------------------------------------------------------------------
public static String ToCsv (this IEnumerable<String> values, char fieldDelimiter = ',', char textDelimiter = '"')
{
String data = "";
int valueCount = 0;
foreach (String rawValue in values)
{
valueCount++;
String value = rawValue;
if (value.Contains(fieldDelimiter.ToString()) || value.Contains(textDelimiter.ToString()))
value = textDelimiter + value.Replace(textDelimiter.ToString(), textDelimiter.ToString() + textDelimiter.ToString()) + textDelimiter;
data += (valueCount > 1 ? fieldDelimiter.ToString() : "") + value;
}
return data;
}
//-------------------------------------------------------------------
/// <summary>
/// Extract comma delimited fields from data record.
/// <para>Fields starting with double quotes are terminated with the next single double quotes character.</para>
/// <para>Embedded double quotes pairs are treated as a single double quotes character.</para>
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static List<String> ParseCsvRecord (String data, char fieldDelimiter = ',', char textDelimiter = '"')
{
List<String> fields = new List<string>();
if (data == null) return fields;
// Parse control properties.
String currentField = "";
bool inTextField = false;
bool previousCharTextDelim = false;
// Parse the data.
foreach (char c in data)
{
if (inTextField)
{
if (c == textDelimiter)
{
// Text delimiter found.
if (!previousCharTextDelim)
{
// Make a note that a text delimiter has been found.
previousCharTextDelim = true;
continue;
}
else
{
// 2nd text delimiter found - add single text delimiter to field.
currentField += c;
previousCharTextDelim = false;
continue;
}
}
if (previousCharTextDelim)
{
// Non-text delimiter foudn after text delimiter - End of text field.
inTextField = false;
previousCharTextDelim = false;
if (c == fieldDelimiter)
{
// End of field.
fields.Add(currentField);
currentField = "";
continue;
}
continue;
}
currentField += c;
}
else
{
// Non-text field.
if (c == fieldDelimiter)
{
// End of field.
fields.Add(currentField);
currentField = "";
continue;
}
if (currentField.Length == 0 && c == textDelimiter)
{
// First character of field is text delimiter.
inTextField = true;
continue;
}
// Append character to field.
currentField += c;
}
}
fields.Add(currentField);
return fields;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+92
View File
@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Sarand.Net.Standard.Base.Data
{
//=======================================================================
public class SacsData
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
/// <summary>Construction - Empty data.</summary>
public SacsData () { }
/// <summary>Construction - Data from byte array.</summary>
public SacsData (byte[] data)
{
DataBuffer = new byte[data?.Length ?? 0];
DataLength = data?.Length ?? 0;
if (DataLength > 0)
Array.Copy(data, 0, DataBuffer, 0, DataLength);
CheckForNull();
}
/// <summary>
/// Construction - Data from string.
/// <para>Encoding: ASCII</para>
/// </summary>
public SacsData (string data) :
this(data, Encoding.ASCII)
{ }
/// <summary>Construction - Data from string with specific encoding.</summary>
public SacsData (string data, Encoding encoding) :
this(encoding.GetBytes(data))
{
StringEncoding = encoding;
}
/// <summary>Copy construction.</summary>
public SacsData (SacsData data) :
this(data.DataBuffer)
{
StringEncoding = data.StringEncoding;
}
//-------------------------------------------------------------------
// Properties.
/// <summary>Data buffer - cannot be replaced but individual entries can be amended.</summary>
public byte[] DataBuffer { get; protected set; } = new byte[0];
/// <summary>Length of data may be less than the buffer length.</summary>
public int DataLength { get; protected set; }
/// <summary>Encoding used to convert data to String.</summary>
public Encoding StringEncoding { get; set; } = Encoding.ASCII;
/// <summary>Does the data contain null (0x00) values?<para>In which case conversion to String will be invalid.</para></summary>
public bool ContainsNull { get; protected set; }
//-------------------------------------------------------------------
// Utilities.
private void CheckForNull()
{
ContainsNull = Array.Exists(DataBuffer, item => item == 0);
}
//-------------------------------------------------------------------
// Conversion to String.
public override String ToString ()
{
return String.Format(GetType().Name + " [Length: {0}]", DataLength);
}
public static implicit operator String (SacsData data)
{
return data.StringEncoding.GetString(data.DataBuffer);
}
public String NullTerminatedString
{
get
{
int nullIdx = Array.IndexOf(DataBuffer,(byte)0);
if (nullIdx >= 0)
return StringEncoding.GetString(DataBuffer.Take(nullIdx).ToArray());
return StringEncoding.GetString(DataBuffer);
}
}
//-------------------------------------------------------------------
// Append data.
//-------------------------------------------------------------------
}
//=======================================================================
}
+325
View File
@@ -0,0 +1,325 @@
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Sarand.Net.Standard.Base.Data
{
//=======================================================================
/// <summary>
/// Legacy basic data encryption.
/// <para>Ref. ItemOptions, Session registration</para>
/// </summary>
public static class SacsDataFn
{
//-------------------------------------------------------------------
// Data members.
private static String acknowledge = "Sarand Software Solutions: www.sarand.com";
//-------------------------------------------------------------------
// S t r i n g E n c r y p t i o n
//-------------------------------------------------------------------
/// <summary>Encrypt string data.</summary>
public static String _saecs (String data, SacsData keyData = null)
{
// Raw data must be a multiple of 3 + 2 in length to allow
// for 4 char header and blocks of 3 encoded chars:
data = StringUtil.PadRight(data, ((data.Length / 3) * 3) + 2, ' ');
// Encrypt the data.
SacsData encryptedData = _saecp (new SacsData(data), keyData);
// Encode the encrypted data.
SacsData encodedData = _saecd (encryptedData.DataBuffer);
// Convert the result to a string.
String result = (encodedData != null ? (String)encodedData : "");
// Return the results.
return result;
}
//-------------------------------------------------------------------
/// <summary>Decrypt string data.</summary>
public static String _sadcs (String data, SacsData keyData = null)
{
// Decode the data.
SacsData decodedData = _sadcd (new SacsData(data).DataBuffer);
// Decrypt the data.
SacsData decryptedData = _sadcp (decodedData.DataBuffer, keyData);
// Convert to string.
String rawData = (decryptedData != null ? (String)decryptedData : "");
// Return the results.
return rawData.TrimEnd(new char[] { ' ' });
}
//-------------------------------------------------------------------
// E n c r y p t i o n
//-------------------------------------------------------------------
// Encrypted data includes a header with the following structure:
// Bytes 1 & 2 0x5aec "SArand EnCrypted"
// Byte 3 bits 0-2 Encryption method (currently only '0' supported).
// Byte 4 Reserved.
//-------------------------------------------------------------------
public static SacsData _saecp (SacsData rawData, SacsData keyData)
{
// Select encryption parameters.
// Dictates which sequence of encryption techniques is used (0 - 7).
int encryptMethod = 0;
#if DEBUG
if (encryptMethod != 0)
SaLogger.Warn("_saecp: Unknown enryption method - " + encryptMethod);
#endif
byte[] encryptedData = null;
int encryptedDataLength = 0;
//-------------------------------------------------------------------
// Version 1.0.0 - just use the RC4 method with default key.
if (encryptMethod == 0)
{
SacsData dataRC4 = new SacsData(RC4 (rawData, keyData));
encryptedData = dataRC4.DataBuffer;
encryptedDataLength = dataRC4.DataLength;
}
//-------------------------------------------------------------------
// Header length - May differ by encryption method.
ushort resultHeaderLength = 4;
// Create a buffer for the result.
int resultLength = (rawData.DataLength + resultHeaderLength);
byte[] resultBuffer = new byte [resultLength];
if (encryptMethod == 0)
{
// First two bytes always the same.
// 'SA' = "Sarand".
resultBuffer[0] = 0x5a;
// 'EC' = "Encrypted".
resultBuffer[1] = 0xec;
// Next byte holds the encrypt method in the 3 high bits:
resultBuffer[2] = (byte)(encryptMethod << 5);
resultBuffer[3] = 0;
}
// Copy the encrypted data to the remaining portion of the buffer.
Array.Copy(encryptedData, 0, resultBuffer, resultHeaderLength, encryptedData.Length);
// Return data.
return new SacsData(resultBuffer);
}
//-------------------------------------------------------------------
// D e c r y p t i o n
//-------------------------------------------------------------------
/// <summary>Decryot data.</summary>
public static SacsData _sadcp (byte[] rawData, SacsData keyData)
{
// Decode the header.
if (rawData == null || rawData.Length == 0)
{
// No data.
return new SacsData();
}
if (rawData.Length < 4)
{
SaLogger.Warn("_sadcp Invalid data length: " + rawData.Length + ". Expected multiple of 4.");
return new SacsData();
}
byte headerChar1 = rawData[0];
byte headerChar2 = rawData[1];
// High 3 bits of header byte #3.
int encryptMethod = rawData[2]>>5;
// May differ by encryption method.
ushort resultHeaderLength = 4;
// Verify the encrypted header:
if (headerChar1 != 0x5a || headerChar2 != 0xec)
{
SaLogger.Warn("_sadcp Invalid header.");
return new SacsData();
}
if (encryptMethod != 0)
{
SaLogger.Warn("_sadcp Invalid encryption method.");
return new SacsData();
}
// Decrypt data.
SacsData resultData = new SacsData();
// Method '0' - simple RC4 decryption.
if (encryptMethod == 0)
{
resultData = RC4(rawData.Skip(resultHeaderLength).ToArray(), keyData);
}
// Convert the data to the return type:
return resultData;
}
//-------------------------------------------------------------------
// E n c o d i n g
//-------------------------------------------------------------------
/// <summary>Encode data.</summary>
public static SacsData _saecd (byte[] pData)
{
// Prepare a buffer for the results.
int dataLength = pData.Length;
byte[] encodeBuffer = new byte[((dataLength + 2) / 3) * 4];
int iBufPos = 0;
// Data is encoded in lots of 3 bytes at a time.
// Each lot of 3 bytes becoms 4 bytes in the encoded data set:
for (uint i = 0; i < dataLength; i += 3)
{
byte byte0 = pData[i];
byte byte1 = (byte)(i < dataLength - 1 ? pData[i+1] : 0);
byte byte2 = (byte)(i < dataLength - 2 ? pData[i+2] : 0);
// The 3 data bytes are first converted to 4 6-bit values:
// Raw: abcdefgh ijklmnop qrstuvwx
// Conv: 00abcdef 00ghijkl 00mnopqr 00stuvwx
// Each of the resulting 4 bytes is then incremented by 0x20 (ascii code for a space):
encodeBuffer[iBufPos++] = ENC(byte0 >> 2); // Byte 1 bits 7-2
encodeBuffer[iBufPos++] = ENC((byte0 << 4) & 0x30 | (byte1 >> 4) & 0x0F); // Byte 1 bits 1-0 & Byte 2 bits 7-4
encodeBuffer[iBufPos++] = ENC((byte1 << 2) & 0x3C | (byte2 >> 6) & 0x03); // Byte 2 bits 3-0 & Byte 3 bits 7-6
encodeBuffer[iBufPos++] = ENC(byte2 & 0x3F); // Byte 3 bits 5-0
}
SacsData pdEncode = new SacsData(encodeBuffer);
return pdEncode;
}
private static byte ENC (int data)
{
return (byte)((data & 0x3F) + 0x20/*Space*/);
}
//-------------------------------------------------------------------
/// <summary>Decode data.</summary>
public static SacsData _sadcd (byte[] rawData)
{
// The encoded data must be a multiple of 4 bytes.
if (rawData == null || rawData.Length % 4 != 0)
{
SaLogger.Warn("_sadcd Data is not a multiple of 4 bytes. Length: " + (rawData?.Length ?? 0));
return new SacsData();
}
// Prepare a buffer for the results.
byte[] resultsBuffer = new byte[rawData.Length / 4 * 3];
int bufferIdx = 0;
// Iterate through the encoded data in blocks of four bytes.
for (uint idx = 0; idx < rawData.Length; idx += 4)
{
byte p = rawData[idx];
int c1 = (DEC(rawData[idx+0]) << 2) | (DEC(rawData[idx+1]) >> 4);
#if DEBUG
int z = rawData[idx+1];
z = DEC(z);
z = z << 4;
z = rawData[idx + 2];
z = DEC(z);
z = z >> 2;
z = (DEC(rawData[idx + 1]) << 4) | (DEC(rawData[idx + 2]) >> 2);
#endif
int c2 = (DEC(rawData[idx+1]) << 4) | (DEC(rawData[idx+2]) >> 2);
int c3 = (DEC(rawData[idx+2]) << 6) | DEC(rawData[idx+3]);
resultsBuffer[bufferIdx++] = (byte)c1;
resultsBuffer[bufferIdx++] = (byte)c2;
resultsBuffer[bufferIdx++] = (byte)c3;
}
return new SacsData(resultsBuffer);
}
private static byte DEC (int data)
{
#if DEBUG
int z = data - 0x20;
z = z & 0x3F;
#endif
return (byte)((data - 0x20/*Space*/) & 0x3F);
}
//-------------------------------------------------------------------
// R C 4 E n c r y p t i o n
//-------------------------------------------------------------------
// www.systweak.com & www.codeproject.com/cpp/crypt_routine.asp
// This Crypt routine uses an algorithm created by Rodney Thayer.
// This algorithm can use keys of various sizes.
// With a 40-bit key (5 characters) it can be freely exported from the U.S.
// The cipher is considered robust with 128 bits of key material but can use up to 2048 bits.
// It is compatible with RSA’s RC4 algorithm.
//---------------------------------------------------------------------------
// The only moral here is that we are generating a 256 bit key given from the user. To encrypt we always pick a random byte from the key and encrypt the text with that key. If we have a large pattern of repeating text with this algorithm the key repeat interval (distance between n repetitions) will be approximately of 21700 (2 to the 1700th power).
// For very good security I recommend using 128 bits (16 characters) of key material. If you want optimal security you could use 2048 bits (256 characters) of key material (but this is usually considered an overkill).
// This routine is constructed from a draft taken from Internet Engineering Task Force (IETF) at http://www.ietf.cnri.reston.va.us/home.htm
// I have tested the routine on a 27 MB file to test that it produce valid decrypted output from input and get the valid results.
// We do not enter the key so we will be using default key provide by the crypt routine:
// TCHAR HelloCrypt[] = "Hello Crypt";
// Encrypt: Crypt(HelloCrypt, _tcslen(HelloCrypt));
// Decrypt: Crypt(HelloCrypt, _tcslen(HelloCrypt));
// If we have to supply a key we have to use crypt as follows:
// TCHAR Data[] = "abcdefghijklmnopqrstuvwxyz"
// TCHAR Key[] = "Strong key";
// Encrypt: Crypt(Data, _tcslen(Data), Key, _tcslen(Data));
// Decrypt: Crypt(Data, _tcslen(Data), Key, _tcslen(Data));
//---------------------------------------------------------------------------
internal static SacsData RC4 (SacsData dataRaw, SacsData keyData)
{
return RC4(dataRaw.DataBuffer, keyData != null ? keyData.DataBuffer : null);
}
internal static SacsData RC4 (byte[] rawData, SacsData keyData)
{
return RC4(rawData, keyData != null ? keyData.DataBuffer : null);
}
public static SacsData RC4 (byte[] rawData, byte[] userKey = null)
{
// Key buffers (256 bytes = 1024 bits):
byte[] keyBuffer = new byte[257];
Array.Clear(keyBuffer, 0, keyBuffer.Length);
// Initialize the key buffer.
for (int keyIdx = 0; keyIdx <= 255U; keyIdx++)
keyBuffer[keyIdx] = (byte)keyIdx;
// Either use the specific key supplied, or the default (unsecured) key:
byte[] keyData = Encoding.ASCII.GetBytes(acknowledge);
int keyLength = StringUtil.GetLength(acknowledge);
if (userKey != null && userKey.Length > 0)
{
keyData = userKey;
keyLength = userKey.Length;
}
// Scramble this raw key into the key buffer.
uint rootKeyIdx = 0;
for (int dataIdx = 0; dataIdx < 256; dataIdx++)
{
// Position in the root key.
rootKeyIdx = (rootKeyIdx + (uint)keyBuffer[dataIdx] + keyData[dataIdx % keyLength]) % 256U;
byte temp = keyBuffer[dataIdx];
keyBuffer[dataIdx] = keyBuffer[rootKeyIdx];
keyBuffer[rootKeyIdx] = temp;
}
#if DEBUG_X
// Retrieve the contents of the key buffer in a readable form.
String key = "";
for (int keyBufferIdx = 0; keyBufferIdx <= 12; keyBufferIdx++) key += ((char)keyBuffer[keyBufferIdx]).ToString() + ".";
#endif
int i = 0;
int j = 0;
// Create a buffer for the encrypted data:
byte[]resultData = new byte [rawData.Length];
for (uint x = 0; x < rawData.Length; x++)
{
// Increment i.
i = (int)((i + 1U) % 256U);
// Increment j.
j = (int)((j + (uint)keyBuffer[i]) % 256U);
// Scramble pKeyBuf #1 further so encryption routine will only repeat itself at great interval.
byte temp = keyBuffer[i];
keyBuffer[i] = keyBuffer[j];
keyBuffer[j] = temp;
// Get ready to create pseudo random byte for encryption key.
int t = (int)(((int) keyBuffer[i] + (int)keyBuffer[j]) % 256U);
// Get the target byte:
byte k = keyBuffer[t];
// Xor with the data and done:
byte raw = rawData[x];
resultData[x] = (byte)(raw ^ k);
}
// Create the return value.
return new SacsData(resultData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
namespace Sarand.Net.Standard.Base.Data
{
//=======================================================================
public static class SacsShaHasher
{
//-------------------------------------------------------------------
// Properties.
public static String ShaSaltDelim { get; } = ":";
public static String Sha256Prefix { get; } = "Sha256:";
public static String Sha384Prefix { get; } = "Sha384:";
public static String Sha512Prefix { get; } = "Sha512:";
//-------------------------------------------------------------------
// Ref. https://xamarinhelp.com/cryptography-in-xamarin-forms/
// Hash Salt.
// To add a salt to the hash, append the Salt (random string) to the value.
// The Salt can be stored with the hashed value.
// Salts do not need to be kept secret and should be different for each hash.
// This is to reduce the impact of pre-built hash tables, eg. where common passwords (‘password’) generate the same Hash.
// It also stops the same password generating the identical Hash for 2 different accounts.
//-------------------------------------------------------------------
/// <summary>
/// Non-reversible encryption.
/// <para>Result is converted to Base64 string to ensure that all bytes are properly recorded, as UTF8 doesn’t have an exact byte match for each possible byte sequence.</para>
/// <para>Algorythm must be dispoed by calling routine.</para>
/// </summary>
public static string HashString (this string value, HashAlgorithm algorythm)
{
return Convert.ToBase64String(algorythm.ComputeHash(System.Text.Encoding.UTF8.GetBytes(value)));
}
//-------------------------------------------------------------------
/// <summary>
/// Non-reversible encryption using Sha256.
/// <para>Result is converted to Base64 string to ensure that all bytes are properly recorded, as UTF8 doesn’t have an exact byte match for each possible byte sequence.</para>
/// </summary>
public static string HashSha256 (this string value)
{
using (HashAlgorithm algorythm = SHA256.Create())
{
return HashString(value, algorythm);
}
}
//-------------------------------------------------------------------
/// <summary>
/// Non-reversible encryption using Sha384.
/// <para>Result is converted to Base64 string to ensure that all bytes are properly recorded, as UTF8 doesn’t have an exact byte match for each possible byte sequence.</para>
/// </summary>
public static string HashSha384 (this string value)
{
using (HashAlgorithm algorythm = SHA384.Create())
{
return HashString(value, algorythm);
}
}
//-------------------------------------------------------------------
/// <summary>
/// Non-reversible encryption using Sha512.
/// <para>Result is converted to Base64 string to ensure that all bytes are properly recorded, as UTF8 doesn’t have an exact byte match for each possible byte sequence.</para>
/// </summary>
public static string HashSha512 (this string value)
{
using (HashAlgorithm algorythm = SHA512.Create())
{
return HashString(value, algorythm);
}
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+203
View File
@@ -0,0 +1,203 @@
using System;
using System.Collections.Generic;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.Dbas
{
//==========================================================================
//[Serializable()]
public enum DbColumnDataType
{
// Ref. https://msdn.microsoft.com/en-us/library/yy6y35y8%28v=vs.110%29.aspx
Unknown = -1,
VarChar, //SQL_C_CHAR,
Bit, // SQL_C_BIT,
TinyInt, // SQL_C_TINYINT,
SmallInt, // SQL_C_SHORT,
Int, //SQL_C_SLONG,
BigInt, // SQL_C_SBIGINT,
Single, // SQL_C_REAL,
Double, // SQL_C_DOUBLE,
Decimal, // SQL_DECIMAL?,
DateTime, // SQL_C_TYPE_TIMESTAMP
Binary,
};
//[Serializable()]
public enum DbColumnNullable
{
NoNulls = 0, // SQL_NO_NULLS,
Nullable = 1, // SQL_NULLABLE,
Unknown = 2, // SQL_NULLABLE_UNKNOWN, // Not known whether or not a data type supports null values.
};
//==========================================================================
//[Serializable()]
public class DbDfnColumn
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public DbDfnColumn () { }
public DbDfnColumn (String schema, String table, short pos, String name)
{
Schema = schema;
Table = table;
Pos = pos;
Name = name;
}
//-------------------------------------------------------------------
// Attributes.
public String Schema { get; set; } = String.Empty;
public String Table { get; set; } = String.Empty;
public short Pos { get; set; } = 0;
public String Name { get; set; } = String.Empty;
public DbColumnDataType DataType { get; set; } = DbColumnDataType.Unknown;
public String DataTypeName { get; set; } = String.Empty;
public short DataTypeNative { get; set; } = 0;
public String DataTypeNameNative { get; set; } = String.Empty;
public bool IsIdentity { get; set; } = false;
public long Size { get; set; } = 0;
public long BufferLen { get; set; } = 0;
public short DecimalDigits { get; set; } = 0;
public DbColumnNullable Nullable { get; set; } = DbColumnNullable.Unknown;
public bool IsNullable { get; set; } = false;
public String Remarks { get; set; } = String.Empty;
//-------------------------------------------------------------------
// Variable type and default - used in variable declaration.
public String GetVariableTypeName ()
{
switch (DataType)
{
case DbColumnDataType.Binary:
return "ByteArray";
}
return GetVariableType();
}
public String GetVariableType ()
{
switch (DataType)
{
case DbColumnDataType.VarChar:
return "String";
case DbColumnDataType.Bit:
return "Boolean";
case DbColumnDataType.TinyInt:
return "Byte";
case DbColumnDataType.SmallInt:
return "Int16";
case DbColumnDataType.Int:
return "Int32";
case DbColumnDataType.BigInt:
return "Int64";
case DbColumnDataType.Single:
return "Single";
case DbColumnDataType.Double:
return "Double";
case DbColumnDataType.Decimal:
return "Decimal";
case DbColumnDataType.DateTime:
return "DateTime";
case DbColumnDataType.Binary:
return "Byte[]";
}
return String.Empty;
}
public String GetVariableDefault ()
{
switch (DataType)
{
case DbColumnDataType.VarChar:
return "String.Empty";
case DbColumnDataType.Bit:
return "false";
case DbColumnDataType.TinyInt:
return "0";
case DbColumnDataType.SmallInt:
return "0";
case DbColumnDataType.Int:
return "0";
case DbColumnDataType.BigInt:
return "0";
case DbColumnDataType.Single:
return "0.0f";
case DbColumnDataType.Double:
return "0.0";
case DbColumnDataType.Decimal:
return "0.0m";
case DbColumnDataType.DateTime:
return "DateTime.MinValue";
case DbColumnDataType.Binary:
return "null";
}
return String.Empty;
}
public String GetGetFieldSuffix ()
{
switch (DataType)
{
case DbColumnDataType.VarChar:
case DbColumnDataType.Bit:
case DbColumnDataType.TinyInt:
case DbColumnDataType.SmallInt:
case DbColumnDataType.Int:
case DbColumnDataType.BigInt:
case DbColumnDataType.Single:
case DbColumnDataType.Double:
case DbColumnDataType.Decimal:
case DbColumnDataType.DateTime:
return GetVariableType();
case DbColumnDataType.Binary:
return "ByteArray";
}
return String.Empty;
}
//-------------------------------------------------------------------
}
//==========================================================================
// Column list extensions.
public static class DbDfnColumnList
{
// List of column names.
public static List<String> GetNames (this List<DbDfnColumn> columns)
{
List<String> names = new List<String>();
foreach (var column in columns)
{
names.Add(column.Name);
}
return names;
}
// Identity column.
public static String GetIdentityColumnName (this List<DbDfnColumn> columns)
{
List<String> names = new List<String>();
foreach (var column in columns)
{
if (column.IsIdentity)
return column.Name;
}
return String.Empty;
}
// Locate the column in the list:
public static DbDfnColumn GetColumnByPos (this List<DbDfnColumn> columns, short pos)
{
foreach (var column in columns)
{
if (column.Pos == pos)
return column;
}
return null;
}
public static DbDfnColumn GetColumnByName (this List<DbDfnColumn> columns, String name)
{
foreach (var column in columns)
{
if (column.Name.EqualsCI(name))
return column;
}
return null;
}
}
//==========================================================================
}
@@ -0,0 +1,37 @@
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.Dbas
{
//=======================================================================
//[Serializable()]
public class DbDfnDatabase
{
//-------------------------------------------------------------------
// Data members.
private readonly List<DbDfnTable> tables = new List<DbDfnTable>();
//-------------------------------------------------------------------
// Construction.
public DbDfnDatabase () { }
//-------------------------------------------------------------------
// Tables.
public List<DbDfnTable> Tables
{
get { return tables; }
set { tables.Clear(); tables.AddRange(value); }
}
public bool HasTables ()
{
return (tables != null && tables.Count > 0);
}
public void CearTables ()
{
tables.Clear();
}
public void AddTable (DbDfnTable table)
{
tables.Add(table);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+96
View File
@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.Dbas
{
//=======================================================================
//[Serializable()]
public class DbDfnIndex
{
//-------------------------------------------------------------------
// Definitions.
public static readonly int SQL_TABLE_STAT = 0;
public static readonly int SQL_INDEX_CLUSTERED = 1;
public static readonly int SQL_INDEX_HASHED = 2;
public static readonly int SQL_INDEX_OTHER = 3;
// SQL_INDEX_BTREE = ???;
// SQL_INDEX_CONTENT = ???;
//-------------------------------------------------------------------
// Data members.
private readonly List<DbDfnIndexColumn> columns = new List<DbDfnIndexColumn>();
//-------------------------------------------------------------------
// Construction.
public DbDfnIndex () { }
public DbDfnIndex (String schema, String table, String name, bool unique)
{
Schema = schema;
Table = table;
Name = name;
Unique = unique;
}
//-------------------------------------------------------------------
// Attributes.
public String Schema { get; set; } = String.Empty;
public String Table { get; set; } = String.Empty;
public String Name { get; set; } = String.Empty;
public int IndexType { get; set; } = -1;
public bool Unique { get; set; } = false;
public bool Primary { get; set; } = false;
public bool Clustered { get; set; } = false;
public bool Nullable { get; set; } = false;
public int Pages { get; set; } = 0;
public int UniqueValues { get; set; } = 0;
public bool HasColumns { get; set; } = false;
//-------------------------------------------------------------------
// Columns.
public List<DbDfnIndexColumn> Columns
{
get { return columns; }
set { columns.Clear(); columns.AddRange(value); }
}
public void CearColumns ()
{
columns.Clear();
}
public void AddColumn (DbDfnIndexColumn column)
{
columns.Add(column);
}
//-------------------------------------------------------------------
}
//=======================================================================
// Index list extensions.
public static class DbDfnIndexList
{
// List of index names.
public static List<String> GetNames (this List<DbDfnIndex> indices)
{
List<String> names = new List<String>();
foreach (var index in indices)
names.Add(index.Name);
return names;
}
// Locate the index in the list.
public static DbDfnIndex GetIndexByName (this List<DbDfnIndex> indices, String name)
{
return indices.Find(idx => idx.Name.EqualsCI(name));
}
// Retrieve primary index.
public static DbDfnIndex GetPrimaryIndex (this List<DbDfnIndex> indices)
{
return indices.Find(idx => idx.Primary);
}
// Retrieve clustered index.
public static DbDfnIndex GetClusteredIndex (this List<DbDfnIndex> indices)
{
DbDfnIndex index = indices.Find(idx => idx.Clustered);
if (index == null)
index = indices.Find(idx => idx.IndexType == DbDfnIndex.SQL_INDEX_CLUSTERED);
return index;
}
}
//=======================================================================
}
@@ -0,0 +1,61 @@
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.Dbas
{
//==========================================================================
//[Serializable()]
public class DbDfnIndexColumn
{
//-------------------------------------------------------------------
// Construction.
public DbDfnIndexColumn () { }
public DbDfnIndexColumn (short seq, String name, bool ascend)
{
this.Seq = seq;
this.Name = name;
this.Ascend = ascend;
}
//-------------------------------------------------------------------
// Attributes.
public short Seq { get; set; } = 0;
public String Name { get; set; } = String.Empty;
public bool Ascend { get; set; } = true;
//-------------------------------------------------------------------
}
//==========================================================================
// Index Column list extensions.
public static class DbDfnIndexColumnList
{
//-------------------------------------------------------------------
/// <summary>Compile list of column names.</summary>
public static List<String> GetNames (this List<DbDfnIndexColumn> columns)
{
List<String> names = new List<String>();
foreach (var column in columns)
{
names.Add(column.Name);
}
return names;
}
//-------------------------------------------------------------------
/// <summary>Get position (from 1) for column by name.</summary>
public static int GetPosForName (this List<DbDfnIndexColumn> columns, String columnName)
{
for (int idx = 0; idx < columns.Count; idx++)
foreach (var column in columns)
{
if (columns[idx].Name.EqualsCI(columnName)) return idx + 1;
}
return 0;
}
//-------------------------------------------------------------------
}
//==========================================================================
}
+160
View File
@@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.Dbas
{
//=======================================================================
//[Serializable()]
public class DbDfnTable
{
//-------------------------------------------------------------------
// Data members.
private readonly List<DbDfnColumn> columns = new List<DbDfnColumn>();
private readonly List<DbDfnIndex> indices = new List<DbDfnIndex>();
//-------------------------------------------------------------------
// Construction.
public DbDfnTable () { }
public DbDfnTable (String schema, String name, String remarks)
{
this.Schema = schema;
this.Name = name;
this.Remarks = remarks;
}
//-------------------------------------------------------------------
// Properties.
public String Schema { get; set; } = String.Empty;
public String Name { get; set; } = String.Empty;
public String QualifiedName { get { return (!StringUtil.IsEmpty(Schema) ? Schema + "." : "") + Name; } }
public String Remarks { get; set; } = String.Empty;
//-------------------------------------------------------------------
// Comparison.
public bool MatchesSchemaName (String schema, String name)
{
return (this.Schema.EqualsCI(schema) && this.Name.EqualsCI(name));
}
public bool MatchesName (String name)
{
return (this.Name.EqualsCI(name));
}
//-------------------------------------------------------------------
// Columns.
public bool HasColumnByName (String columnName)
{
return (columns != null && columns.Exists(col => col.Name.EqualsCI(columnName)));
}
public bool HasColumns ()
{
return (columns != null && columns.Count > 0);
}
public List<DbDfnColumn> Columns
{
get { return new List<DbDfnColumn>(columns); }
set { columns.Clear(); columns.AddRange(value); }
}
public void CearColumns ()
{
columns.Clear();
}
public void AddColumn (DbDfnColumn column)
{
columns.Add(column);
}
public List<String> GetPrimaryColumnNames ()
{
List<String>primaryColumnNames = new List<string>();
// Identify primary index.
var index = Indices.GetPrimaryIndex();
if (index == null) index = indices.GetClusteredIndex();
if (index != null)
{
foreach (var column in index.Columns)
primaryColumnNames.Add(column.Name);
}
if (primaryColumnNames.IsEmpty()) throw new NotSupportedException("Table " + Name + " has no primary columns defined.");
return primaryColumnNames;
}
//-------------------------------------------------------------------
// Indices.
public bool HasIndexByName (String indexName)
{
return (indices != null && indices.Exists(idx => idx.Name.EqualsCI(indexName)));
}
public List<DbDfnIndex> Indices
{
get { return new List<DbDfnIndex>(indices); }
set { indices.Clear(); indices.AddRange(value); }
}
public DbDfnIndex GetIndexByName (String name)
{
return indices.GetIndexByName(name);
}
public void CearIndices ()
{
indices.Clear();
}
public void AddIndex (DbDfnIndex Index)
{
indices.Add(Index);
}
//-------------------------------------------------------------------
}
//=======================================================================
// Table list extensions.
public static class DbDfnTableList
{
//-------------------------------------------------------------------
/// <summary>List of table names.</summary>
public static List<String> GetNames (this List<DbDfnTable> tables)
{
List<String> names = new List<String>();
foreach (DbDfnTable table in tables)
{
names.Add(table.Name);
}
return names;
}
//-------------------------------------------------------------------
/// <summary>Extract the table definition by schema and table name.</summary>
public static DbDfnTable GetTableBySchemaName (this List<DbDfnTable> tables, String schema, String name)
{
foreach (DbDfnTable table in tables)
{
if (table.MatchesSchemaName(schema, name))
return table;
}
return null;
}
/// <summary>Extract the table definition by table name.</summary>
public static DbDfnTable GetTableByName (this List<DbDfnTable> tables, String name)
{
foreach (DbDfnTable table in tables)
{
if (table.MatchesName(name))
return table;
}
return null;
}
//-------------------------------------------------------------------
/// <summary>Get total number of columns for all tables.</summary>
public static int GetColumnCount (this List<DbDfnTable> tables)
{
int columnCount = 0;
foreach (DbDfnTable table in tables)
columnCount += table.Columns.Count;
return columnCount;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+751
View File
@@ -0,0 +1,751 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.Dbas
{
//=======================================================================
// Definitions.
public enum DbJoinType
{
Inner,
LeftOuter,
RightOuter,
FullOuter,
Cross,
}
public enum DbOrderByNullMode
{
Default,
DemoteNulls,
PromoteNulls,
DateDemoteNulls,
DatePromoteNulls
}
public enum DbDateDiffInterval
{
Year,
Quarter,
Month,
DayOfYear,
Day,
Weekday,
Week,
Hour,
Minute,
Second,
}
//=======================================================================
// Global DBMS.
// When dealing with databases using different DBMSs, the methods below that
// depend on the DBMS to correctly format query clauses need to ensure that
// they are referencing the correct DBMS type.
// This global value is designed to be populated whenever a Query is created
// with a specific DBMS specified so that it can be made available to other
// methods that depend on the value (especially static ones).
public class DbGlobalDbms
{
private DbGlobalDbms () { }
private static DbmsType dbms = DbmsType.General;
public static DbmsType Type
{
get { return dbms; }
set { if (value != DbmsType.General) dbms = value; }
}
}
//=======================================================================
public class DbQueryJoin
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public DbQueryJoin (DbJoinType type = DbJoinType.Inner)
{
this.Type = type;
}
//-------------------------------------------------------------------
// Properties.
public DbJoinType Type { get; private set; } = DbJoinType.Inner;
public String JoinType { get { return GetJoinType(Type); } }
public StringPair Table { get; set; } = null; /* Second value is the optional alias. */
public List<String> Filters { get; } = new List<String>();
public List<DbQueryJoin> SubJoins { get; } = new List<DbQueryJoin>();
//-------------------------------------------------------------------
public static String GetJoinType (DbJoinType type)
{
switch (type)
{
case DbJoinType.LeftOuter: return (DbGlobalDbms.Type == DbmsType.Sage ? "LEFT JOIN" : "LEFT OUTER JOIN");
case DbJoinType.RightOuter: return (DbGlobalDbms.Type == DbmsType.Sage ? "RIGHT JOIN" : "RIGHT OUTER JOIN");
case DbJoinType.FullOuter: return (DbGlobalDbms.Type == DbmsType.Sage ? "FULL JOIN" : "FULL OUTER JOIN");
case DbJoinType.Cross: return "CROSS JOIN";
case DbJoinType.Inner:
default: return "INNER JOIN";
}
}
//-------------------------------------------------------------------
public String BuildJoin (String enclosedJoin)
{
//---------------------------------------------------------------
// Select syntax.
// SELECT [ DISTINCT | ALL ]
// column_expression1, column_expression2, ....
// [ FROM from_clause ]
// [ WHERE where_expression ]
// [ GROUP BY expression1, expression2, .... ]
// [ HAVING having_expression ]
// [ ORDER BY order_column_expr1, order_column_expr2, .... ]
// column_expression ::= expression [ AS ] [ column_alias ]
// from_clause ::= select_table1, select_table2, ...
// from_clause ::= select_table1 LEFT [OUTER] JOIN select_table2 ON expr ...
// from_clause ::= select_table1 RIGHT [OUTER] JOIN select_table2 ON expr ...
// from_clause ::= select_table1 [INNER] JOIN select_table2 ...
// select_table ::= table_name [ AS ] [ table_alias ]
// select_table ::= ( sub_select_statement ) [ AS ] [ table_alias ]
// order_column_expr ::= expression [ ASC | DESC ]
// Issues.
// MS Access - complex join expressions must be bracketed: ON (expr ...)
//---------------------------------------------------------------
String joinData = String.Empty;
//---------------------------------------------------------------
// Integrate multiple joins (nested).
// <join1-Type>
// (<join1-Table>
// <join2-Type>
// (<join2-Table> <join3-Type> <join3-Table> ON <join3-Filter>)
// ON <join2-Filter>)
// ON <join1-Filter>
//---------------------------------------------------------------
// Sage doesn't use 'AS' to alias tables.
String tableAliasOperator = (DbGlobalDbms.Type == DbmsType.Sage ? " " : " AS ");
String joinType = GetJoinType(Type);
String joinTable = Table.Concat (tableAliasOperator, false);
String subJoinData = String.Empty;
foreach (var subJoin in SubJoins)
{
// Add another bracket in front of the table:
joinTable = "(" + joinTable;
// Add the sub join.
subJoinData += subJoin.BuildJoin("") + ")";
}
// Build this join's list of filter conditions.
// Take care not to add too many embedded brackets - confuses Access etc.
// [Original version: lFilter.Concat (" AND ", "(", ")", true, true)].
String filter = String.Empty;
foreach (var rawFilterClause in Filters)
{
// Retrieve the next filter clause.
String filterClause = rawFilterClause.Trim();
if (filterClause.IsEmpty()) continue;
// Embed the clause in brackets if it contains at least one 'OR' operator.
if (filterClause.IndexOf(" OR ", StringComparison.OrdinalIgnoreCase) >= 0)
filterClause = "(" + filterClause + ")";
filter += (!filter.IsEmpty() ? " AND " : "") + filterClause;
}
// Surround the entire filter in brackets (if not already done).
if (filter.FindMatchingBrace(0) != filter.Length)
filter = "(" + filter + ")";
// Build the join.
if (enclosedJoin.IsEmpty() && subJoinData.IsEmpty())
{
//-----------------------------------------------------------
// Simple join syntax.
// <Join-Type> <Join-Table> ON <Join-Filter>
joinData = joinType + " " + joinTable + " ON " + filter;
//-----------------------------------------------------------
}
else
{
//-----------------------------------------------------------
// More complex join syntax - nested joins.
// <Join-Type> (<Join-Table> <NestedJoin>) ON <Join-Filter>
//-----------------------------------------------------------
// Bracket the table, enclosing any enclosed or subordinate joins.
joinData = joinType + " (" + joinTable;
if (!subJoinData.IsEmpty())
joinData += " " + subJoinData;
if (!enclosedJoin.IsEmpty())
joinData += " " + enclosedJoin;
joinData += ") ON " + filter;
//-----------------------------------------------------------
}
return joinData;
}
//-------------------------------------------------------------------
// Debugging.
public void Dump ()
{
SaLogger.Debug(GetType().Name + ".Dump");
SaLogger.Debug("\tType = " + GetJoinType(Type));
SaLogger.Debug("\tTable = " + Table.Concat(" AS "));
SaLogger.Debug("\tFilters = " + Filters.Concat("", "\t\t", "\n"));
if (SubJoins.Count > 0)
{
}
SaLogger.Debug("\tJoin = " + BuildJoin(""));
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class DbQueryJoinList
{
//-------------------------------------------------------------------
// List extensions.
public static String BuildJoins (this List<DbQueryJoin> joins, String primaryTable)
{
String joinData = primaryTable;
if (joins.Count > 0)
{
//-----------------------------------------------------------
// Integrate multiple joins (nested).
// <join1-Type>
// (<join1-Table>
// <join2-Type>
// (<join2-Table> <join3-Type> <join3-Table> ON <join3-Filter>)
// ON <join2-Filter>)
// ON <join1-Filter>
//-----------------------------------------------------------
bool bBracketFromFirstToLast = true;
if (bBracketFromFirstToLast)
{
// Bracketing from the first through to the last causes the
// Main table name to be included in the brackets.
// This appears to work for complex joins (multiple tables)
// but may not be 100% compliant with the SQL specification.
int joinCount = 0;
foreach (var join in joins)
{
// Add the next join in the list.
if (++joinCount > 1 && !joinData.IsEmpty()) joinData = "(" + joinData + ")";
joinData = joinData + " " + join.BuildJoin("");
joinData.Trim();
}
}
else
{
// Jul-04 Attempt to organise joins with innermost bracketed.
// Start from the end of the list and work backwards.
// NB Fails with multiple joins - check SQL specification!
// Start with the last join and roll up towards the first.
joinData = String.Empty;
foreach (var join in joins)
{
// Add the next join in the list.
joinData = join.BuildJoin(joinData);
joinData.Trim();
}
joinData = primaryTable + " " + joinData;
}
}
return joinData;
}
//-------------------------------------------------------------------
}
//=======================================================================
public class DbQuery
{
//-------------------------------------------------------------------
// Results paging.
public class Paging
{
public uint PageNo { get; set; } = 0;
public ushort PageLength { get; set; } = 0;
public virtual bool Paged { get { return PageNo > 0 && PageLength > 0; } }
}
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public DbQuery (DbmsType dbms)
{
DbGlobalDbms.Type = dbms;
}
//-------------------------------------------------------------------
// Properties.
public bool DistinctRows { get; set; } = false;
public uint RowLimit { get; set; } = 0;
public List<StringPair> Tables { get; } = new List<StringPair>();
public List<StringPair> Columns { get; } = new List<StringPair>(); /* s1 = qualified column name, s2 = optional alias. */
public List<String> Filters { get; } = new List<string>();
public List<String> GroupBy { get; } = new List<string>();
public List<String> OrderBy { get; } = new List<string>();
public List<DbQueryJoin> Joins { get; } = new List<DbQueryJoin>();
/// <summary>Date value used as alternative to Null on database (ref. C++ ODBC defaults).</summary>
public static DateTime NullDate { get; } = new DateTime(1970, 1, 1);
//-------------------------------------------------------------------
// S t r i n g V a l u e F o r m a t t i n g
//-------------------------------------------------------------------
/// <summary>
/// Format the value so that it can be used in a conditional clause.
/// <para>- single quotes are duplicated.</para>
/// </summary>
public static String FormatForCondition (String value)
{
return StringUtil.EmptyOnNull(value).Replace("'", "''");
}
/// <summary>
/// Format the specified string value so that it can be used in a 'LIKE' clause.
/// <para>- single quotes are duplicated</para>
/// <para>- open square bracket ('[') is enclosed in square brackets ('[[]')</para>
/// <para>- percent ('%') is enclosed in square brackets ('[%]') unless wildcards allowed.</para>
/// <para>- asterisk ('*') converted to percent ('%') if wildcards allowed.</para>
/// </summary>
public static String FormatForLike (String value, bool allowWildcards = false)
{
value = StringUtil.EmptyOnNull(value).Replace("'", "''").Replace("[", "[[]");
if (allowWildcards)
{
value = value.Replace("*", "%");
}
else
{
value = value.Replace("%", "[%]");
}
return value;
}
/// <summary>
/// Format the list of values so that they it can be used in an 'In' clause.
/// <para>- Ignore blanks (if instructed), remove duplicates &amp; pass string values through 'FormatForCondition'.</para>
/// <para>- Format as: ('Value1','Value2','Value3') if string data, else (Value1,Value2,Value3) etc.</para>
/// <para>- Returns empty string if list is empty.</para>
/// </summary>
public static String FormatForIn (List<String> rawValues, bool stringData = true, bool ignoreBlanks = true)
{
String result = String.Empty;
List<String> values = new List<String>();
foreach (var rawValue in rawValues)
{
String value = rawValue;
if (!rawValue.IsEmpty() || !ignoreBlanks)
{
if (stringData)
value = "'" + FormatForCondition(value) + "'";
else value.Trim(); // Non string data can be cleansed.
values.Add(value);
}
}
values.RemoveDuplicatesCI();
if (values.Count > 0)
result = "(" + values.Concat(",") + ")";
return result;
}
//-------------------------------------------------------------------
// D a t e V a l u e F o r m a t t i n g
//-------------------------------------------------------------------
public static String GetDateDelim ()
{
String delim = "#"; // MS Access only?
if (DbGlobalDbms.Type == DbmsType.MsSql)
delim = "'"; // MS SQL Server.
return delim;
}
public static String FormatDateForCondition (DateTime date, bool delimit = true, bool includeMidnight = false)
{
String value = DateUtil.Format(date, "dd-MMM-yyyy");
if (includeMidnight)
value += " 00:00:00";
if (delimit)
{
String delim = GetDateDelim();
value = delim + value + delim;
}
return value;
}
public static String FormatDateTimeForCondition (DateTime dateTime, bool delimit = true, bool precise = false)
{
String value = DateUtil.Format(dateTime, "yyyy-MM-dd HH:mm:ss"+(precise?".fff":""));
if (delimit)
{
String delim = GetDateDelim();
value = delim + value + delim;
}
return value;
}
public static String FormatTimeForCondition (DateTime time, bool delimit = true, bool zeroSeconds = false)
{
String value = String.Empty;
if (zeroSeconds)
value = DateUtil.Format(time, "HH:mm:00");
else
value = DateUtil.Format(time, "HH:mm:ss");
if (delimit)
{
String delim = GetDateDelim();
value = delim + value + delim;
}
return value;
}
//-------------------------------------------------------------------
// F o r m a t F o r F i l t e r
//-------------------------------------------------------------------
public static String FormatNullValueFilter (String expression, bool excludeNulls)
{
String clause = String.Empty;
expression = StringUtil.EmptyOnNull(expression).Trim();
if (!expression.IsEmpty())
{
// Surround the expression in brackets if not present.
if (expression.Left(1) != "(")
expression = "(" + expression + ")";
// Logic to include or exclude null values.
if (excludeNulls)
{
// Explicitly exclude Null values.
//if (DbGlobalDbms.Type == DbmsType.MsSql)
// sClause = "(CASE WHEN "+sExpr+" IS NULL THEN 0 ELSE 1 END) = 1";
//else
clause = expression + " IS NOT NULL";
}
else
{
// Explicitly include only Null values.
//if (DbGlobalDbms.Type == DbmsType.MsSql)
// sClause = "(CASE WHEN "+sExpr+" IS NULL THEN 0 ELSE 1 END) = 0";
//else
clause = expression + " IS NULL";
}
}
return clause;
}
// Construct 'Order By' clauses, allowing direction (ASC/DESC) and whether
// to demote NULL values (push to end of sequence) into account.
public static List<String> FormatForOrderBy (String expression, bool ascending, DbOrderByNullMode nullMode = DbOrderByNullMode.Default, bool treatZeroAsNull = false)
{
List<String> orderBy = new List<string>();
expression = StringUtil.EmptyOnNull(expression).Trim();
if (!expression.IsEmpty())
{
// Surround the expression in brackets if not present.
if (expression.Left(1) != "(")
expression = "(" + expression + ")";
// Logic to promote null values to start of sequence or demote null values to the end of the sequence.
String nullsClause = String.Empty;
if (nullMode == DbOrderByNullMode.PromoteNulls || nullMode == DbOrderByNullMode.DatePromoteNulls)
{
if (DbGlobalDbms.Type == DbmsType.MsSql)
{
// Treat NULLs before other values.
List<String> nullsFilters = new List<String>();
nullsFilters.Add(expression + " IS NULL");
if (treatZeroAsNull)
nullsFilters.Add(expression + "=0");
if (nullMode == DbOrderByNullMode.DatePromoteNulls)
nullsFilters.Add(expression + "<=" + FormatDateForCondition(NullDate));
nullsClause = "(CASE WHEN " + nullsFilters.Concat(" OR ", "(", ")") + " THEN 0 ELSE 1 END)";
}
else
nullsClause = expression + " IS NULL";
}
else if (nullMode == DbOrderByNullMode.DemoteNulls || nullMode == DbOrderByNullMode.DateDemoteNulls)
{
if (DbGlobalDbms.Type == DbmsType.MsSql)
{
// Treat NULLs after other values.
List<String> nullsFilters = new List<string>();
nullsFilters.Add(expression + " IS NULL");
if (treatZeroAsNull)
nullsFilters.Add(expression + "=0");
if (nullMode == DbOrderByNullMode.DateDemoteNulls)
nullsFilters.Add(expression + "<=" + FormatDateForCondition(NullDate));
nullsClause = "(CASE WHEN " + nullsFilters.Concat(" OR ", "(", ")") + " THEN 1 ELSE 0 END)";
}
else
nullsClause = expression + " IS NOT NULL";
}
if (!nullsClause.IsEmpty()) orderBy.Add(nullsClause);
// Main clause for this expression.
orderBy.Add(expression + " " + (ascending ? "ASC" : "DESC"));
}
return orderBy;
}
//-------------------------------------------------------------------
// S Q L F u n c t i o n F o r m a t t i n g
//-------------------------------------------------------------------
// NB Many functions depend on the DBMS in use.
// Default versions are for Access/SQL.
public static String FormatFn_Len (String column)
{
if (String.IsNullOrEmpty(column)) return "''";
// MS Access.
return "Len(" + column + ")";
}
public static String FormatFn_Left (String column, int length)
{
if (String.IsNullOrEmpty(column) || length < 1) return "''";
// MS Access.
return "Left(" + column + "," + length.ToString() + ")";
}
public static String FormatFn_Right (String column, int length)
{
if (String.IsNullOrEmpty(column) || length < 1) return "''";
// MS Access.
return "Right(" + column + "," + length.ToString() + ")";
}
public static String FormatFn_TimeValue (String expression)
{
if (DbGlobalDbms.Type == DbmsType.MsSql)
{
// Convert with Date/Time values.
// Param 3 - Date/Time format.
// 1 = mm/dd/yy
// 2 = yy.mm.dd
// 3 = dd/mm/yy
// 4 = dd.mm.yy
// 5 = dd-mm-yy
// 6 = dd mmm yy
// 7 = mmm dd, yy
// 8 = hh:mm:ss
// 9 = mmm dd yyyy hh:mm:ss:000AP
return "Convert(VarChar(10)," + expression + ",8)"; // CONVERT(varchar(10), DateClosed, 8)
}
return "TimeValue(" + expression + ")";
}
//-------------------------------------------------------------------
// N u m e r i c F u n c t i o n s
//-------------------------------------------------------------------
public static String FormatFn_Abs (String sExpr)
{
if (sExpr.IsEmpty()) return "''";
return "Abs(" + sExpr + ")";
}
public static String FormatFn_Round (String expression, int decimals = 0)
{
if (String.IsNullOrEmpty(expression) || decimals < 0) return "''";
if (DbGlobalDbms.Type == DbmsType.Access)
{
// MS Access.
// NB Uses the 'round-to-even' logic - 0.5 values go to the even whole value rather than always up.
// Solution to get it behave consistently - add a fraction to make it 0.5001.
return "Round(" + expression + ((long)(10 ^ -(decimals + 2))).ToString() + "," + decimals.ToString() + ")";
}
return "Round(" + expression + "," + decimals.ToString() + ")";
}
public static String FormatFn_Truncate (String expression, int decimals = 0)
{
if (String.IsNullOrEmpty(expression) || decimals < 0) return "''";
if (DbGlobalDbms.Type == DbmsType.MsSql)
return "Round(" + expression + "," + decimals.ToString() + ",1)";
if (DbGlobalDbms.Type == DbmsType.Access)
// MS Access.
if (decimals > 0)
{
String factor = ((long)(10^decimals)).ToString();
return "Int(" + expression + "*" + factor + "," + decimals.ToString() + ")/" + factor;
}
return "Int(" + expression + "," + decimals.ToString() + ")";
}
public static String FormatFn_FormatNumber (String expression, int decimals)
{
if (String.IsNullOrEmpty(expression) || decimals < 0) return "''";
// MS Access.
return "FormatNumber(" + expression + "," + decimals.ToString() + ")";
}
//-------------------------------------------------------------------
// D a t e D i f f e r e n c e
//-------------------------------------------------------------------
// Date difference: eg. DateDiff('n',Col1,Col2).
public static String FormatFn_DateDiff (DbDateDiffInterval dateInterval, String column1, String column2)
{
if (column1.IsEmpty() || column2.IsEmpty()) return "''";
String interval = String.Empty;
if (DbGlobalDbms.Type == DbmsType.MsSql)
{
// SQL Server.
switch (dateInterval)
{
// NB No delimiter.
case DbDateDiffInterval.Year: interval = "yyyy"; break;
case DbDateDiffInterval.Quarter: interval = "q"; break;
case DbDateDiffInterval.Month: interval = "m"; break;
case DbDateDiffInterval.DayOfYear: interval = "y"; break;
case DbDateDiffInterval.Day: interval = "d"; break;
//case Weekday: interval = "w"; break; // Not supported by SQL Server.
case DbDateDiffInterval.Week: interval = "ww"; break;
case DbDateDiffInterval.Hour: interval = "h"; break;
case DbDateDiffInterval.Minute: interval = "n"; break;
case DbDateDiffInterval.Second: interval = "s"; break;
//case MilliSecond: interval = "ms"; break; // Not yet defined.
}
}
else
{
// MS Access.
switch (dateInterval)
{
case DbDateDiffInterval.Year: interval = "'yyyy'"; break;
case DbDateDiffInterval.Quarter: interval = "'q'"; break;
case DbDateDiffInterval.Month: interval = "'m'"; break;
case DbDateDiffInterval.DayOfYear: interval = "'y'"; break;
case DbDateDiffInterval.Day: interval = "'d'"; break;
case DbDateDiffInterval.Weekday: interval = "'w'"; break;
case DbDateDiffInterval.Week: interval = "'ww'"; break;
case DbDateDiffInterval.Hour: interval = "'h'"; break;
case DbDateDiffInterval.Minute: interval = "'n'"; break;
case DbDateDiffInterval.Second: interval = "'s'"; break;
}
if (interval.IsEmpty()) return "''";
}
return "DateDiff(" + interval + "," + column1 + "," + column2 + ")";
}
//-------------------------------------------------------------------
// B i t M a p p e d V a l u e s
//-------------------------------------------------------------------
public static String BuildBitMappedValue (String column, ushort bit, bool match)
{
// Filter clause to test for specific bit set in field.
String clause = String.Empty;
if (!String.IsNullOrEmpty(column))
{
if (bit > 0 && bit <= 32)
{
// The operation depands on whether the value should be matched or not.
String condition = (match? "<>" : "=");
// Test is achieved by shifting the value so as to drop the target bit, shift back one bit
// and identify those value that then do not match the original value shifted by one place less.
// Example match: abcdefgh, Test bit 3: abcde0 <> abcdef
clause = "(" + FormatFn_Truncate(column + "/" + Math.Pow(2, bit).ToString()) + "*2)" + condition + FormatFn_Truncate(column + "/" + Math.Pow(2, bit - 1).ToString());
if (DbGlobalDbms.Type == DbmsType.MsSql)
clause = "(Case When " + clause + " Then 1 Else 0 End)";
}
#if DEBUG
else
{
SaLogger.Debug("BuildBitMappedFilter - Invalid bit (" + bit + ").");
}
#endif
}
#if DEBUG
else
{
SaLogger.Debug("BuildBitMappedFilter - NULL column name.");
}
#endif
return clause;
}
public static String BuildBitMappedFilter (String column, ushort bit, bool match)
{
String clause = BuildBitMappedValue(column, bit, match);
if (!clause.IsEmpty())
{
if (DbGlobalDbms.Type == DbmsType.MsSql)
clause = "(" + clause + " <> 0)";
}
return clause;
}
//-------------------------------------------------------------------
// A t t r i b u t e s
//-------------------------------------------------------------------
public void Clear ()
{
Tables.Clear();
Columns.Clear();
Filters.Clear();
GroupBy.Clear();
OrderBy.Clear();
Joins.Clear();
}
public bool IsValid ()
{
return (Tables.Count > 0);
}
//-------------------------------------------------------------------
// B u i l d Q u e r y
//-------------------------------------------------------------------
public String BuildQuery (Paging paging = null)
{
if (Tables.Count < 1) return "";
//---------------------------------------------------------------------
// Start with the column names.
String query = "SELECT ";
if (DistinctRows) query += "DISTINCT ";
if (RowLimit > 0) query += "TOP " + RowLimit.ToString() + " ";
if (Columns.Count < 1) query += "* ";
else query += Columns.Concat(" AS ", false).Concat(",");
//---------------------------------------------------------------------
// Add the tables and joins.
String tableAliasOperator = DbGlobalDbms.Type == DbmsType.Sage? " " : " AS ";
if (Tables.Count > 1 || Joins.Count < 1)
{
query += " FROM " + Tables.Concat(tableAliasOperator, false).Concat(",") + " " + Joins.BuildJoins("");
}
else if (Tables.Count == 1)
{
// Table name is bracketed with multiple joins...
query += " FROM " + Joins.BuildJoins(Tables.Concat(tableAliasOperator, false)[0]);
}
query.TrimEnd(' ');
//---------------------------------------------------------------------
// Filters.
String filter = Filters.Concat (" AND ", "(", ")", true, true);
// Jan2019 Not required: if (Filters.Count > 1) filter = "(" + filter + ")";
if (!filter.IsEmpty()) query += " WHERE " + filter;
// Groupings.
String groupByData = GroupBy.Concat(",", "", "", true, true);
if (!groupByData.IsEmpty()) query += " GROUP BY " + groupByData;
// Sort sequence.
String orderByData = OrderBy.Concat(",", "", "", true, true);
if (!orderByData.IsEmpty()) query += " ORDER BY " + orderByData;
// Paging.
if (paging != null && paging.Paged)
{
// Must have an 'Order By' clause to use 'Offset ... Fetch Next ...'
if (orderByData.IsEmpty())
throw new NotSupportedException("Query paging requires sort ofrder to be specified");
if (DbGlobalDbms.Type != DbmsType.MsSql)
throw new NotSupportedException("Query paging only supported for MS SQL");
query += " OFFSET " + ((paging.PageNo - 1) * paging.PageLength) + " ROWS FETCH NEXT " + paging.PageLength + " ROWS ONLY";
}
//---------------------------------------------------------------------
#if DEBUG
bool bDisplayDebugMsg = false;
if (bDisplayDebugMsg)
SaLogger.Debug("Query: " + query);
#endif
//---------------------------------------------------------------------
return query;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+176
View File
@@ -0,0 +1,176 @@
using System;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.App;
namespace Sarand.Net.Standard.Base.Dbas
{
//=======================================================================
public abstract class DbResultSet : SaErrorStateItem, IDbResultSet
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
protected DbResultSet ()
{
}
//-------------------------------------------------------------------
// Navigation.
public abstract bool NextRecord ();
public abstract void Close ();
//-------------------------------------------------------------------
// Properties.
public abstract int FieldCount { get; }
public bool IsValidColumn (int columnIdx)
{
return (columnIdx >= 0 && columnIdx < FieldCount);
}
public abstract bool HasRows { get; }
//-------------------------------------------------------------------
// Field Values.
public abstract int GetOrdinal (String columnName);
public abstract String GetDataTypeName (int columnIdx);
public abstract String GetName (int columnIdx);
public abstract bool IsDbNull (int columnIdx);
public abstract Object GetValue (int columnIdx);
public abstract bool GetBoolean (int columnIdx);
public abstract byte GetByte (int columnIdx);
public abstract short GetInt16 (int columnIdx);
public abstract int GetInt32 (int columnIdx);
public abstract long GetInt64 (int columnIdx);
public abstract float GetFloat (int columnIdx);
public abstract double GetDouble (int columnIdx);
public abstract decimal GetDecimal (int columnIdx);
public abstract String GetString (int columnIdx);
public abstract DateTime GetDateTime (int columnIdx);
//-------------------------------------------------------------------
// Get Field Value.
public DataValue GetFieldValue (String columnName)
{
String fn = GetType().Name+".GetFieldValue('" + columnName + "')";
DataValue dataValue = null;
try
{
dataValue = GetFieldValue(GetOrdinal(columnName));
}
catch (Exception exc)
{
SaLogger.Warn(fn + " Exception: " + exc.Message);
}
return dataValue;
}
#if DEBUG
private static bool handlingGetFieldValueException = false;
#endif
public DataValue GetFieldValue (int columnIdx)
{
String fn = GetType().Name+".GetFieldValue(" + columnIdx + ")";
if (IsDbNull(columnIdx)) return null;
String columnDataType = "";
DataValue dataValue = null;
try
{
columnDataType = GetDataTypeName(columnIdx);
switch (columnDataType)
{
// Catering for c# (DataTableReader) and sql (DbDataReader) type names
case "bit":
case "Bool":
dataValue = new DataValue(GetBoolean(columnIdx));
break;
case "tinyint":
dataValue = new DataValue((long)GetByte(columnIdx));
break;
case "smallint":
case "Int16":
dataValue = new DataValue((long)GetInt16(columnIdx));
break;
case "int":
case "int identity":
case "Int32":
dataValue = new DataValue((long)GetInt32(columnIdx));
break;
case "bigint":
case "Int64":
dataValue = new DataValue((long)GetInt64(columnIdx));
break;
case "real":
case "float":
case "Float":
case "Single":
case "Double":
dataValue = new DataValue(GetDouble(columnIdx));
break;
case "decimal":
case "Decimal":
case "numeric":
case "Numeric":
dataValue = new DataValue(GetDecimal(columnIdx));
break;
case "char":
case "varchar":
case "nvarchar":
case "String":
dataValue = new DataValue(GetString(columnIdx));
break;
case "datetime":
case "DateTime":
case "smalldatetime":
case "SmallDateTime":
dataValue = new DataValue(GetDateTime(columnIdx));
break;
default:
SaLogger.Warn(fn + " Unrecognised data type '" + columnDataType + "' for column '" + GetName(columnIdx) + "'");
break;
}
}
catch (Exception exc)
{
SaLogger.Warn(fn + " Data type: "+ columnDataType + ". Exception: " + exc.Message /*+ "\n" + exc.ToString()*/);
#if DEBUG
if (!handlingGetFieldValueException)
{
// Reinvoke for debugging.
handlingGetFieldValueException = true;
DataValue value = GetFieldValue(columnIdx);
handlingGetFieldValueException = false;
}
#endif
}
return dataValue;
}
// Get Record Values.
public NamedValue.Map GetRecordValues ()
{
NamedValue.Map values = null;
values = new NamedValue.Map();
for (int columnIdx = 0; columnIdx < FieldCount; columnIdx++)
{
String columnName = GetName(columnIdx);
DataValue dataValue = GetFieldValue(columnIdx);
if (dataValue != null)
values.AddValue(columnName, dataValue);
}
return values;
}
//-------------------------------------------------------------------
// Logging.
public static SaMessageLevel LoggingLevel { get; set; } = SaMessageLevel.None;
public static String LoggingPrefix { get; set; } = " ~~~ ";
protected static void Log (String fn, String message, SaMessageLevel level = SaMessageLevel.Debug)
{
if ((int)level > (int)LoggingLevel) return;
SaLogger.Log((fn + "\n\t" + message).Trim(),level);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,29 @@
using System;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.Dbas
{
//=======================================================================
public class DbUpdateInfo
{
//-------------------------------------------------------------------
// Construction.
public DbUpdateInfo (SysId userId, String userName, String host)
{
UserId = SysId.InvalidOnNull(userId);
UserName = StringUtil.EmptyOnNull(userName);
Host = StringUtil.EmptyOnNull(host);
}
//-------------------------------------------------------------------
// Properties.
public SysId UserId { get; private set; }
public String UserName { get; private set; }
public String Host { get; private set; }
//-------------------------------------------------------------------
}
//=======================================================================
}
+10
View File
@@ -0,0 +1,10 @@
namespace Sarand.Net.Standard.Base.Dbas
{
//=======================================================================
public static class DbUtil
{
//-------------------------------------------------------------------
//-------------------------------------------------------------------
}
//=======================================================================
}
+54
View File
@@ -0,0 +1,54 @@
using Sarand.Net.Standard.Base.Dbas;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Text;
namespace Sarand.Net.Standard.Base.Dbas
{
//=======================================================================
/// <summary>
/// Database information - object supplied by main application.
/// <para>NB Application may deploy to databases with schema differences.</para>
/// <para>Each instance of a DbDataItem must be database specific, ie. must be able to distinguish the schema in use.</para>
/// </summary>
public interface IDatabaseInfo
{
String Context { get; }
bool TableExists(String schemaName, String tableName);
bool ColumnExists(String schemaName, String tableName, String columnName);
List<DbDfnColumn> GetColumnsForTable(String schemaName, String tableName);
}
//=======================================================================
public interface IDbDataItem
{
//-------------------------------------------------------------------
// Population.
void PopulateFrom (DbCommand dbCommand, String tableAlias, String filter);
void PopulateFrom (DbDataReader dbdataReader);
//-------------------------------------------------------------------
// Retrieve field values.
String GetFieldString (DbDataReader dbDataReader, String name, String defaultValue);
Boolean GetFieldBoolean (DbDataReader dbDataReader, String name, Boolean defaultValue);
Int16 GetFieldInt16 (DbDataReader dbDataReader, String name, Int16 defaultValue);
Byte GetFieldByte (DbDataReader dbDataReader, String name, Byte defaultValue);
Int32 GetFieldInt32 (DbDataReader dbDataReader, String name, Int32 defaultValue);
Int64 GetFieldInt64 (DbDataReader dbDataReader, String name, Int64 defaultValue);
Single GetFieldSingle (DbDataReader dbDataReader, String name, Single defaultValue);
Double GetFieldDouble (DbDataReader dbDataReader, String name, Double defaultValue);
Decimal GetFieldDecimal (DbDataReader dbDataReader, String name, Decimal defaultValue);
DateTime GetFieldDateTime (DbDataReader dbDataReader, String name, DateTime defaultValue);
//-------------------------------------------------------------------
// Database updates via ODBC Command.
bool ExecuteInsertCommand (DbCommand dbCommand);
bool ExecuteUpdateCommand (DbCommand dbCommand);
//bool ExecuteDeleteCommand (DbCommand dbCommand);
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,9 @@
namespace Sarand.Net.Standard.Base.Dbas
{
//=======================================================================
public interface IDbDataTable
{
}
//=======================================================================
}
@@ -0,0 +1,37 @@
using System;
using Sarand.Net.Standard.Base.Core;
namespace Sarand.Net.Standard.Base.Dbas
{
//=======================================================================
interface IDbResultSet : IDisposable
{
//-------------------------------------------------------------------
// Navigation.
bool NextRecord ();
void Close ();
//-------------------------------------------------------------------
// Attributes.
int FieldCount { get; }
bool HasRows { get; }
int GetOrdinal (String columnName);
String GetDataTypeName (int columnIdx);
String GetName (int columnIdx);
bool GetBoolean (int columnIdx);
short GetInt16 (int columnIdx);
int GetInt32 (int columnIdx);
long GetInt64 (int columnIdx);
float GetFloat (int columnIdx);
double GetDouble (int columnIdx);
decimal GetDecimal (int columnIdx);
String GetString (int columnIdx);
DateTime GetDateTime (int columnIdx);
//-------------------------------------------------------------------
// Field values.
DataValue GetFieldValue (String columnName);
DataValue GetFieldValue (int columnIdx);
NamedValue.Map GetRecordValues ();
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Data.Common;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
namespace Sarand.Net.Standard.Base.Dbas
{
//=======================================================================
// Definitions.
public enum DbmsType
{
General,
Access,
MsSql,
MySql,
Oracle,
Pervasive,
Dbase,
FoxPro,
Paradox,
Interbase,
/// <summary>No 'AS' in table alias, no 'OUTER' in join type.</summary>
Sage,
QBooks,
SybaseAdvantage,
}
public enum SelectLockMode
{
/// <summary>No specific lock mode requested - use DBMS default.</summary>
Default,
/// <summary>Shared locks (MSSQL default). [MSSQL]</summary>
ReadCommitted,
/// <summary>Dirty reads - no locking. [MSSQL]</summary>
ReadUncommitted,
/// <summary>Locks all data in query. [MSSQL]</summary>
RepeatableRead,
/// <summary>Range lock on data preventing modifications. [MSSQL]</summary>
Serializable,
};
/// <summary>Reserved word delimiters - used by database definitions.</summary>
public enum DbReservedWordDelim
{
None,
SquareBrackets,
DoubleQuotes
};
public enum DbGetValueType { eDbValueSum, eDbValueMin, eDbValueMax };
//=======================================================================
public interface ISacsDatabase : ISacsStateItem
{
//-------------------------------------------------------------------
// Database properties.
String ConnectionString { get; }
DbmsType DbmsType { get; }
String DatabaseName { get; }
SelectLockMode SelectLockMode { get; set; }
bool DisplayErrors { get; set; }
DbConnection CreateConnection(bool forceOdbc = false);
//-------------------------------------------------------------------
// Identity generation.
SysId GetNextId(String objectName, DbUpdateInfo dbUpdateInfo, bool databaseTable = true, String idColumn = "Id");
int GetNextIdFromTable(String objectName, String idColumn = "Id");
bool SetMinNextId(String objectName, SysId id, DbUpdateInfo dbUpdateInfo, bool databaseTable = true, String idColumn = "Id", bool force = false);
//-------------------------------------------------------------------
// Database schema.
List<String> GetTableNames ();
List<DbDfnTable> GetTableDfns (bool refresh = false);
DbDfnTable GetTableDfn (String name, bool refresh = false);
bool TableExists (String tableName);
bool ColumnExists (String tableName, String columnName);
bool IndexExists (String tableName, String indexName);
//-------------------------------------------------------------------
// Query records.
String GetSelectQueryPrefix ();
IDbDataTable ExecuteQuery (String query);
int ExecuteNonQuery (String query);
int ExecuteInsert (String tableName, List<StringPair> columnNameValues);
int ExecuteUpdate (String tableName, List<StringPair> columnNameValues, String filter);
int ExecuteDelete (String tableName, String filter);
// Specific queries.
long GetRowCount (String tableName, String filter);
DataValue GetRowValue (String tableName, String columnName, String filter);
DataValue GetRowValue (String tableName, String columnName, String filter, DbGetValueType getValueType);
//-------------------------------------------------------------------
}
//=======================================================================
}
+257
View File
@@ -0,0 +1,257 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
namespace Sarand.Net.Standard.Base.Dbas
{
//=======================================================================
public static class OdbcUtil
{
//-------------------------------------------------------------------
// O d b c D r i v e r s
//-------------------------------------------------------------------
[DllImport("odbccp32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool SQLGetInstalledDriversW (char[] lpszBuf, ushort cbufMax, out ushort pcbBufOut);
/// <summary>
/// Gets the ODBC driver names from the SQLGetInstalledDrivers function.
/// </summary>
/// <returns>a string array containing the ODBC driver names, if the call to SQLGetInstalledDrivers was successfull; null, otherwise.</returns>
public static string[] GetOdbcDriverNames ()
{
string[] odbcDriverNames = null;
char[] driverNamesBuffer = new char[ushort.MaxValue];
ushort size;
bool succeeded = SQLGetInstalledDriversW(driverNamesBuffer, ushort.MaxValue, out size);
if (succeeded == true)
{
char[] driverNames = new char[size - 1];
Array.Copy(driverNamesBuffer, driverNames, size - 1);
odbcDriverNames = (new string(driverNames)).Split('\0');
}
return odbcDriverNames;
}
/// <summary>
/// Gets the name of an ODBC driver for Microsoft SQL Server giving preference to the most recent one.
/// </summary>
/// <returns>the name of an ODBC driver for Microsoft SQL Server, if one is present; null, otherwise.</returns>
public static string GetOdbcSqlDriverName ()
{
List<string> driverPrecedence = new List<string>() { "SQL Server Native Client 14.0", "SQL Server Native Client 13.0", "SQL Server Native Client 12.0", "SQL Server Native Client 11.0", "SQL Server Native Client 10.0", "SQL Server Native Client 9.0", "SQL Server" };
string[] availableOdbcDrivers = GetOdbcDriverNames();
string driverName = null;
if (availableOdbcDrivers != null)
{
driverName = driverPrecedence.Intersect(availableOdbcDrivers).FirstOrDefault();
}
return driverName;
}
//-------------------------------------------------------------------
// SQL Data Type to Native Data Type
//-------------------------------------------------------------------
public static short GetSqlNativeDataType (String dataTypeName)
{
switch (dataTypeName)
{
case "varchar": return 12; // SQL_VARCHAR
case "nvarchar": return -9; // SQL_WVARCHAR
case "bit": return -7; // SQL_BIT
case "tinyint": return -6; // SQL_TINYINT
case "smallint": return 5; // SQL_SMALLINT
case "int": return 4; // SQL_INTEGER
case "bigint": return -5; // SQL_BIGINT
case "real": return 7; // SQL_REAL
case "float": return 6; // SQL_FLOAT
case "decimal": return 3; // SQL_DECIMAL
case "numeric": return 2; // SQL_NUMERIC
case "datetime": return 93; // SQL_TYPE_TIMESTAMP
case "smalldatetime": return 93; // SQL_TYPE_TIMESTAMP
case "varbinary": return 93; // SQL_TYPE_TIMESTAMP
default:
SaLogger.Warn("OdbcUtil.GetSqlNativeDataType Unrecognised SQL data type: '" + dataTypeName + "'");
break;
}
return 0;
}
//-------------------------------------------------------------------
// O d b c T o D a t a T y p e
//-------------------------------------------------------------------
// ANSI SQL data type C++ data type
// CHAR CString
// DECIMAL CString 1
// SMALLINT int
// REAL float
// INTEGER long
// FLOAT double
// DOUBLE double
// NUMERIC CString 1
// VARCHAR CString
// LONGVARCHAR CLongBinary, CString 2
// BIT BOOL
// TINYINT BYTE
// BIGINT CString 1
// BINARY CByteArray
// VARBINARY CByteArray
// LONGVARBINARY CLongBinary, CByteArray 3
// DATE CTime, CString
// TIME CTime, CString
// TIMESTAMP CTime, CString
// Notes:
// 1. ANSI DECIMAL and NUMERIC map to CString because SQL_C_CHAR is the default ODBC transfer type.
// 2. Character data beyond 255 characters is truncated by default when mapped to CString.
// You can extend the truncation length by explicitly setting the nMaxLength argument of RFX_Text.
// 3. Binary data beyond 255 characters is truncated by default when mapped to CByteArray.
// You can extend the truncation length by explicitly setting the nMaxLength argument of RFX_Binary.
//-------------------------------------------------------------------
public static DbColumnDataType OdbcToDataType (short dataType)
{
// Data types:
// 2 DECIMAL, CURRENCY
// 4 INTEGER, COUNTER
// 5 SMALLINT
// 7 REAL
// 8 DOUBLE
// 11 DATETIME (Date & Time)
// 12 VARCHAR
// -6 BYTE
// -7 BIT (YesNo)
// -11 GUID (NumberReplId)
DbColumnDataType type = DbColumnDataType.Unknown;
switch (dataType)
{
//---------------------------------------------------------------
case 1: // SQL_CHAR: // 1 CHAR(n) Character string of fixed string length n.
case 12: // SQL_VARCHAR: // 12 VARCHAR(n) Variable-length character string with a maximum string length n.
case -1: // SQL_LONGVARCHAR: // -1 LONG VARCHAR Variable length character data. Maximum length is data source–dependent.[9]
case -8: // SQL_WCHAR: // -8 WCHAR(n) Unicode character string of fixed string length n
case -9: // SQL_WVARCHAR: // -9 VARWCHAR(n) Unicode variable-length character string with a maximum string length n
case -10: // SQL_WLONGVARCHAR: // -10 LONGWVARCHAR Unicode variable-length character data. Maximum length is data source–dependent
type = DbColumnDataType.VarChar;
break;
//---------------------------------------------------------------
case 3: // SQL_DECIMAL: // 3 DECIMAL(p,s) Signed, exact, numeric value with a precision of at least p and scale s. (The maximum precision is driver-defined.) (1 <= p <= 15; s <= p).[4]
case 2: // SQL_NUMERIC: // 2 NUMERIC(p,s) Signed, exact, numeric value with a precision p and scale s (1 <= p <= 15; s <= p).[4]
type = DbColumnDataType.Decimal;
break;
//---------------------------------------------------------------
case -6: // SQL_TINYINT: // -6 TINYINT Exact numeric value with precision 3 and scale 0 (signed: –128 <= n <= 127, unsigned:0 <= n <= 255)[3].
type = DbColumnDataType.TinyInt; //eByte
break;
//---------------------------------------------------------------
case 5: // SQL_SMALLINT: // 5 SMALLINT Exact numeric value with precision 5 and scale 0 (signed: –32,768 <= n <= 32,767, unsigned: 0 <= n <= 65,535)[3].
type = DbColumnDataType.SmallInt;
break;
//---------------------------------------------------------------
case 4: // SQL_INTEGER: // 4 INTEGER Exact numeric value with precision 10 and scale 0 (signed: –2[31] <= n <= 2[31] – 1, unsigned:0 <= n <= 2[32] – 1)[3].
type = DbColumnDataType.Int;
break;
//---------------------------------------------------------------
case 7: // SQL_REAL: // 7 REAL Signed, approximate, numeric value with a binary precision 24 (zero or absolute value 10[–38] to 10[38]).
type = DbColumnDataType.Single;
break;
//---------------------------------------------------------------
case 6: // SQL_FLOAT: // 6 FLOAT(p) Signed, approximate, numeric value with a binary precision of at least p. (The maximum precision is driver-defined.)[5]
case 8: // SQL_DOUBLE: // 8 DOUBLE PRECISION Signed, approximate, numeric value with a binary precision 53 (zero or absolute value 10[–308] to 10[308]).
type = DbColumnDataType.Double;
break;
//---------------------------------------------------------------
case 9: // SQL_DATETIME: // 9 DateTime ODBC
case 11: // 11 DateTime Access DateTime format (SQL_TIMESTAMP?)
case 91: // SQL_TYPE_DATE: // 91 DATE Year, month, and day fields, conforming to the rules of the Gregorian calendar.
case 92: // SQL_TYPE_TIME: // 92 TIME(p) Hour, minute, and second fields, with valid values for hours of 00 to 23, valid values for minutes of 00 to 59, and valid values for seconds of 00 to 61. Precision p indicates the seconds precision.
case 93: // SQL_TYPE_TIMESTAMP: // 93 Time Stamp TIMESTAMP(p) - Year, month, day, hour, minute, and second fields, with valid values as defined for the DATE and TIME data types.
type = DbColumnDataType.DateTime;
break;
//---------------------------------------------------------------
case -7: // SQL_BIT: // -7 BIT Single bit binary data.[8]
type = DbColumnDataType.Bit;
break;
//---------------------------------------------------------------
case -2: // SQL_BINARY: // -2 BINARY(n) Binary data of fixed length n.[9]
case -3: // SQL_VARBINARY: // -3 VARBINARY(n) Variable length binary data of maximum length n. The maximum is set by the user.[9]
case -4: // SQL_LONGVARBINARY: // -4 LONG VARBINARY Variable length binary data. Maximum length is data source–dependent.[9]
type = DbColumnDataType.Binary;
break;
//---------------------------------------------------------------
case -5: // SQL_BIGINT: // -5 BIGINT Exact numeric value with precision 19 (if signed) or 20 (if unsigned) and scale 0 (signed: –2[63] <= n <= 2[63] – 1, unsigned:0 <= n <= 2[64] – 1)[3],[9].
case 102: // SQL_INTERVAL_MONTH: // 102 INTERVAL MONTH(p) Number of months between two dates; p is the interval leading precision.
case 101: // SQL_INTERVAL_YEAR: // 101 INTERVAL YEAR(p) Number of years between two dates; p is the interval leading precision.
case 107: // SQL_INTERVAL_YEAR_TO_MONTH: // 107 INTERVAL YEAR(p) TO MONTH Number of years and months between two dates; p is the interval leading precision.
case 103: // SQL_INTERVAL_DAY: // 103 INTERVAL DAY(p) Number of days between two dates; p is the interval leading precision.
case 104: // SQL_INTERVAL_HOUR: // 104 INTERVAL HOUR(p) Number of hours between two date/times; p is the interval leading precision.
case 105: // SQL_INTERVAL_MINUTE: // 105 INTERVAL MINUTE(p) Number of minutes between two date/times; p is the interval leading precision.
case 106: // SQL_INTERVAL_SECOND: // 106 INTERVAL SECOND(p,q) Number of seconds between two date/times; p is the interval leading precision and q is the interval seconds precision.
case 108: // SQL_INTERVAL_DAY_TO_HOUR: // 108 INTERVAL DAY(p) TO HOUR Number of days/hours between two date/times; p is the interval leading precision.
case 109: // SQL_INTERVAL_DAY_TO_MINUTE: // 109 INTERVAL DAY(p) TO MINUTE Number of days/hours/minutes between two date/times; p is the interval leading precision.
case 110: // SQL_INTERVAL_DAY_TO_SECOND: // 110 INTERVAL DAY(p) TO SECOND(q) Number of days/hours/minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision.
case 111: // SQL_INTERVAL_HOUR_TO_MINUTE: // 111 INTERVAL HOUR(p) TO MINUTE Number of hours/minutes between two date/times; p is the interval leading precision.
case 112: // SQL_INTERVAL_HOUR_TO_SECOND: // 112 INTERVAL HOUR(p) TO SECOND(q) Number of hours/minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision.
case 113: // SQL_INTERVAL_MINUTE_TO_SECOND: // 113 INTERVAL MINUTE(p) TO SECOND(q) Number of minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision.
case -11: // SQL_GUID: // -11 GUID Fixed length Globally Unique Identifier.
break;
//---------------------------------------------------------------
default:
SaLogger.Debug("OdbcUtil.OdbcToDataType Unrecognised native data type: " + dataType);
break;
//---------------------------------------------------------------
}
return type;
}
//-------------------------------------------------------------------
public static String GetOdbcDataTypeCaption (short dataType)
{
switch (dataType)
{
//---------------------------------------------------------------
case 1: return "Char"; // SQL_CHAR
case 12: return "VarChar"; // SQL_VARCHAR
case -1: return "LongVarChar"; // SQL_LONGVARCHAR
case -8: return "WChar"; // SQL_WCHAR
case -9: return "VarWChar"; // SQL_WVARCHAR
case -10: return "LongWVarChar"; // SQL_WLONGVARCHAR
case 3: return "Decimal"; // SQL_DECIMAL
case 2: return "Numeric"; // SQL_NUMERIC
case -6: return "TinyInt"; // SQL_TINYINT
case 5: return "SmallInt"; // SQL_SMALLINT
case 4: return "Integer"; // SQL_INTEGER
case 7: return "Real"; // SQL_REAL
case 6: return "Float"; // SQL_FLOAT
case 8: return "Double"; // SQL_DOUBLE
case 9: return "DateTime"; // SQL_DATETIME
case 11: return "TimeStamp_11";
case 91: return "Date"; // SQL_TYPE_DATE
case 92: return "Time"; // SQL_TYPE_TIME
case 93: return "TimeStamp"; // SQL_TYPE_TIMESTAMP
case -7: return "Bit"; // SQL_BIT
case -5: return "BigInt"; // SQL_BIGINT
case -2: return "Binary"; // SQL_BINARY
case -3: return "VarBinary"; // SQL_VARBINARY
/*
case SQL_LONGVARBINARY: // -4 LONG VARBINARY Variable length binary data. Maximum length is data source–dependent.[9]
case SQL_INTERVAL_MONTH: // 102 INTERVAL MONTH(p) Number of months between two dates; p is the interval leading precision.
case SQL_INTERVAL_YEAR: // 101 INTERVAL YEAR(p) Number of years between two dates; p is the interval leading precision.
case SQL_INTERVAL_YEAR_TO_MONTH: // 107 INTERVAL YEAR(p) TO MONTH Number of years and months between two dates; p is the interval leading precision.
case SQL_INTERVAL_DAY: // 103 INTERVAL DAY(p) Number of days between two dates; p is the interval leading precision.
case SQL_INTERVAL_HOUR: // 104 INTERVAL HOUR(p) Number of hours between two date/times; p is the interval leading precision.
case SQL_INTERVAL_MINUTE: // 105 INTERVAL MINUTE(p) Number of minutes between two date/times; p is the interval leading precision.
case SQL_INTERVAL_SECOND: // 106 INTERVAL SECOND(p,q) Number of seconds between two date/times; p is the interval leading precision and q is the interval seconds precision.
case SQL_INTERVAL_DAY_TO_HOUR: // 108 INTERVAL DAY(p) TO HOUR Number of days/hours between two date/times; p is the interval leading precision.
case SQL_INTERVAL_DAY_TO_MINUTE: // 109 INTERVAL DAY(p) TO MINUTE Number of days/hours/minutes between two date/times; p is the interval leading precision.
case SQL_INTERVAL_DAY_TO_SECOND: // 110 INTERVAL DAY(p) TO SECOND(q) Number of days/hours/minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision.
case SQL_INTERVAL_HOUR_TO_MINUTE: // 111 INTERVAL HOUR(p) TO MINUTE Number of hours/minutes between two date/times; p is the interval leading precision.
case SQL_INTERVAL_HOUR_TO_SECOND: // 112 INTERVAL HOUR(p) TO SECOND(q) Number of hours/minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision.
case SQL_INTERVAL_MINUTE_TO_SECOND: // 113 INTERVAL MINUTE(p) TO SECOND(q) Number of minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision.
case SQL_GUID: // -11 GUID Fixed length Globally Unique Identifier.
*/
default:
break;
//---------------------------------------------------------------
}
return "Type " + dataType;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,42 @@
using Sarand.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sarand.Net.Standard.Base.Diary
{
//=======================================================================
public abstract class SaDiaryConsole : SaErrorStateItem
{
//-------------------------------------------------------------------
// Definitions.
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Static initialisation.
static SaDiaryConsole ()
{
}
//-------------------------------------------------------------------
// Construction.
protected SaDiaryConsole ()
{
}
//-------------------------------------------------------------------
// Properties.
// Database.
public abstract bool DatabaseIsValid { get; }
public abstract bool CanUpdateDatabase { get; }
//-------------------------------------------------------------------
// D i a r y E n t r i e s
//-------------------------------------------------------------------
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sarand.Net.Standard.Base.Email
{
//=======================================================================
public class SaEmailAttachmentData
{
//-------------------------------------------------------------------
// Construction.
public SaEmailAttachmentData() : this("", "") { }
public SaEmailAttachmentData(String mediaType, String mediaSubtype)
{
ContentType.MediaType = mediaType;
ContentType.MediaSubtype = mediaSubtype;
}
//-------------------------------------------------------------------
// Properties.
public bool Inline { get; set; }
/// <summary>Filename - identity for non-inline attachments.</summary>
public String Filename { get; set; }
/// <summary>ContentId - identity for inline attachments.</summary>
public String ContentId { get; set; }
/// <summary>Attachment data - as byte array.</summary>
public byte[] Data { get; set; }
public class ContentTypeData
{
public String MediaType { get; set; } = "application";
public String MediaSubtype { get; set; } = "octet-stream";
}
/// <summary>Content type identified as Media and sub media types, eg. 'Image','png'.</summary>
public ContentTypeData ContentType { get; } = new ContentTypeData();
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,46 @@
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sarand.Net.Standard.Base.Email
{
//=======================================================================
public class SaEmailConfig
{
//-------------------------------------------------------------------
// Construction.
public SaEmailConfig(String context)
{
Context = StringUtil.EmptyOnNull(context);
}
//-------------------------------------------------------------------
// Properties.
public String Context { get; }
public class MessageData
{
public String AddressFrom { get; set; }
public List<String> AddressesBcc { get; } = new List<string>();
}
public MessageData Message { get; } = new MessageData();
public class SMTPData
{
public String Server { get; set; }
public int ServerPort { get; set; }
public bool UseSSL { get; set; }
}
public SMTPData SMTP { get; } = new SMTPData();
public class SendData
{
public String Using { get; set; }
public String UserName { get; set; }
public String Password { get; set; }
}
public SendData Send { get; } = new SendData();
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,194 @@
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace Sarand.Net.Standard.Base.Email
{
//=======================================================================
public abstract class SaEmailConsole
{
//-------------------------------------------------------------------
// Protected construction.
protected SaEmailConsole()
{
}
//-------------------------------------------------------------------
// Send email.
//-------------------------------------------------------------------
public bool SendEmail(SaEmailData emailData)
{
String fn = GetType().Name + ".SendEmail";
// Validate email data.
if (emailData.AddressesTo.IsEmpty())
{
SaLogger.Error(fn + " Unable to send email - To address not specified.");
return false;
}
try
{
// Mail message.
MimeMessage mailMessage = CreateMailMessage(emailData);
// Send the email.
using (var smtpClient = new SmtpClient())
{
smtpClient.ServerCertificateValidationCallback = (s, c, h, e) => true;
smtpClient.Connect(emailData.Config.SMTP.Server, emailData.Config.SMTP.ServerPort, emailData.Config.SMTP.UseSSL ? SecureSocketOptions.StartTlsWhenAvailable : SecureSocketOptions.Auto);
if (!StringUtil.IsEmpty(emailData.Config.Send.UserName))
smtpClient.Authenticate(emailData.Config.Send.UserName, emailData.Config.Send.Password);
smtpClient.Send(mailMessage);
smtpClient.Disconnect(true);
}
}
catch (Exception exc)
{
SaLogger.Error(fn + " " + ExceptionUtil.GetLogMessage(exc));
return false;
}
SaLogger.Info(fn + " Email sent to " + emailData.AddressesTo[0] + ". Subject: " + emailData.Subject);
return true;
}
//-------------------------------------------------------------------
private MimeMessage CreateMailMessage(SaEmailData emailData)
{
// Mail message.
MimeMessage mailMessage = new MimeMessage();
// Addresses.
mailMessage.From.Add(new MailboxAddress("", emailData.Config.Message.AddressFrom));
foreach (String addressTo in emailData.AddressesTo)
mailMessage.To.Add(new MailboxAddress("", addressTo));
foreach (String addressCc in emailData.AddressesCc)
mailMessage.Cc.Add(new MailboxAddress("", addressCc));
foreach (String addressBcc in emailData.AddressesBcc)
mailMessage.Bcc.Add(new MailboxAddress("", addressBcc));
foreach (String addressBcc in emailData.Config.Message.AddressesBcc)
mailMessage.Bcc.Add(new MailboxAddress("", addressBcc));
// Body builder.
var builder = new BodyBuilder();
// First add the message itself.
mailMessage.Subject = emailData.Subject;
if (emailData.HtmlBody)
builder.HtmlBody = emailData.MessageBody;
else
builder.TextBody = emailData.MessageBody;
// Attachments.
int attachmentCount = 0;
foreach (SaEmailAttachmentData attachmentData in emailData.Attachments)
{
attachmentCount++;
// Construct attachment.
MimePart attachment = new MimePart(attachmentData.ContentType.MediaType, attachmentData.ContentType.MediaSubtype)
{
Content = new MimeContent(new MemoryStream(attachmentData.Data)),
ContentDisposition = new ContentDisposition(attachmentData.Inline ? ContentDisposition.Inline : ContentDisposition.Attachment),
ContentTransferEncoding = ContentEncoding.Base64
};
// All attachments should have a Filename.
attachment.FileName = attachmentData.Filename;
if (StringUtil.IsEmpty(attachment.FileName))
attachment.FileName = "Attachment_" + StringUtil.PadLeft(attachmentCount.ToString(), 3, '0') + ".dat";
// Inline attachments also have a ContentId.
if (attachmentData.Inline)
{
attachment.ContentId = attachmentData.ContentId;
if (StringUtil.IsEmpty(attachmentData.ContentId))
attachmentData.ContentId = "Attachment_" + StringUtil.PadLeft(attachmentCount.ToString(), 3, '0') + "@saxonite.com";
// Add the inline attachment as a linked resource.
builder.LinkedResources.Add(attachment);
}
else
{
// Add the attachment.
builder.Attachments.Add(attachment);
}
}
// Populate the message body.
mailMessage.Body = builder.ToMessageBody();
return mailMessage;
}
//-------------------------------------------------------------------
// Replace dynamic Email Body tags.
//-------------------------------------------------------------------
// Tags.
/// <summary>Check whether the body includes a specific tag.</summary>
public static bool EmailContainsTag(SaEmailData emailData, String tag)
{
return (Regex.Matches(emailData.MessageBody, Regex.Escape(tag), RegexOptions.IgnoreCase).Count > 0);
}
/// <summary>Replace a dynamic tag in the email message.</summary>
public static void ReplaceDynamicEmailTag(SaEmailData emailData, String tag, String content)
{
if (EmailContainsTag(emailData, tag))
emailData.MessageBody = Regex.Replace(emailData.MessageBody, Regex.Escape(tag), content ?? "", RegexOptions.IgnoreCase);
}
/// <summary>
/// Replace dynamic tags in email message that are known to the base project eg.:
/// <para>- stylesheet (including style tags).</para>
/// </summary>
public static void ReplaceDynamicEmailTags(SaEmailData emailData)
{
// Nothing to do.
}
//-------------------------------------------------------------------
protected void AddInlineImageToEmail(SaEmailData emailData, String imageResourceName, byte[] imageData, String imageType = "png")
{
string tag = "[#image." + imageResourceName + "]";
String imageFilename = Regex.Replace(imageResourceName, "_" + imageType, "." + imageType);
if (Regex.Matches(emailData.MessageBody, Regex.Escape(tag), RegexOptions.IgnoreCase).Count > 0)
{
String imageContentId = FormatAttachmentContentId(Regex.Replace(imageFilename, "\\.", "_"));
if (!emailData.Attachments.Exists(item => StringUtil.EmptyOnNull(item.ContentId).EqualsCI(imageContentId)))
{
// Image not yet in attachments.
SaEmailAttachmentData attachment = new SaEmailAttachmentData("image", imageResourceName)
{
Filename = imageFilename,
ContentId = imageContentId,
Data = imageData,
Inline = true
};
emailData.Attachments.Add(attachment);
}
String content = "cid:" + imageContentId;
emailData.MessageBody = Regex.Replace(emailData.MessageBody, Regex.Escape(tag), content, RegexOptions.IgnoreCase);
}
}
//-------------------------------------------------------------------
public static String FormatAttachmentContentId(String attachmentName)
{
// Inline content Ids should be formatted similar to an email address (user@domain).
// Replace '@' and whitespace with underscores and add domain.
String contentId = Regex.Replace(Regex.Replace(Regex.Replace(attachmentName, "@", "_"), "\\s+", "_"), "_+", "_") + "@saxonite.com";
return contentId;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sarand.Net.Standard.Base.Email
{
//=======================================================================
public class SaEmailData
{
//-------------------------------------------------------------------
// Properties.
public SaEmailConfig Config { get; set; }
public List<String> AddressesTo { get; } = new List<string>();
public List<String> AddressesCc { get; } = new List<string>();
public List<String> AddressesBcc { get; } = new List<string>();
public String Subject { get; set; }
public String MessageBody { get; set; }
public bool HtmlBody { get; set; }
public List<SaEmailAttachmentData> Attachments { get; } = new List<SaEmailAttachmentData>();
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,239 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.StaticFiles;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace Sarand.Net.Standard.Base.Helpers
{
//==========================================================================
public static class SacsContentTypeHelper
{
//-------------------------------------------------------------------
// Definition - Content Type Data.
public class ContentTypeData
{
protected ContentTypeData(List<String> extensions, String caption, String contentType, Sacs.MimeType mime)
{
ContentType = contentType;
Caption = caption;
Extensions.AddRange(extensions);
Mime = mime;
}
public String PreferredContentType { get; protected set; }
public String ContentType { get; }
public String Caption { get; }
public List<String> Extensions { get; } = new List<string>();
public Sacs.MimeType Mime { get; }
}
private class ContentTypeData_Internal : ContentTypeData
{
public ContentTypeData_Internal(String extension, String caption, String contentType, Sacs.MimeType mime = Sacs.MimeType.Unknown) :
base(StringListUtil.Split(extension, ",", true, true, true), caption, contentType, mime)
{ }
public new String PreferredContentType { set { base.PreferredContentType = value; } }
}
//-------------------------------------------------------------------
// Data members.
private static readonly List<ContentTypeData> contentTypes;
private static FileExtensionContentTypeProvider provider = null;
private static readonly HashSet<Sacs.MimeType> allowedMimeTypes_CandidateCV = new HashSet<Sacs.MimeType>()
{
Sacs.MimeType.MsDoc,
Sacs.MimeType.MsDocx,
Sacs.MimeType.OpenDocText,
Sacs.MimeType.Pdf,
Sacs.MimeType.Rtf
};
//-------------------------------------------------------------------
// Static initialisation.
static SacsContentTypeHelper()
{
// A few useful content types for reference.
contentTypes = new List<ContentTypeData>()
{
// Data.
new ContentTypeData_Internal("txt", "Text, (generally ASCII or ISO 8859-n)", "text/plain", Sacs.MimeType.Text),
new ContentTypeData_Internal("csv", "Comma separated variable", "text/csv", Sacs.MimeType.Csv),
new ContentTypeData_Internal("xml", "XML (not readable from casual users)", "application/xml", Sacs.MimeType.Xml) { PreferredContentType = "text/xml" },
new ContentTypeData_Internal("xml", "XML (readable from casual users)", "text/xml", Sacs.MimeType.Xml),
new ContentTypeData_Internal("zip", "ZIP archive", "application/x-zip-compressed", Sacs.MimeType.Zip),
new ContentTypeData_Internal("zip", "ZIP archive", "application/zip", Sacs.MimeType.Zip) { PreferredContentType = "application/x-zip-compressed" },
new ContentTypeData_Internal("bin", "Generic binary data", "application/octet-stream", Sacs.MimeType.Bin),
// Web.
new ContentTypeData_Internal("html,htm", "HyperText Markup Language (HTML)", "text/html", Sacs.MimeType.Html),
new ContentTypeData_Internal("css", "Cascading Style Sheets (CSS)", "text/css", Sacs.MimeType.Css),
new ContentTypeData_Internal("js", "JavaScript", "application/javascript", Sacs.MimeType.Javascript),
new ContentTypeData_Internal("js", "JavaScript", "text/javascript", Sacs.MimeType.Javascript) { PreferredContentType = "application/javascript" },
new ContentTypeData_Internal("json", "JSON format", "application/json", Sacs.MimeType.Json),
// Images.
new ContentTypeData_Internal("png", "Portable Network Graphics", "image/png", Sacs.MimeType.Png),
new ContentTypeData_Internal("jpg,jpeg", "JPEG images", "image/jpeg", Sacs.MimeType.Jpeg),
new ContentTypeData_Internal("bmp", "Windows OS/2 Bitmap Graphics", "image/bmp", Sacs.MimeType.Bmp),
new ContentTypeData_Internal("gif", "Graphics Interchange Format (GIF)", "image/gif", Sacs.MimeType.Gif),
new ContentTypeData_Internal("tif,tiff", "Tagged Image File Format (TIFF)", "image/tiff", Sacs.MimeType.Tif),
// Documents.
new ContentTypeData_Internal("pdf", "Adobe Portable Document Format (PDF)", "application/pdf", Sacs.MimeType.Pdf),
// Text documents.
new ContentTypeData_Internal("rtf", "Rich text format file", "application/rtf", Sacs.MimeType.Rtf),
new ContentTypeData_Internal("me", "ReadMe text file", "application/x-troff-me"),
new ContentTypeData_Internal("doc", "Microsoft Word", "application/msword", Sacs.MimeType.MsDoc),
new ContentTypeData_Internal("docx", "Microsoft Word (OpenXML)", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", Sacs.MimeType.MsDocx),
new ContentTypeData_Internal("odt", "OpenDocument text document", "application/vnd.oasis.opendocument.text", Sacs.MimeType.OpenDocText),
// Spreadsheets.
new ContentTypeData_Internal("xls", "Microsoft Excel", "application/vnd.ms-excel", Sacs.MimeType.MsXls),
new ContentTypeData_Internal("xlsx", "Microsoft Excel (OpenXML)", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", Sacs.MimeType.MsXlsx),
new ContentTypeData_Internal("ods", "OpenDocument spreadsheet document", "application/vnd.oasis.opendocument.spreadsheet", Sacs.MimeType.OpenDocSpreadsheet),
// Miscellaneous.
new ContentTypeData_Internal("aac", "AAC audio", "audio/aac"),
new ContentTypeData_Internal("abw", "AbiWord document", "application/x-abiword"),
new ContentTypeData_Internal("arc", "Archive document (multiple files embedded)", "application/x-freearc"),
new ContentTypeData_Internal("avi", "AVI: Audio Video Interleave", "video/x-msvideo"),
new ContentTypeData_Internal("azw", "Amazon Kindle eBook format", "application/vnd.amazon.ebook"),
new ContentTypeData_Internal("bz", "BZip archive", "application/x-bzip"),
new ContentTypeData_Internal("bz2", "BZip2 archive", "application/x-bzip2"),
new ContentTypeData_Internal("csh", "C-Shell script", "application/x-csh"),
new ContentTypeData_Internal("csv", "Comma-separated values (CSV)", "text/csv"),
new ContentTypeData_Internal("eot", "MS Embedded OpenType fonts", "application/vnd.ms-fontobject"),
new ContentTypeData_Internal("epub", "Electronic publication (EPUB)", "application/epub+zip"),
new ContentTypeData_Internal("gz", "GZip Compressed Archive", "application/x-gzip"),
new ContentTypeData_Internal("gz", "GZip Compressed Archive", "application/gzip") { PreferredContentType = "application/x-gzip" },
new ContentTypeData_Internal("ico", "Icon format", "image/x-icon"),
new ContentTypeData_Internal("ico", "Icon format", "image/vnd.microsoft.icon") { PreferredContentType = "image/x-icon" },
new ContentTypeData_Internal("ics", "iCalendar format", "text/calendar"),
new ContentTypeData_Internal("jar", "Java Archive (JAR)", "application/java-archive"),
new ContentTypeData_Internal("jsonld", "JSON-LD format", "application/ld+json"),
new ContentTypeData_Internal("midi,mid", "Musical Instrument Digital Interface (MIDI)", "audio/mid"),
new ContentTypeData_Internal("midi,mid", "Musical Instrument Digital Interface (MIDI)", "audio/midi") { PreferredContentType = "audio/mid" },
new ContentTypeData_Internal("midi,mid", "Musical Instrument Digital Interface (MIDI)", "audio/x-midi") { PreferredContentType = "audio/mid" },
new ContentTypeData_Internal("mjs", "JavaScript module", "text/javascript"),
new ContentTypeData_Internal("mp3", "MP3 audio", "audio/mpeg"),
new ContentTypeData_Internal("mpeg", "MPEG Video", "video/mpeg"),
new ContentTypeData_Internal("mpkg", "Apple Installer Package", "application/vnd.apple.installer+xml"),
new ContentTypeData_Internal("odp", "OpenDocument presentation document", "application/vnd.oasis.opendocument.presentation"),
new ContentTypeData_Internal("oga", "OGG audio", "audio/ogg"),
new ContentTypeData_Internal("ogv", "OGG video", "video/ogg"),
new ContentTypeData_Internal("ogx", "OGG", "application/ogg"),
new ContentTypeData_Internal("opus", "Opus audio", "audio/opus"),
new ContentTypeData_Internal("otf", "OpenType font", "font/otf"),
new ContentTypeData_Internal("php", "Hypertext Preprocessor (Personal Home Page)", "application/x-httpd-php"),
new ContentTypeData_Internal("ppt", "Microsoft PowerPoint", "application/vnd.ms-powerpoint"),
new ContentTypeData_Internal("pptx", "Microsoft PowerPoint (OpenXML)", "application/vnd.openxmlformats-officedocument.presentationml.presentation"),
new ContentTypeData_Internal("rar", "RAR archive", "application/vnd.rar"),
new ContentTypeData_Internal("rtf", "Rich Text Format (RTF)", "application/rtf"),
new ContentTypeData_Internal("sh", "Bourne shell script", "application/x-sh"),
new ContentTypeData_Internal("svg", "Scalable Vector Graphics (SVG)", "image/svg+xml"),
new ContentTypeData_Internal("swf", "Small web format (SWF) or Adobe Flash document", "application/x-shockwave-flash"),
new ContentTypeData_Internal("tar", "Tape Archive (TAR)", "application/x-tar"),
new ContentTypeData_Internal("ts", "MPEG transport stream", "video/vnd.dlna.mpeg-tts"),
new ContentTypeData_Internal("ts", "MPEG transport stream", "video/mp2t") { PreferredContentType = "video/vnd.dlna.mpeg-tts" },
new ContentTypeData_Internal("ttf", "TrueType Font", "application/x-font-ttf"),
new ContentTypeData_Internal("ttf", "TrueType Font", "font/ttf") { PreferredContentType = "application/x-font-ttf" },
new ContentTypeData_Internal("vsd", "Microsoft Visio", "application/vnd.visio"),
new ContentTypeData_Internal("wav", "Waveform Audio Format", "audio/wav"),
new ContentTypeData_Internal("webm", "WEBM video", "video/webm"),
new ContentTypeData_Internal("weba", "WEBM audio", "audio/webm"),
new ContentTypeData_Internal("webp", "WEBP image", "image/webp"),
new ContentTypeData_Internal("woff", "Web Open Font Format (WOFF)", "application/font-woff"),
new ContentTypeData_Internal("woff", "Web Open Font Format (WOFF)", "font/woff") { PreferredContentType = "application/font-woff" },
new ContentTypeData_Internal("woff2", "Web Open Font Format (WOFF)", "font/woff2"),
new ContentTypeData_Internal("xhtml", "XHTML", "application/xhtml+xml"),
new ContentTypeData_Internal("xul", "XUL", "application/vnd.mozilla.xul+xml"),
new ContentTypeData_Internal("3gp", "3GPP video container", "video/3gpp"),
new ContentTypeData_Internal("3gp", "3GPP audio container", "audio/3gpp") { PreferredContentType = "video/3gpp" },
new ContentTypeData_Internal("3g2", "3GPP2 video container", "video/3gpp2"),
new ContentTypeData_Internal("3g2", "3GPP2 audio container", "audio/3gpp2") { PreferredContentType = "video/3gpp2" },
new ContentTypeData_Internal("7z", "7-zip archive", "application/x-7z-compressed")
};
}
//-------------------------------------------------------------------
// Properties.
/// <summary>Generic content type used for 'unknown' document types.</summary>
public static String ContentTypeDefault { get; } = "application/octet-stream";
/// <summary>Selection of comon content types.</summary>
public static List<ContentTypeData> ContentTypes { get { return new List<ContentTypeData>(contentTypes); } }
public static FileExtensionContentTypeProvider Provider
{
get
{
if (provider == null)
provider = new FileExtensionContentTypeProvider();
return provider;
}
}
//-------------------------------------------------------------------
/// <summary>Generic content types that might have a better preferred content type when converting, eg. from extension.</summary>
private static readonly List<String> genericContentTypes = new List<string>() { "application/octet-stream", "application/oleobject" };
/// <summary>
/// Given a file path, determine the MIME type.
/// <para>Ref. NuGet package Microsoft.AspNetCore.StaticFiles</para>
/// </summary>
public static String GetContentTypeFromFilename(this String fileName)
{
string contentType = "";
if (!Provider.TryGetContentType(fileName, out contentType) || genericContentTypes.ContainsCI(contentType))
{
// Locate suitable 'common' content type definition.
String extension = Regex.Replace(Path.GetExtension(fileName), "^\\.", "");
// Use the 'primary' definition for the extension, with no 'preferred' content type.
ContentTypeData contentTypeData = contentTypes.Find(item => item.Extensions.ContainsCI(extension) && StringUtil.IsEmpty(item.PreferredContentType));
if (contentTypeData != null)
contentType = contentTypeData.ContentType;
}
if (StringUtil.IsEmpty(contentType))
contentType = ContentTypeDefault;
return contentType;
}
//----------------------------------------------------------------------
/// <summary>
/// Interpret the content type for the specified file.
/// <para>- The file's content type if not generic (application/octet-stream, application/oleobject).</para>
/// <para>- The content type derived from the file's extension if available</para>
/// <para>- Generic content type: application/octet-stream.</para>
/// </summary>
public static String GetContentTypeFromFormFile(String contentType, String fileName)
{
// Return file's content type if specific.
if (!StringUtil.IsEmpty(contentType) && !genericContentTypes.ContainsCI(contentType))
return contentType;
// Otherwise, interpret filename's content type.
if (!StringUtil.IsEmpty(fileName))
return SacsContentTypeHelper.GetContentTypeFromFilename(fileName);
return ContentTypeDefault;
}
/// <summary>
/// Interpret the content type for the specified file.
/// <para>- The file's content type if not generic (application/octet-stream, application/oleobject).</para>
/// <para>- The content type derived from the file's extension if available</para>
/// <para>- Generic content type: application/octet-stream.</para>
/// </summary>
public static String GetContentTypeFromFormFile(this IFormFile formFile)
{
return GetContentTypeFromFormFile(formFile.ContentType, formFile.FileName);
}
//----------------------------------------------------------------------
/// <summary>Convert the content type to enumerated Mime.</summary>
public static Sacs.MimeType GetMimeTypeFromContentType(this String contentType)
{
if (StringUtil.IsEmpty(contentType)) return Sacs.MimeType.Unknown;
ContentTypeData contentTypeData = contentTypes.Find(item => item.ContentType.EqualsCI(contentType));
if (contentTypeData != null)
return contentTypeData.Mime;
return Sacs.MimeType.Unknown;
}
//----------------------------------------------------------------------
}
//==========================================================================
}
@@ -0,0 +1,266 @@
using CsvHelper;
using CsvHelper.Configuration;
using Sarand.Net.Standard.Base.Core;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
namespace Sarand.Net.Standard.Base.Helpers
{
//=======================================================================
public static class SacsCsvHelper
{
//-------------------------------------------------------------------
// Definitions.
/// <summary>
/// Encapsulate CSV Reader and the text object that it is to parse.
/// </summary>
private class ReaderClass : SacsDisposable
{
public ReaderClass() : this(DefaultCulture) { }
public ReaderClass(CultureInfo culture)
{
Reader = new StringReader("");
CsvReader = new CsvReader(Reader, culture ?? DefaultCulture);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
CsvReader.Dispose();
Reader.Dispose();
}
}
public StringReader Reader { get; }
public CsvReader CsvReader { get; }
}
/// <summary>
/// Encapsulate CSV Writer and the text stream that it is to generate.
/// </summary>
private class WriterClass : SacsDisposable
{
// Construction.
public WriterClass() : this(DefaultCulture) { }
public WriterClass(CultureInfo culture)
{
Writer = new StringWriter() { NewLine = "" };
CsvWriter = new CsvWriter(Writer, culture ?? DefaultCulture);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
CsvWriter.Dispose();
Writer.Dispose();
}
}
// Properties.
public StringWriter Writer { get; }
public CsvWriter CsvWriter { get; }
// To CSV.
public String ToCsv(IEnumerable<String> values)
{
foreach (String field in values)
CsvWriter.WriteField(field);
CsvWriter.NextRecord();
// NB Shouldn't need to strip line delimiters as the writer's NewLine is blank but CsvHelper v16 still adds the delimiter.
String record = Writer.GetStringBuilder().ToString().Replace("\r", "").Replace("\n", "");
// Clear the String Writer data. [Ref. https://stackoverflow.com/questions/12839982/clear-all-content-in-xmltextwriter-and-stringwriter]
Writer.GetStringBuilder().Clear();
return record;
}
}
//-------------------------------------------------------------------
// Properties.
public static CultureInfo DefaultCulture { get; set; } = CultureInfo.InvariantCulture;
//-------------------------------------------------------------------
// C o n f i g u r a t i o n D e f a u l t s
//-------------------------------------------------------------------
// AllowComments false bool
// BadDataFound {Method = {Void BadDataFound(CsvHelper.ReadingContext)}} System.Action<CsvHelper.ReadingContext>
// BufferSize 2048 int
// Comment 35 '#' char
// CountBytes false bool
// CultureInfo {en-GB} System.Globalization.CultureInfo
// Delimiter "," string
// DetectColumnCountChanges false bool
// DoubleQuoteString "\"\"" string
// DynamicPropertySort null System.Collections.Generic.IComparer<string>
// Encoding {System.Text.UTF8Encoding} System.Text.Encoding {System.Text.UTF8Encoding}
// Escape 34 '"' char
// GetConstructor {Method = {System.Reflection.ConstructorInfo GetConstructor(System.Type)}} System.Func<System.Type, System.Reflection.ConstructorInfo>
// HasHeaderRecord true bool
// HeaderValidated {Method = {Void HeaderValidated(Boolean, System.String[], Int32, CsvHelper.ReadingContext)}} System.Action<bool, string[], int, CsvHelper.ReadingContext>
// IgnoreBlankLines true bool
// IgnoreQuotes false bool
// IgnoreReferences false bool
// IncludePrivateMembers false bool
// InjectionCharacters {char[4]} char[]
// InjectionEscapeCharacter 9 '\t' char
// LineBreakInQuotedFieldIsBadData false bool
// Maps {CsvHelper.Configuration.ClassMapCollection} CsvHelper.Configuration.ClassMapCollection
// MemberTypes Properties CsvHelper.Configuration.MemberTypes
// MissingFieldFound {Method = {Void MissingFieldFound(System.String[], Int32, CsvHelper.ReadingContext)}} System.Action<string[], int, CsvHelper.ReadingContext>
// PrepareHeaderForMatch {Method = {System.String PrepareHeaderForMatch(System.String, Int32)}} System.Func<string, int, string>
// Quote 34 '"' char
// QuoteString "\"" string
// ReadingExceptionOccurred {Method = {Boolean ReadingExceptionOccurred(CsvHelper.CsvHelperException)}} System.Func<CsvHelper.CsvHelperException, bool>
// ReferenceHeaderPrefix null System.Func<System.Type, string, string>
// SanitizeForInjection false bool
// ShouldQuote {Method = {Boolean ShouldQuote(System.String, CsvHelper.WritingContext)}} System.Func<string, CsvHelper.WritingContext, bool>
// ShouldSkipRecord {Method = {Boolean ShouldSkipRecord(System.String[])}} System.Func<string[], bool>
// ShouldUseConstructorParameters {Method = {Boolean ShouldUseConstructorParameters(System.Type)}} System.Func<System.Type, bool>
// TrimOptions None CsvHelper.Configuration.TrimOptions
// TypeConverterCache {CsvHelper.TypeConversion.TypeConverterCache} CsvHelper.TypeConversion.TypeConverterCache
// TypeConverterOptionsCache {CsvHelper.TypeConversion.TypeConverterOptionsCache} CsvHelper.TypeConversion.TypeConverterOptionsCache
// UseNewObjectForNullReferenceMembers true bool
//-------------------------------------------------------------------
public class ParserConfig /* IParserConfiguration */
{
/// <summary>Culture</summary>
public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture;
/// <summary>Gets or sets the size of the buffer used for reading CSV files. Default: 2048.</summary>
public int BufferSize { get; set; } = 2048;
/// <summary>
/// Gets or sets a value indicating whether the number of bytes should be counted while parsing.
/// <para>This will slow down parsing because it needs to get the byte count of every char for the given encoding.</para>
/// <para>The CsvHelper.Configuration.IParserConfiguration.Encoding needs to be set correctly for this to be accurate.</para>
/// <para>Default: false.</para>
/// </summary>
public bool CountBytes { get; set; } = false;
/// <summary>Gets or sets the encoding used when counting bytes.</summary>
public Encoding Encoding { get; set; } = Encoding.UTF8;
/// <summary>
/// Gets or sets the function that is called when bad field data is found.
/// <para>A field has bad data if it contains a quote and the field is not quoted (escaped).</para>
/// <para>You can supply your own function to do other things like logging the issue instead of throwing an exception.</para>
/// <para>Arguments: context</para>
/// </summary>
public Action<ReadingContext> BadDataFound { get; set; }
/// <summary>
/// Gets or sets a value indicating if a line break found in a quote field should be considered bad data.
/// <para>True to consider a line break bad data, otherwise false.</para>
/// <para>Defaults to false.</para>
/// </summary>
public bool LineBreakInQuotedFieldIsBadData { get; set; } = false;
/// <summary>
/// Gets or sets the character used to denote a line that is commented out.
/// <para>Default: '#'.</para>
/// </summary>
public char Comment { get; set; } = '#';
/// <summary>
/// <para>Gets or sets a value indicating if comments are allowed.</para>
/// <para>True to allow commented out lines, otherwise false (default).</para></summary>
public bool AllowComments { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating if blank lines should be ignored when reading.
/// <para>True to ignore, otherwise false.</para>
/// <para>Default: true.</para>
/// </summary>
public bool IgnoreBlankLines { get; set; } = true;
/// <summary>Gets or sets a value indicating if quotes should be ingored when parsing and treated like any other character.</summary>
public bool IgnoreQuotes { get; set; } = false;
/// <summary>
/// Gets or sets the character used to quote fields.
/// <para>Default: '"'.</para>
/// </summary>
public char Quote { get; set; } = '"';
/// <summary>
/// Gets or sets the delimiter used to separate fields.
/// <para>Default: CultureInfo.CurrentCulture.TextInfo.ListSeparator (',').</para>
/// </summary>
public string Delimiter { get; set; }
/// <summary>
/// Gets or sets the escape character used to escape a quote inside a field.
/// <para>Default: '"'.</para>
/// </summary>
public char Escape { get; set; } = '"';
/// <summary>
/// Gets or sets the field trimming options.
/// <para>Default: None</para>
/// </summary>
public TrimOptions TrimOptions { get; set; } = TrimOptions.None;
}
//-------------------------------------------------------------------
// C o n v e r t T o C S V
//-------------------------------------------------------------------
public static List<String[]> ParseCsv(String data, ParserConfig parserConfig = null)
{
List<String[]> records = new List<string[]>();
using (TextReader reader = new StringReader(data))
using (CsvParser csvParser = new CsvParser(reader, parserConfig?.Culture ?? CultureInfo.InvariantCulture))
{
if (parserConfig != null)
{
if (parserConfig.BufferSize > 0)
csvParser.Configuration.BufferSize = parserConfig.BufferSize;
csvParser.Configuration.CountBytes = parserConfig.CountBytes;
csvParser.Configuration.Encoding = parserConfig.Encoding;
if (parserConfig.BadDataFound != null)
csvParser.Configuration.BadDataFound = parserConfig.BadDataFound;
csvParser.Configuration.LineBreakInQuotedFieldIsBadData = parserConfig.LineBreakInQuotedFieldIsBadData;
csvParser.Configuration.Comment = parserConfig.Comment;
csvParser.Configuration.AllowComments = parserConfig.AllowComments;
csvParser.Configuration.IgnoreBlankLines = parserConfig.IgnoreBlankLines;
csvParser.Configuration.IgnoreQuotes = parserConfig.IgnoreQuotes;
csvParser.Configuration.Quote = parserConfig.Quote;
if (parserConfig.Delimiter != null)
csvParser.Configuration.Delimiter = parserConfig.Delimiter;
csvParser.Configuration.Escape = parserConfig.Escape;
csvParser.Configuration.TrimOptions = parserConfig.TrimOptions;
}
while (true)
{
String[] record = csvParser.Read();
if (record == null) break;
records.Add(record);
}
}
return records;
}
//-------------------------------------------------------------------
// C o n v e r t T o C S V
//-------------------------------------------------------------------
// Single record.
public static String ToCsv(IEnumerable<String> values)
{
return ToCsv(values, DefaultCulture);
}
public static String ToCsv(IEnumerable<String> values, CultureInfo culture)
{
String record = "";
using (WriterClass writer = new WriterClass(culture ?? DefaultCulture))
{
record = writer.ToCsv(values);
}
return record;
}
//-------------------------------------------------------------------
// Multiple records.
public static List<String> ToCsv(IEnumerable<IEnumerable<String>> data)
{
return ToCsv(data, DefaultCulture);
}
public static List<String> ToCsv(IEnumerable<IEnumerable<String>> data, CultureInfo culture)
{
List<String> records = new List<string>();
using (WriterClass writer = new WriterClass(culture ?? DefaultCulture))
{
foreach (String[] values in data)
records.Add(writer.ToCsv(values));
}
return records;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,87 @@
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
using System.Text.RegularExpressions;
namespace Sarand.Net.Standard.Base.Helpers
{
//=======================================================================
public static class SacsDataValidationHelper
{
//-------------------------------------------------------------------
/// <summary>
/// Validate the email address.
/// <para>Ref. https://stackoverflow.com/questions/1365407/c-sharp-code-to-validate-email-address</para>
/// </summary>
public static bool IsValidEmailAddress(String value)
{
if (StringUtil.IsEmpty(value)) return false;
if (!new EmailAddressAttribute().IsValid(value)) return false;
// Exclude spaces.
if (Regex.Matches(value, " ").Count > 0) return false;
// Exclude consecutive dots.
if (Regex.Matches(value, "\\.\\.").Count > 0) return false;
// Check for at least one dot after @ [to ensure john@smith is invalid].
if (Regex.Matches(value, "@[^@.]+\\.").Count < 1) return false;
return true;
}
//-------------------------------------------------------------------
/// <summary>
/// Validate the phone number.
/// <para>Ref. https://www.twilio.com/blog/validating-phone-numbers-effectively-with-c-and-the-net-frameworks</para>
/// </summary>
public static bool IsValidPhone(String value)
{
if (StringUtil.IsEmpty(value)) return false;
if (!new PhoneAttribute().IsValid(value)) return false;
// Apply minimum length.
if (StringUtil.GetLength(value) < 8) return false;
return true;
}
//-------------------------------------------------------------------
// Tax identification number.
// European country Tax Identification Numbers:
// https://ec.europa.eu/taxation_customs/vies/faqvies.do#item_11
//-------------------------------------------------------------------
/// <summary>
/// Validate the tax identification number.
/// </summary>
public static bool IsValidTaxIdentificationNumber(String countryCode, String value)
{
if (StringUtil.IsEmpty(value)) return false;
//---------------------------------------------------------------
// GB, United Kingdom.
// Standard:
// GB999 9999 99 block of 3 digits, block of 4 digits and block of 2 digits [00-96]
// Branch:
// GB999 9999 99 9995 as above followed by block of 3 digits
// Government departments:
// GBGD9996 'GD' followed by block of 3 or 4 digits
// Health Authoritites:
// GBHA9997 'HA' followed by block of 3 or 4 digits
//---------------------------------------------------------------
// Apply minimum length.
if (StringUtil.GetLength(value) < 8) return false;
// Country prefix.
if (!value.StartsWith("GB")) return false;
return true;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,128 @@
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
namespace Sarand.Net.Standard.Base.Helpers
{
//=======================================================================
public static class SacsEncryptionHelper
{
//-------------------------------------------------------------------
/// <summary>Number of byes in key. [32 bytes = 256 bits]</summary>
public static int KeyLength { get; } = 32;
/// <summary>Default encoding: UTF8.</summary>
public static Encoding DefaultEncoding { get; } = Encoding.UTF8;
//-------------------------------------------------------------------
// Symmetric Encryption in .NET Core
// System.Security.Cryptography implements and provides the APIs for encryption in .NET Core.
// Ref. https://damienbod.com/2020/08/19/symmetric-and-asymmetric-encryption-in-net-core/
//-------------------------------------------------------------------
public static bool IsBase64(String value, int expectedLength = 0)
{
try
{
Byte[] decoded = Convert.FromBase64String(value);
if (expectedLength > 0 && decoded.Length != expectedLength)
return false;
}
catch (Exception) { return false; }
return true;
}
public static String EncodePlainKey(String plainKey, int keyLength = 32, Encoding encoding = null)
{
if (StringUtil.IsEmpty(plainKey))
throw new NotSupportedException("Plain key must not be null/blank");
// Ensure key if 32 bytes long.
while (StringUtil.GetLength(plainKey) < keyLength)
plainKey += plainKey;
plainKey = StringUtil.Left(plainKey, keyLength);
// Encode to base-64.
encoding = encoding ?? DefaultEncoding;
return Convert.ToBase64String(encoding.GetBytes(plainKey));
}
//-------------------------------------------------------------------
// Generate random key and IV.
// NB Can use own key if suitable - 32 character value in base-64 format.
public static String InitSymmetricEncryptionKey()
{
var key = GetEncodedRandomString(KeyLength); // 256 bits.
return key;
}
public static string InitSymmetricEncryptionIV(String key)
{
Aes cipher = CreateCipher(key);
var IVBase64 = Convert.ToBase64String(cipher.IV);
return IVBase64;
}
public static (string Key, string IVBase64) InitSymmetricEncryptionKeyIV()
{
var key = InitSymmetricEncryptionKey();
var IVBase64 = InitSymmetricEncryptionIV(key);
return (key, IVBase64);
}
//-------------------------------------------------------------------
public static string GetEncodedRandomString(int length)
{
var base64 = Convert.ToBase64String(GenerateRandomBytes(length));
return base64;
}
private static Aes CreateCipher(string keyBase64)
{
// Default values: Keysize 256 bits, Padding PKC27.
Aes cipher = Aes.Create();
// Ensure the integrity of the ciphertext if using CBC
cipher.Mode = CipherMode.CBC;
cipher.Padding = PaddingMode.ISO10126;
byte[] rawKey = Convert.FromBase64String(keyBase64);
String rawKeyValue = System.Text.Encoding.Default.GetString(rawKey);
cipher.Key = rawKey;
return cipher;
}
private static byte[] GenerateRandomBytes(int length)
{
var byteArray = new byte[length];
SacsRandomHelper.Fill(byteArray);
return byteArray;
}
//-------------------------------------------------------------------
public static string Encrypt(string text, string IV, string key)
{
Aes cipher = CreateCipher(key);
cipher.IV = Convert.FromBase64String(IV);
ICryptoTransform cryptTransform = cipher.CreateEncryptor();
byte[] plaintext = Encoding.UTF8.GetBytes(text);
byte[] cipherText = cryptTransform.TransformFinalBlock(plaintext, 0, plaintext.Length);
return Convert.ToBase64String(cipherText);
}
//-------------------------------------------------------------------
public static string Decrypt(string encryptedText, string IV, string key)
{
Aes cipher = CreateCipher(key);
cipher.IV = Convert.FromBase64String(IV);
ICryptoTransform cryptTransform = cipher.CreateDecryptor();
byte[] encryptedBytes = Convert.FromBase64String(encryptedText);
byte[] plainBytes = cryptTransform.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
return Encoding.UTF8.GetString(plainBytes);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,253 @@
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Tasks;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Sarand.Net.Standard.Base.Helpers
{
//=======================================================================
public static class SacsHttpClientHelper
{
//-------------------------------------------------------------------
/// <summary>Request configuration</summary>
public class RequestData : SacsTaskData
{
// Construction.
public RequestData(CancellationToken? cancellationToken) : base(cancellationToken) { }
/// <summary>URL to send request to.</summary>
public UriBuilder UriBuilder { get; } = new UriBuilder();
/// <summary>
/// Request timeout. Default: 5 secs.
/// <para>NB Not currently supported as can only be changed when static HTTP Client not in use.</para>
/// </summary>
public int TimeoutSecs { get; set; } = RequestTimeoutSecs_Default;
/// <summary>
/// Headers to send with request.
/// <para>Accept, Accept-Charset, Accept-Encoding, Accept-Language, User-Agent, Transfer-Encoding, Pragma, Expect etc.</para>
/// </summary>
public Dictionary<String, List<String>> RequestHeaders { get; } = new Dictionary<String, List<String>>();
/// <summary>
/// Content Headers to send with request.
/// <para>Content-Length (bytes), Content-Type (media type), Content-Encoding (compression algorithm), Content-Language etc.</para>
/// <para>Content-Type header is defaulted for POST request if not specified.</para>
/// </summary>
public Dictionary<String, List<String>> ContentHeaders { get; } = new Dictionary<String, List<String>>();
/// <summary>
/// Form values to post as 'URL Encoded'.
/// <para>Content-Type defaults to 'application/x-www-form-urlencoded; charset=utf-8'.</para>
/// </summary>
public List<KeyValuePair<string, string>> FormData { get; } = new List<KeyValuePair<string, string>>();
/// <summary>
/// Data to POST. Ignored if FormData populated.
/// <para>Content-Type defaults to 'text/plain; charset=utf-8'.</para>
/// </summary>
public String PostData { get; set; }
/// <summary>Use POST request rather than GET even if no data to post.</summary>
public bool ForcePost { get; set; }
}
//-------------------------------------------------------------------
/// <summary>Response data</summary>
public class ResponseData
{
public DateTime RequestStarted { get; set; }
public DateTime RequestCompleted { get; set; }
/// <summary>Response headers.</summary>
public Dictionary<String, IEnumerable<String>> ResponseHeaders { get; } = new Dictionary<String, IEnumerable<String>>();
/// <summary>Response content headers.</summary>
public Dictionary<String, IEnumerable<String>> ContentHeaders { get; } = new Dictionary<String, IEnumerable<String>>();
/// <summary>Indicates whether Status Code represents success.</summary>
public bool Success { get; set; }
/// <summary>HTTP Status Code.</summary>
public HttpStatusCode StatusCode { get; set; }
/// <summary>HTTP message version. Default: 1.1</summary>
public Version Version { get; set; }
/// <summary>HTTP Reason Code.</summary>
public String ReasonPhrase { get; set; } = "";
/// <summary>Response data.</summary>
public String Data { get; set; } = "";
/// <summary>Elapsed time for request.</summary>
public TimeSpan Elapsed { get; set; }
/// <summary>Exception thrown during handling of request.</summary>
public Exception Exception { get; set; }
/// <summary>Error state message - generally exception type and message.</summary>
public String ErrorStateMessage { get; set; }
}
//-------------------------------------------------------------------
// Data members.
private static readonly HttpClient httpClient;
//-------------------------------------------------------------------
// Static initialisation.
static SacsHttpClientHelper ()
{
// Configure client.
httpClient = new HttpClient();
httpClient.Timeout = new TimeSpan(0, 0, RequestTimeoutSecs_Default);
}
//-------------------------------------------------------------------
// Properties.
public static HttpClient HttpClient { get { return httpClient; } }
public static int RequestTimeoutSecs_Default { get; } = 5;
public static String HeaderName_ContentType { get; } = "Content-Type";
//-------------------------------------------------------------------
public static async Task<ResponseData> DoRequest (RequestData requestData)
{
String fn = "SacsHttpClientHelper.DoRequest";
requestData.ThrowIfCancellationRequested();
ResponseData responseData = new ResponseData();
// Construct content to post.
HttpContent postContent = null;
if (requestData.FormData.Count > 0)
{
postContent = new FormUrlEncodedContent(requestData.FormData);
if (!requestData.ContentHeaders.ContainsKey(HeaderName_ContentType))
requestData.ContentHeaders.Add(HeaderName_ContentType, "application/x-www-form-urlencoded; charset=utf-8");
}
else if (!StringUtil.IsEmpty(requestData.PostData) || requestData.ForcePost)
{
postContent = new StringContent(StringUtil.EmptyOnNull(requestData.PostData), Encoding.UTF8);
if (!requestData.ContentHeaders.ContainsKey(HeaderName_ContentType))
requestData.ContentHeaders.Add(HeaderName_ContentType, "text/plain; charset=utf-8");
}
requestData.ThrowIfCancellationRequested();
// Make request.
DateTime startedAt = DateTime.Now;
try
{
// Request.
using (HttpRequestMessage request = new HttpRequestMessage())
{
request.RequestUri = requestData.UriBuilder.Uri;
// Request headers.
foreach (String headerName in requestData.RequestHeaders.Keys)
{
if (request.Headers.Contains(headerName))
request.Headers.Remove(headerName);
request.Headers.Add(headerName, requestData.RequestHeaders[headerName]);
}
// Default request headers.
if (HttpClient.DefaultRequestHeaders.Accept.Count == 0 && request.Headers.Accept.Count == 0)
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/*"));
// Configure request.
HttpResponseMessage response = null;
// Send request.
if (postContent != null)
{
// POST.
request.Method = HttpMethod.Post;
request.Content = postContent;
// Content headers.
foreach (String headerName in requestData.ContentHeaders.Keys)
{
if (request.Content.Headers.Contains(headerName))
request.Content.Headers.Remove(headerName);
request.Content.Headers.Add(headerName, requestData.ContentHeaders[headerName]);
}
// Send request.
responseData.RequestStarted = DateTime.Now;
response = await HttpClient.SendAsync(request, requestData.CancellationToken);
responseData.RequestCompleted = DateTime.Now;
}
else
{
// GET.
request.Method = HttpMethod.Get;
responseData.RequestStarted = DateTime.Now;
response = await HttpClient.SendAsync(request, requestData.CancellationToken);
responseData.RequestCompleted = DateTime.Now;
}
// Extract response data.
responseData.Version = response.Version;
responseData.Success = response.IsSuccessStatusCode;
responseData.StatusCode = response.StatusCode;
responseData.ReasonPhrase = response.ReasonPhrase;
if (response.Headers != null)
{
foreach (var header in response.Headers)
responseData.ResponseHeaders[header.Key] = new List<String>(header.Value);
}
if (response.Content != null && response.Content.Headers != null)
{
foreach (var header in response.Content.Headers)
responseData.ContentHeaders[header.Key] = new List<String>(header.Value);
}
responseData.Data = await response.Content.ReadAsStringAsync();
if (response != null) response.Dispose();
}
}
catch (Exception exc)
{
// Ignore if task has been cancelled.
if (requestData.IsCancellationRequested) throw exc;
// Record exception.
responseData.Exception = exc;
if (exc.InnerException != null)
{
exc = exc.InnerException;
}
String msg = exc.GetType().Name + ": " + exc.Message;
SaLogger.Warn(fn + " " + msg + ExceptionUtil.GetStackEntries(exc, 6).Concat("", "\n\t"));
responseData.ErrorStateMessage = msg;
}
requestData.ThrowIfCancellationRequested();
// Complete response data.
responseData.Elapsed = DateTime.Now - startedAt;
return responseData;
}
//-------------------------------------------------------------------
// R e s p o n s e S t a t u s D e s c r i p t i o n
//-------------------------------------------------------------------
public static String GetResponseStatusDescription (HttpStatusCode httpStatus)
{
// Ref. https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
switch (httpStatus)
{
// 1xx: Informational - Request received, continuing process.
case HttpStatusCode.Continue: return "Continue";
// 2xx: Success - The action was successfully received, understood, and accepted.
case HttpStatusCode.OK: return "OK";
case HttpStatusCode.Created: return "Created";
case HttpStatusCode.Accepted: return "Accepted";
// 3xx: Redirection - Further action must be taken in order to complete the request.
case HttpStatusCode.MultipleChoices: return "Multiple choices";
case HttpStatusCode.MovedPermanently: return "Moved permanently";
case HttpStatusCode.Found: return "Found";
// 4xx: Client Error -The request contains bad syntax or cannot be fulfilled.
case HttpStatusCode.BadRequest: return "Bad request";
case HttpStatusCode.Unauthorized: return "Unauthorized";
case HttpStatusCode.PaymentRequired: return "Payment required";
case HttpStatusCode.Forbidden: return "Forbidden";
case HttpStatusCode.NotFound: return "Not found";
case HttpStatusCode.MethodNotAllowed: return "Method not allowed";
case HttpStatusCode.NotAcceptable: return "Not acceptable";
// 5xx: Server Error -The server failed to fulfill an apparently valid request.
case HttpStatusCode.InternalServerError: return "Internal server error";
}
return httpStatus.ToString();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,20 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sarand.Net.Standard.Base.Helpers
{
//=======================================================================
public static class SacsJsonHelper
{
//-------------------------------------------------------------------
public static dynamic DeserializeJsonObject(String data)
{
return JsonConvert.DeserializeObject<dynamic>(data);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
namespace Sarand.Net.Standard.Base.Helpers
{
//=======================================================================
public static class SacsRandomHelper
{
//-------------------------------------------------------------------
// Properties.
public static RandomNumberGenerator RandomNumberGenerator { get; } = RandomNumberGenerator.Create();
private static Random random;
public static Random Random { get { if (random == null) random = new Random(GetInt32(Int32.MaxValue)); return random; } }
public static void Fill(byte[] byteArray)
{
// ToDo RandomNumberGenerator.Fill(byteArray);
RandomNumberGenerator.GetBytes(byteArray);
}
//-------------------------------------------------------------------
/// <summary>
/// Generates a random integer between 0 (inclusive) and a specified exclusive upper
/// bound using a cryptographically strong random number generator.
/// </summary>
/// <param name="toExclusive">The exclusive upper bound of the random range.</param>
/// <returns>A random integer between 0 (inclusive) and toExclusive (exclusive).</returns>
/// <exception cref="ArgumentOutOfRangeException">The toExclusive parameter is less than or equal to 0.</exception>
public static int GetInt32(int toExclusive)
{
return RandomNumberGenerator.GetInt32(toExclusive);
//return Random.Next(toExclusive);
}
//-------------------------------------------------------------------
/// <summary>
/// Generates a random integer between a specified inclusive lower bound and a specified
/// exclusive upper bound using a cryptographically strong random number generator.
/// </summary>
/// <param name="fromInclusive">The inclusive lower bound of the random range.</param>
/// <param name="toExclusive">The exclusive upper bound of the random range.</param>
/// <returns>A random integer between fromInclusive (inclusive) and toExclusive (exclusive).</returns>
/// <exception cref="ArgumentOutOfRangeException">The toExclusive parameter is less than or equal to the fromInclusive parameter.</exception>
public static int GetInt32(int fromInclusive, int toExclusive)
{
return RandomNumberGenerator.GetInt32(fromInclusive, toExclusive);
//return Random.Next(fromInclusive, toExclusive);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,243 @@
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
namespace Sarand.Net.Standard.Base.Helpers
{
//=======================================================================
public static class SacsResourceHelper
{
//-------------------------------------------------------------------
// Definitions.
public enum TextResource
{
SaBaseCss,
SaBaseJs,
JQueryJs,
JQueryUiCss,
JQueryUiJs,
}
public enum ImageResource
{
// Burgers.
Icon_Burger_Black_48_png,
Icon_Burger_Black_64_png,
// Arrows.
Arrow_Left_Black_16_png,
Arrow_Left_Double_Black_16_png,
Arrow_Right_Black_16_png,
Arrow_Right_Double_Black_16_png,
Arrow_Left_Black_24_png,
Arrow_Left_Double_Black_24_png,
Arrow_Right_Black_24_png,
Arrow_Right_Double_Black_24_png,
// IconShock.
IconShock_Arrow_Down_Purple_72_png,
IconShock_Arrow_Right_Purple_72_png,
IconShock_Add_Green_72_png,
IconShock_Pencil_Orange_72_png,
IconShock_Close_Red_72_png,
IconShock_TrashCan_Grey_72_png,
}
//-------------------------------------------------------------------
// NB 'Properties' not available in Core .Net project.
// Resources must be loaded via csproj so that they are available in the Manifest Resources:
// <ItemGroup>
// <EmbeddedResource Include="Properties\Resources.resx">
// <Generator>ResXFileCodeGenerator</Generator>
// <LastGenOutput>Resources.Designer.cs</LastGenOutput>
// </EmbeddedResource>
// <EmbeddedResource Include="Resources\html\*.css" />
// <EmbeddedResource Include="Resources\html\*.js" />
// </ItemGroup>
// REf. https://stackoverflow.com/questions/38762368/embedded-resource-in-net-core-libraries
//-------------------------------------------------------------------
public static String GetTextResource(TextResource textResource)
{
String partialPath = "";
String resourceFilename = "";
switch (textResource)
{
case TextResource.SaBaseCss:
//return Properties.Resources.sa_base_css;
resourceFilename = "sa-base.css";
break;
case TextResource.SaBaseJs:
//return Properties.Resources.sa_base_js;
resourceFilename = "sa-base.js";
break;
case TextResource.JQueryJs:
//return Properties.Resources.jquery_1_12_1_min_js;
resourceFilename = "jquery-1.12.1.min.js";
break;
case TextResource.JQueryUiCss:
//return Properties.Resources.jquery_ui_1_12_1_min_css;
resourceFilename = "jquery-ui-1.12.1.min.css";
break;
case TextResource.JQueryUiJs:
//return Properties.Resources.jquery_ui_1_12_1_min_js;
resourceFilename = "jquery-ui-1.12.1.min.js";
break;
}
if (!StringUtil.IsEmpty(resourceFilename))
{
var assembly = typeof(SacsResourceHelper).GetTypeInfo().Assembly;
String resourceText = GetManifestResourceAsText(assembly, partialPath, resourceFilename);
if (!StringUtil.IsEmpty(resourceText))
return resourceText;
}
return "";
}
//-------------------------------------------------------------------
public static byte[] GetImageResource(ImageResource imageResource)
{
String partialPath = "";
String resourceFilename = "";
switch (imageResource)
{
//-----------------------------------------------------------
// Burgers.
case ImageResource.Icon_Burger_Black_48_png:
resourceFilename = "burger_black_48.png";
break;
case ImageResource.Icon_Burger_Black_64_png:
resourceFilename = "burger_black_64.png";
break;
//-----------------------------------------------------------
// Arrows.
case ImageResource.Arrow_Left_Black_16_png:
resourceFilename = "arrow_left_black_16.png";
break;
case ImageResource.Arrow_Left_Double_Black_16_png:
resourceFilename = "arrow_left_double_black_16.png";
break;
case ImageResource.Arrow_Right_Black_16_png:
resourceFilename = "arrow_right_black_16.png";
break;
case ImageResource.Arrow_Right_Double_Black_16_png:
resourceFilename = "arrow_right_double_black_16.png";
break;
case ImageResource.Arrow_Left_Black_24_png:
resourceFilename = "arrow_left_black_24.png";
break;
case ImageResource.Arrow_Left_Double_Black_24_png:
resourceFilename = "arrow_left_double_black_24.png";
break;
case ImageResource.Arrow_Right_Black_24_png:
resourceFilename = "arrow_right_black_24.png";
break;
case ImageResource.Arrow_Right_Double_Black_24_png:
resourceFilename = "arrow_right_double_black_24.png";
break;
//-----------------------------------------------------------
// IconShock.
case ImageResource.IconShock_Arrow_Down_Purple_72_png:
partialPath = "IconShock";
resourceFilename = "arrow_down_purple_72.png";
break;
case ImageResource.IconShock_Arrow_Right_Purple_72_png:
partialPath = "IconShock";
resourceFilename = "arrow_right_purple_72.png";
break;
case ImageResource.IconShock_Add_Green_72_png:
partialPath = "IconShock";
resourceFilename = "add_green_72.png";
break;
case ImageResource.IconShock_Pencil_Orange_72_png:
partialPath = "IconShock";
resourceFilename = "pencil_orange_72.png";
break;
case ImageResource.IconShock_Close_Red_72_png:
partialPath = "IconShock";
resourceFilename = "close_red_72.png";
break;
case ImageResource.IconShock_TrashCan_Grey_72_png:
partialPath = "IconShock";
resourceFilename = "trashcan_grey_72.png";
break;
//-----------------------------------------------------------
}
if (!StringUtil.IsEmpty(resourceFilename))
{
var assembly = typeof(SacsResourceHelper).GetTypeInfo().Assembly;
byte[] resourceData = GetManifestResourceAsBinary(assembly, partialPath, resourceFilename);
if (resourceData != null)
return resourceData;
}
return new byte[0];
}
//-------------------------------------------------------------------
/// <summary>Retrieve resource as String based on assembly and partial resource name.</summary>
public static String GetManifestResourceAsText(Assembly assembly, String partialPath, String resourceNameEndsWith)
{
String fn = typeof(SacsResourceHelper).Name + ".GetManifestResourceAsText";
if (StringUtil.IsEmpty(resourceNameEndsWith)) return "";
using (Stream resourceStream = GetManifestResourceStream(assembly, partialPath, resourceNameEndsWith))
{
if (resourceStream != null)
{
using (StreamReader resourceReader = new StreamReader(resourceStream, Encoding.UTF8))
{
return resourceReader.ReadToEnd();
}
}
}
return "";
}
//-------------------------------------------------------------------
/// <summary>Retrieve resource as byte array based on assembly and partial resource name.</summary>
public static byte[] GetManifestResourceAsBinary(Assembly assembly, String partialPath, String resourceNameEndsWith)
{
String fn = typeof(SacsResourceHelper).Name + ".GetManifestResourceAsBinary";
if (
StringUtil.IsEmpty(resourceNameEndsWith)) return null;
using (Stream resourceStream = GetManifestResourceStream(assembly, partialPath, resourceNameEndsWith))
{
if (resourceStream != null)
{
using (BinaryReader resourceReader = new BinaryReader(resourceStream, Encoding.UTF8))
{
return resourceReader.ReadBytes((int)resourceStream.Length);
}
}
}
SaLogger.Warn(fn + " Failed to locate text resource: " + resourceNameEndsWith);
return null;
}
//-------------------------------------------------------------------
/// <summary>Retrieve resource as stream based on assembly and partial resource name.</summary>
public static Stream GetManifestResourceStream(Assembly assembly, String partialPath, String resourceNameEndsWith)
{
String fn = typeof(SacsResourceHelper).Name + ".GetManifestResourceStream";
//Logger.Debug(fn + " Manifest resource names: " + new List<String>(assembly.GetManifestResourceNames()).Concat("","\n\t"));
foreach (String resourceName in assembly.GetManifestResourceNames())
{
//SaLogger.Debug(fn + " Resource: " + resourceName);
// Match the partial path.
if (!StringUtil.IsEmpty(partialPath) && !resourceName.ContainsCI(partialPath)) continue;
// Match the resource filename to the end of the resource name - resource names start with default namespace (eg. 'Sarand').
if (!resourceName.EndsWithCI(resourceNameEndsWith)) continue;
//SaLogger.Debug(fn + " Resource filename: " + resourceNameEndsWith + ". Matched to resource: " + resourceName);
return assembly.GetManifestResourceStream(resourceName);
}
SaLogger.Warn(fn + " Resource filename not found: " + resourceNameEndsWith);
return null;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+146
View File
@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.IO;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
namespace Sarand.Net.Standard.Base.IO
{
//=======================================================================
public static class DirectoryUtil
{
//-------------------------------------------------------------------
// D i r e c t o r y E x i s t s
//-------------------------------------------------------------------
public static bool DirectoryExists (String directory)
{
try
{
return Directory.Exists(directory);
}
catch (Exception) { }
return false;
}
//-------------------------------------------------------------------
// G e t D i r e c t o r y N a m e
//-------------------------------------------------------------------
/// <summary>
/// Returns the directory information for the specified path string.
/// </summary>
/// <param name="path">The path of a file or directory.</param>
/// <returns>Directory information for path, or null if path denotes a root directory or is
/// null. Returns System.String.Empty if path does not contain directory information.</returns>
public static String GetDirectoryName (String path)
{
try
{
// Exceptions:
// T:System.ArgumentException:
// The path parameter contains invalid characters, is empty, or contains only white spaces.
// T:System.IO.PathTooLongException:
// The path parameter is longer than the system-defined maximum length.
return Path.GetDirectoryName(path);
}
catch (Exception) { }
return String.Empty;
}
//-------------------------------------------------------------------
// E n u m e r a t e D i r e c t o r i e s
//-------------------------------------------------------------------
/// <summary>
/// Retrieve list of sub directories in directory.
/// <para>Avoiding Exception thrown when network path isn't valid.</para>
/// </summary>
public static IEnumerable<String> EnumerateDirectories (String path)
{
try
{
return Directory.EnumerateDirectories(path);
}
catch (Exception exc)
{
SaLogger.Error("DirectoryUtil.EnumerateDirectories " + exc.GetType().Name + ": " + exc.Message + ". Path: '" + path + "'");
}
return new List<String>();
}
//-------------------------------------------------------------------
// E n u m e r a t e F i l e s
//-------------------------------------------------------------------
/// <summary>
/// Retrieve list of files in directory.
/// <para>Avoiding Exception thrown when network path isn't valid.</para>
/// </summary>
public static IEnumerable<String> EnumerateFiles (String path)
{
try
{
return Directory.EnumerateFiles(path);
}
catch (Exception exc)
{
SaLogger.Error("DirectoryUtil.EnumerateFiles " + exc.GetType().Name + ": " + exc.Message + ". Path: '" + path + "'");
}
return new List<String>();
}
//-------------------------------------------------------------------
/// <summary>
/// Retrieve list of files in directory.
/// <para>Avoiding Exception thrown when network path isn't valid.</para>
/// </summary>
public static IEnumerable<String> EnumerateFiles (String path, String searchPattern)
{
try
{
return Directory.EnumerateFiles(path, searchPattern);
}
catch (Exception exc)
{
SaLogger.Error("DirectoryUtil.EnumerateFiles " + exc.GetType().Name + ": " + exc.Message + ". Path: '" + path + "'. Pattern: " + searchPattern);
}
return new List<String>();
}
//-------------------------------------------------------------------
/// <summary>
/// Retrieve list of files in directory.
/// <para>Avoiding Exception thrown when network path isn't valid.</para>
/// </summary>
public static IEnumerable<String> EnumerateFiles (String path, String searchPattern, SearchOption searchOption)
{
try
{
return Directory.EnumerateFiles(path, searchPattern, searchOption);
}
catch (Exception exc)
{
SaLogger.Error("DirectoryUtil.EnumerateFiles " + exc.GetType().Name + ": " + exc.Message + ". Path: '" + path + "'. Pattern: " + searchPattern + ". Option: " + searchOption.ToString());
}
return new List<String>();
}
//-------------------------------------------------------------------
// C r e a t e D i r e c t o r y
//-------------------------------------------------------------------
/// <summary>
/// Create Directory in the specified path.
/// <para>Avoiding Exception thrown when network path isn't valid.</para>
/// </summary>
public static DirectoryInfo CreateDirectory (String path)
{
try
{
return Directory.CreateDirectory(path);
}
catch (Exception exc)
{
SaLogger.Error("DirectoryUtil.CreateDirectory " + exc.GetType().Name + ": " + exc.Message + ". Path: '" + path + "'");
}
return null;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+361
View File
@@ -0,0 +1,361 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.IO
{
//=======================================================================
public static class FileUtil
{
//-------------------------------------------------------------------
// U t i l i t i e s
//-------------------------------------------------------------------
public static String GetFileLengthFmt(long length)
{
// Longs run out around EB
string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
if (length == 0) return "0" + suf[0];
long bytes = Math.Abs(length);
int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
double num = Math.Round(bytes / Math.Pow(1024, place), place > 0 ? 1 : 0);
if (num >= 1000.0)
{
// Avoid values with 4 digits (and thousands separator).
place++;
num = Math.Round(bytes / Math.Pow(1024, place), place > 0 ? 1 : 0, MidpointRounding.AwayFromZero);
}
return (Math.Sign(length) * num).ToString(place > 0 ? "N1" : "N0") + " " + suf[place];
}
//-------------------------------------------------------------------
// P a t h
//-------------------------------------------------------------------
public static String GetPathDelim(String path)
{
if (!StringUtil.IsEmpty(path) && path.IndexOf(Path.AltDirectorySeparatorChar) >= 0)
return Path.AltDirectorySeparatorChar.ToString();
return Path.PathSeparator.ToString();
}
//-------------------------------------------------------------------
// D i r e c t o r y
//-------------------------------------------------------------------
public delegate bool DirectoryExistsFn(String directoryPath);
public static DirectoryExistsFn fnDirectoryExists { get; set; } = null;
public static bool IsDirectory(String directoryPath)
{
try
{
// Alternative method, if specified.
if (fnDirectoryExists != null)
return fnDirectoryExists(directoryPath);
// Default method.
return Directory.Exists(directoryPath);
}
catch (Exception exc) { SaLogger.Debug("FileUtil.IsDirectory " + exc.GetType().Name + ": " + exc.Message); }
return false;
}
public delegate String DirectoryGetParentFn(String filepath);
public static DirectoryGetParentFn fnDirectoryGetParent { get; set; }
public static String GetParentDirectory(String filepath)
{
try
{
// Alternative method, if specified.
if (fnDirectoryGetParent != null)
return fnDirectoryGetParent(filepath);
// Default method.
DirectoryInfo parentDirectoryInfo = Directory.GetParent(filepath);
if (parentDirectoryInfo != null)
return parentDirectoryInfo.FullName;
}
catch (Exception exc) { SaLogger.Debug("FileUtil.GetParentDirectory " + exc.GetType().Name + ": " + exc.Message); }
return String.Empty;
}
public delegate bool DirectoryCreateDirectoryFn(String filepath);
public static DirectoryCreateDirectoryFn fnDirectoryCreateDirectory { get; set; } = null;
public static bool CreateDirectory(String directoryPath)
{
try
{
// Alternative method, if specified.
if (fnDirectoryCreateDirectory != null)
return fnDirectoryCreateDirectory(directoryPath);
// Default method.
DirectoryInfo info = Directory.CreateDirectory(directoryPath);
return info.Exists;
}
catch (Exception exc) { SaLogger.Debug("FileUtil.CreateDirectory " + exc.GetType().Name + ": " + exc.Message); }
return false;
}
public static bool ValidateDirectory(String directoryPath, bool allowCreation = false)
{
// Validate parent folder first.
String parentDirectoryPath = GetParentDirectory(directoryPath);
if (!StringUtil.IsEmpty(parentDirectoryPath) && !ValidateDirectory(parentDirectoryPath, allowCreation))
return false;
// Validate folder itself.
if (!IsDirectory(directoryPath) && allowCreation)
CreateDirectory(directoryPath);
return IsDirectory(directoryPath);
}
//-------------------------------------------------------------------
// F i l e
//-------------------------------------------------------------------
public static DateTime GetFileLastWriteTime(String filePath)
{
try
{
return File.GetLastWriteTime(filePath);
}
catch (Exception exc)
{
SaLogger.Warn(typeof(FileUtil).Name + ".GetFileLastWriteTime " + exc.GetType().Name + ": " + exc.Message);
}
return DateTime.MinValue;
}
//-------------------------------------------------------------------
// File utilities.
public delegate bool FileExistsFn(String filePath);
public static FileExistsFn fnFileExists { get; set; } = null;
/// <summary>
/// Test whether file exists.
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public static bool IsFile(String filePath)
{
try
{
// Alternative method, if specified.
if (fnFileExists != null)
return fnFileExists(filePath);
// Default method.
return File.Exists(filePath);
}
catch (Exception exc) { SaLogger.Debug("FileUtil.IsFile " + exc.GetType().Name + ": " + exc.Message); }
return false;
}
//-------------------------------------------------------------------
public delegate String[] FileReadAllLinesFn(String filePath);
public static FileReadAllLinesFn fnFileReadAllLines { get; set; } = null;
/// <summary>
/// Read all lines from a file.
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public static String[] ReadAllFileLines(String filePath)
{
try
{
// Alternative method, if specified.
if (fnFileReadAllLines != null)
return fnFileReadAllLines(filePath);
// Default method.
return File.ReadAllLines(filePath);
}
catch (Exception exc) { SaLogger.Debug("FileUtil.ReadAllFileLines " + exc.GetType().Name + ": " + exc.Message); }
return new String[0];
}
//-------------------------------------------------------------------
/// <summary>
/// <para>Locate the specified file in a folder path relative to the specified folder.</para>
/// <para>Search is in the fspecified folder and then each parent folder in turn.</para>
/// <para>Any subDirectorites at each level found in the suppleid list are also searched.</para>
/// </summary>
public static String LocateFileRelative(String baseFolder, String filename, List<String> subDirectories)
{
if (String.IsNullOrEmpty(filename)) return String.Empty;
String rootfolder = SacsSystem.ApplicationFolder;
#if DEBUG
SaLogger.Trace("FileUtil.LocateFileRelative Root folder = " + rootfolder);
#endif
String directory = rootfolder;
while (!directory.IsEmpty())
{
// Does this directory contain the specified file:
String filepath = directory + Path.AltDirectorySeparatorChar + filename;
if (IsFile(filepath)) return filepath;
// Check sub-directories:
if (subDirectories != null)
{
foreach (var subDirectory in subDirectories)
{
filepath = directory + Path.AltDirectorySeparatorChar + subDirectory + Path.AltDirectorySeparatorChar + filename;
if (IsFile(filepath)) return filepath;
}
}
directory = GetParentDirectory(directory);
}
return String.Empty;
}
/// <summary>Locate file in path relative to application folder.</summary>
public static String LocateFileRelative(String filename, List<String> subDirectories)
{
return LocateFileRelative(SacsSystem.ApplicationFolder, filename, subDirectories);
}
//-------------------------------------------------------------------
// L o c a t e F i l e s
//-------------------------------------------------------------------
// Locate files in the folder that match the file and one of the specified extensions.
// Returned list is the full path for each file.
public static List<String> LocateFiles(String folder, String file, List<String> extensions, int maxSubFolders)
{
List<String> files = new List<string>();
if (!DirectoryUtil.DirectoryExists(folder) || StringUtil.IsEmpty(file)) return files;
// Locate matching files in the specified folder.
String fileSpec = file + "." + (extensions.Count == 1 ? extensions[0] : "*");
foreach (String filename in DirectoryUtil.EnumerateFiles(folder, fileSpec))
{
String filePath = folder + GetPathDelim(folder) + filename;
bool validFile = true;
// Verify the extension:
if (extensions.Count > 0)
{
String extension = Path.GetExtension(filePath);
validFile = extensions.Any(item => item.EqualsCI(extension));
}
if (validFile)
files.Add(filePath);
}
if (maxSubFolders > 0)
{
// Locate sub folders:
foreach (String subFolder in DirectoryUtil.EnumerateDirectories(folder))
{
if (subFolder == "." || subFolder == "..") continue;
files.AddRange(LocateFiles(subFolder, file, extensions, maxSubFolders - 1));
}
}
return files;
}
//-------------------------------------------------------------------
/// <summary>
/// Replace invalid characters with '_'.
/// <para>Invalid characters: \/:*?\"&lt;>|</para>
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
public static String FormatFilename(String filename)
{
if (StringUtil.IsEmpty(filename)) return String.Empty;
// Replace invalid characters with '_'.
filename = filename.ReplaceAny("\\/:*?\"<>|", '_');
filename = StringUtil.ReplaceAll(filename, "__", "_");
filename = StringUtil.ReplaceAll(filename, "..", ".");
filename = filename.ReplaceAll(new string[] { "_ ", " _", " " }, " ");
return filename;
}
//-------------------------------------------------------------------
// Shell32 alternatives to file utilities to avoid 'path too long' exceptions etc.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode/*Auto*/)]
public struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};
[DllImport("shell32.dll", CharSet = CharSet.Unicode/*Auto*/)]
public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags);
//-------------------------------------------------------------------
// Create/Open file handle.
// Ref. http://www.pinvoke.net/default.aspx/kernel32.getfiletime
private const uint GENERIC_READ = 0x80000000;
private const uint FILE_SHARE_READ = 0x1;
private const uint FILE_ATTRIBUTE_NORMAL = 0x80;
private const int INVALID_HANDLE_VALUE = -1;
private const uint OPEN_EXISTING = 3;
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode/*Auto*/, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr SecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
//-------------------------------------------------------------------
// File date/times.
// Ref. http://www.pinvoke.net/default.aspx/kernel32.getfiletime
// REf. https://msdn.microsoft.com/en-us/library/windows/desktop/ms724320(v=vs.85).aspx
public class FileTimes
{
public DateTime CreationTime { get; internal set; } = DateTime.MinValue;
public DateTime LastAccessTime { get; internal set; } = DateTime.MinValue;
public DateTime LastWriteTime { get; internal set; } = DateTime.MinValue;
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetFileTime(IntPtr hFile, ref FILETIME lpCreationTime, ref FILETIME lpLastAccessTime, ref FILETIME lpLastWriteTime);
[StructLayout(LayoutKind.Sequential)]
private struct FILETIME
{
public uint dwLowDateTime;
public uint dwHighDateTime;
}
public static FileTimes GetFileTimes(String filePath)
{
FileTimes fileTimes = new FileTimes();
try
{
// .Net versions first.
// These fail on long file paths.
fileTimes.CreationTime = File.GetCreationTime(filePath).Date;
fileTimes.LastAccessTime = File.GetLastAccessTime(filePath).Date;
fileTimes.LastWriteTime = File.GetLastWriteTime(filePath).Date;
return fileTimes;
}
catch (Exception) { }
// If .Net methods fail, try P/Invoke via kernel32.
IntPtr ptr = IntPtr.Zero;
FILETIME ftCreationTime = new FILETIME();
FILETIME ftLastAccessTime = new FILETIME();
FILETIME ftLastWriteTime = new FILETIME();
try
{
// NB Opening file will fail if it's open elsewhere.
ptr = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
if (ptr.ToInt32() == INVALID_HANDLE_VALUE)
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
if (GetFileTime(ptr, ref ftCreationTime, ref ftLastAccessTime, ref ftLastWriteTime) != true)
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
fileTimes.CreationTime = DateTime.FromFileTimeUtc((((long)ftCreationTime.dwHighDateTime) << 32) | ((uint)ftCreationTime.dwLowDateTime));
fileTimes.LastAccessTime = DateTime.FromFileTimeUtc((((long)ftLastAccessTime.dwHighDateTime) << 32) | ((uint)ftLastAccessTime.dwLowDateTime));
fileTimes.LastWriteTime = DateTime.FromFileTimeUtc((((long)ftLastWriteTime.dwHighDateTime) << 32) | ((uint)ftLastWriteTime.dwLowDateTime));
}
catch (Exception) { }
finally
{
if (ptr != IntPtr.Zero && ptr.ToInt32() != INVALID_HANDLE_VALUE) CloseHandle(ptr);
}
return fileTimes;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,287 @@
using System;
using System.Collections.Generic;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.Interfaces
{
//=======================================================================
public class SaInterface : SacsHierarchyItem
{
//-------------------------------------------------------------------
// Columns.
public enum Column
{
Pos = 0,
// Interface.
Id,
Context,
Category,
Category_Desc,
Type,
Type_Desc,
Code,
Caption,
Active_Y,
Active_TickCross,
Active_Ind,
// Derived properties.
Process_Count,
DataItem_Count,
// Common properties.
Notes,
Status,
Status_Desc,
Status_Ind,
CreateOn_Date,
UpdateOn_Date,
}
//-------------------------------------------------------------------
/// <summary>Properties other than common.</summary>
public class DataProperties
{
public String Context { get; set; } = String.Empty;
public SaInterfaceCategory Category { get; set; } = SaInterfaceCategory.Unknown;
public short Type { get; set; } = 0;
public String Code { get; set; } = String.Empty;
public String Caption { get; set; } = String.Empty;
}
//-------------------------------------------------------------------
// Data.
public new class Data : SacsHierarchyItem.Data
{
// Data members.
private readonly DataProperties data = new DataProperties();
// Construction.
public Data ()
{
Status = DefaultStatus;
}
// Properties.
public String Context
{
get { return data.Context; }
set { CheckChanged(data.Context, value); data.Context = value; }
}
public SaInterfaceCategory Category
{
get { return data.Category; }
set { CheckChanged((int)data.Category, (int)value); data.Category = value; }
}
public short Type
{
get { return data.Type; }
set { CheckChanged(data.Type, value); data.Type = value; }
}
public String Code
{
get { return data.Code; }
set { CheckChanged(data.Code, value); data.Code = value; }
}
public String Caption
{
get { return data.Caption; }
set { CheckChanged(data.Caption, value); data.Caption = value; }
}
// Flags.
public bool Active
{
get { return GetFlag(Flag_Active); }
set { SetFlag(Flag_Active, value); }
}
// Population.
internal void PopulateFrom (Data data)
{
Context = data.Context;
Category = data.Category;
Type = data.Type;
Code = data.Code;
Caption = data.Caption;
base.PopulateFrom(data);
}
public Data Clone ()
{
Data data = new Data();
data.PopulateFrom(this);
return data;
}
}
//-------------------------------------------------------------------
// Error field - identify properties with validation error.
public new class ErrField : SacsHierarchyItem.ErrField
{
// Identifies the field that has invalid data.
public ValidValue Context { get; set; } = ValidValue.Valid;
public ValidValue Category { get; set; } = ValidValue.Valid;
public ValidValue Type { get; set; } = ValidValue.Valid;
public ValidValue Code { get; set; } = ValidValue.Valid;
public ValidValue Caption { get; set; } = ValidValue.Valid;
}
//-------------------------------------------------------------------
// Data members.
private readonly Data data = new Data();
// Flags.
public static readonly Byte Flag_Active = 0x01;
//-------------------------------------------------------------------
// Construction.
public SaInterface (SysId id, String context, SaInterfaceCategory category, short type) :
base(id)
{
base.SetData(data);
Context = context;
Category = category;
Type = type;
Status = DefaultStatus;
}
//-------------------------------------------------------------------
// Statuses.
private static readonly List<SaStatus> validStatuses = new List<SaStatus>()
{
SaStatus.Active,
};
public static SaStatus DefaultStatus { get { return ActiveStatus; } }
public static SaStatus ActiveStatus { get { return SaStatus.Active; } }
public static List<SaStatus> ValidStatuses { get { return new List<SaStatus>(validStatuses); } }
public static DataValue.TriStateType GetStatusTriState (SaStatus status)
{
switch (status)
{
case SaStatus.Active:
return Core.DataValue.TriStateType.Green;
case SaStatus.Deleted:
return Core.DataValue.TriStateType.Purple;
}
return Core.DataValue.TriStateType.Unknown;
}
//-------------------------------------------------------------------
// Properties.
public Data GetData () { return data.Clone(); }
public void SetData (Data data)
{
String fn = GetType().Name + ".SetData";
// Properties that cannot be changed if previously specified.
if (SysId.IsValid(Id) && SysId.ToInt(Id) != SysId.ToInt(data.Id))
throw new NotSupportedException(fn + " Id cannot be changed");
if (!StringUtil.IsEmpty(Context) && !Context.Equals(data.Context))
throw new NotSupportedException(fn + " Context cannot be changed");
if ((int)Category >= 0 && (int)Category != (int)data.Category)
throw new NotSupportedException(fn + " Category cannot be changed");
if (Type >= 0 && Type != data.Type)
throw new NotSupportedException(fn + " Type cannot be changed");
// Populate individual properties to ensure that 'Changed' indicator is kept up to date.
this.data.PopulateFrom(data);
}
public String Context
{
get { return data.Context; }
protected set { data.Context = value; }
}
public SaInterfaceCategory Category
{
get { return data.Category; }
protected set { data.Category = value; }
}
public short Type
{
get { return data.Type; }
protected set { data.Type = value; }
}
public String Code
{
get { return data.Code; }
set { data.Code = value; }
}
public String Caption
{
get { return data.Caption; }
set { data.Caption = value; }
}
//-------------------------------------------------------------------
// Flags.
public bool Active
{
get { return GetFlag(Flag_Active); }
set { SetFlag(Flag_Active, value); }
}
//-------------------------------------------------------------------
// List parameters.
public enum StatusCat
{
Any,
/// <summary>NB Interface activity controlled by flag rather than status.</summary>
ActiveOnly,
}
/// <summary>
/// Item list parameters.
/// </summary>
public new class Params : SacsHierarchyItem.Params
{
public List<SysId> IncludedIds { get; } = new List<SysId>();
public List<SysId> ExcludedIds { get; } = new List<SysId>();
public String Context { get; set; }
public SaInterfaceCategory InterfaceCategory { get; set; } = SaInterfaceCategory.Unknown;
public List<short> IncludedInterfaceTypes { get; } = new List<short>();
/// <summary>Exact code match</summary>
public String Code { get; set; }
public String CaptionPartial { get; set; }
/// <summary>Text to parially match code or caption</summary>
public String InterfaceText { get; set; }
/// <summary>Active flag</summary>
public bool ActiveOnly { get; set; } = true;
public StatusCat StatusCat { get; set; } = StatusCat.Any;
}
//-------------------------------------------------------------------
}
//=======================================================================
// L i s t
//=======================================================================
public static class SaInterfaceList
{
//-------------------------------------------------------------------
// E x i s t s
//-------------------------------------------------------------------
public static bool ExistsByContextCategoryType (this List<SaInterface> interfaces, String context, SaInterfaceCategory interfaceCategory, short interfaceType)
{
return interfaces.Exists(item => item.Context.EqualsCI(context) && item.Category == interfaceCategory && item.Type == interfaceType);
}
public static bool ExistsByContextCategoryType (this List<SaInterface.Data> interfaces, String context, SaInterfaceCategory interfaceCategory, short interfaceType)
{
return interfaces.Exists(item => item.Context.EqualsCI(context) && item.Category == interfaceCategory && item.Type == interfaceType);
}
//-------------------------------------------------------------------
// F i n d A l l
//-------------------------------------------------------------------
public static List<SaInterface> FindAllByContextCategory (this List<SaInterface> interfaces, String context, SaInterfaceCategory interfaceCategory)
{
return interfaces.FindAll(item => item.Context.EqualsCI(context) && item.Category == interfaceCategory);
}
public static List<SaInterface.Data> FindAllByContextCategory (this List<SaInterface.Data> interfaces, String context, SaInterfaceCategory interfaceCategory)
{
return interfaces.FindAll(item => item.Context.EqualsCI(context) && item.Category == interfaceCategory);
}
public static List<SaInterface> FindAllByContextCategoryType (this List<SaInterface> interfaces, String context, SaInterfaceCategory interfaceCategory, short interfaceType)
{
return interfaces.FindAll(item => item.Context.EqualsCI(context) && item.Category == interfaceCategory && item.Type == interfaceType);
}
public static List<SaInterface.Data> FindAllByContextCategoryType (this List<SaInterface.Data> interfaces, String context, SaInterfaceCategory interfaceCategory, short interfaceType)
{
return interfaces.FindAll(item => item.Context.EqualsCI(context) && item.Category == interfaceCategory && item.Type == interfaceType);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,211 @@
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.ItemOptions;
using Sarand.Net.Standard.Base.UpdateLog;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.Interfaces
{
//=======================================================================
public abstract class SaInterfaceConsole : SaErrorStateItem
{
//-------------------------------------------------------------------
// Interface definition.
public class SaInterfaceDfn
{
public SaInterfaceDfn (String context, SaInterfaceCategory category, TypeCodeDesc typeDfn, bool duplicatesAllowed)
{
Context = context;
Category = category;
TypeDfn = typeDfn;
DuplicatesAllowed = duplicatesAllowed;
}
public String Context { get; } = "";
public SaInterfaceCategory Category { get; } = SaInterfaceCategory.Unknown;
public short InterfaceType { get { return (short)TypeDfn.Type; } }
public TypeCodeDesc TypeDfn { get; } = null;
public bool DuplicatesAllowed { get; } = false;
}
//-------------------------------------------------------------------
// Data members.
private readonly List<SaInterfaceDfn> interfaceDfns = new List<SaInterfaceDfn>();
//-------------------------------------------------------------------
// Static initialisation.
static SaInterfaceConsole ()
{
}
//-------------------------------------------------------------------
// Construction.
protected SaInterfaceConsole ()
{
}
//-------------------------------------------------------------------
// Properties.
public abstract short InterfaceDataType { get; }
public abstract SaItemOptionConsole ItemOptionConsole { get; }
public abstract SaUpdateLogConsole UpdateLogConsole { get; }
// Database.
public abstract bool DatabaseIsValid { get; }
public abstract bool CanUpdateDatabase { get; }
//---------------------------------------------------------------
// Manage interfaces - displays dialog.
public abstract bool ManageInterfaces (String interfaceContext, bool userCanManageInterfaces, bool userCanProcessInterfaces, DbUpdateInfo dbUpdateInfo);
//---------------------------------------------------------------
// Utilities.
public abstract String GetItemTypeDesc (short itemType);
public abstract String GetItemCaption (ItemId itemId);
//---------------------------------------------------------------
// Interface item option definitions.
public abstract List<SaItemOptionDfn> GetInterfaceItemOptionDfns (SaInterfaceCategory interfaceCategory, short interfaceType);
public abstract List<SaItemOptionDfn> GetInterfaceItemOptionDfns (SysId interfaceId);
//-------------------------------------------------------------------
// I n t e r f a c e R e g i s t r a t i o n
//-------------------------------------------------------------------
public bool HasRegisteredInterfaces ()
{
return (interfaceDfns.Count > 0);
}
protected void RegisterInterfaces (String context, SaInterfaceCategory category, List<TypeCodeDesc> types, bool allowDuplicates)
{
foreach (TypeCodeDesc typeDfn in types)
{
// Remove any matches from the list.
interfaceDfns.RemoveAll(item => item.Context.EqualsCI(context) && item.Category == category && item.InterfaceType == typeDfn.Type);
// Add interface definition to list.
SaInterfaceDfn interfaceDfn = new SaInterfaceDfn(context, category, typeDfn, allowDuplicates);
interfaceDfns.Add(interfaceDfn);
}
}
public void ClearRegisteredInterfaces ()
{
interfaceDfns.Clear();
}
public List<SaInterfaceDfn> GetInterfaceDfns (String context, bool createOnly)
{
List<SaInterfaceDfn> interfaceDfns = new List<SaInterfaceDfn>();
foreach (SaInterfaceDfn dfn in this.interfaceDfns.FindAll(item => item.Context.EqualsCI(context)))
{
if (!createOnly || CanCreateInterface(dfn.Context, dfn.Category, dfn.InterfaceType))
interfaceDfns.Add(dfn);
}
return interfaceDfns;
}
public bool HasInterfaceDfns (String context, SaInterfaceCategory interfaceCategory, short interfaceType, bool createOnly)
{
List<SaInterfaceDfn> interfaceDfns = new List<SaInterfaceDfn>();
foreach (SaInterfaceDfn dfn in this.interfaceDfns.FindAll(item => item.Context.EqualsCI(context) && ((int)interfaceCategory < 1 || item.Category == interfaceCategory) && ((int)interfaceType < 1 || item.InterfaceType == interfaceType)))
{
if (!createOnly || CanCreateInterface(dfn.Context, dfn.Category, dfn.InterfaceType))
return true;
}
return false;
}
public List<SaInterfaceDfn> GetInterfaceDfns (String context, SaInterfaceCategory interfaceCategory, short interfaceType, bool createOnly)
{
List<SaInterfaceDfn> interfaceDfns = new List<SaInterfaceDfn>();
foreach (SaInterfaceDfn dfn in this.interfaceDfns.FindAll(item => item.Context.EqualsCI(context) && ((int)interfaceCategory < 1 || item.Category == interfaceCategory) && ((int)interfaceType < 1 || item.InterfaceType == interfaceType)))
{
if (!createOnly || CanCreateInterface(dfn.Context, dfn.Category, dfn.InterfaceType))
interfaceDfns.Add(dfn);
}
return interfaceDfns;
}
public virtual String GetInterfaceTypeDesc (String context, SaInterfaceCategory category, short type)
{
SaInterfaceDfn dfn = interfaceDfns.Find(item => item.Context.EqualsCI(context) && item.Category == category && item.InterfaceType == type);
if (dfn != null)
return dfn.TypeDfn.Desc;
return "";
}
//-------------------------------------------------------------------
// I n t e r f a c e A c c e s s C o n t r o l
//-------------------------------------------------------------------
public abstract bool CanCreateInterfaces ();
public abstract bool CanCreateInterfaces (String context, SaInterfaceCategory interfaceCategory);
public abstract bool CanCreateInterface (String context, SaInterfaceCategory interfaceCategory, short interfaceType);
public abstract bool AreMultipleInterfaceInstancesAllowed (String context, SaInterfaceCategory interfaceCategory, short interfaceType);
public abstract bool AreMultipleActiveInterfacesAllowed (String context, SaInterfaceCategory interfaceCategory);
public abstract bool CanUpdateInterface (SaInterface interfaceObj);
public abstract bool CanCopyInterfaces ();
public abstract bool CanCopyInterfaces (String context, SaInterfaceCategory interfaceCategory);
public abstract bool CanCopyInterface (String context, SaInterfaceCategory interfaceCategory, short interfaceType);
public abstract bool CanCopyInterface (SaInterface interfaceObj);
public abstract bool IsCompatibleForInterfaceCopy (String context, SaInterfaceCategory interfaceCategory, int typeFrom, int typeTo);
public abstract bool CanRemoveInterface (SaInterface interfaceObj);
// Interface management.
public abstract SysId CreateInterface (String context, SaInterfaceCategory interfaceCategory, short interfaceType, String code, String caption, DbUpdateInfo dbUpdateInfo);
// Associated events.
public abstract void OnInterfaceCreated (SysId interfaceId);
public abstract void OnInterfaceUpdated (SysId interfaceId);
public abstract void OnInterfaceRemoved (SysId interfaceId);
// Clear configuration options, data items, processes etc.
public abstract bool ClearDependentData (SaInterface interfaceObj, DbUpdateInfo dbUpdateInfo);
//---------------------------------------------------------------
// Process interface.
public abstract bool CanProcessInterface (SaInterface interfaceObj);
public abstract bool ProcessInterface (SaInterface interfaceObj, DbUpdateInfo dbUpdateInfo);
//---------------------------------------------------------------
// Retrieve interfaces.
public abstract SaInterface GetInterfaceById (SysId interfaceId);
public abstract SaInterface GetInterface (SaInterface.Params prmInterface);
public abstract List<SaInterface> GetInterfaceList (SaInterface.Params prmInterface);
//---------------------------------------------------------------
// Conversion between Interface Category & Code and the equivalent database Id.
public abstract SysId GetInterfaceIdForContextCategoryType (String context, SaInterfaceCategory interfaceCategory, short interfaceType);
public abstract SysId GetInterfaceIdForContextCategoryCode (String context, SaInterfaceCategory interfaceCategory, String code);
public abstract SaInterfaceCategory GetCategoryForInterfaceId (SysId interfaceId);
public abstract String GetCodeForInterfaceId (SysId interfaceId);
//---------------------------------------------------------------
// I n t e r f a c e P r o c e s s e s
//---------------------------------------------------------------
// Identify the highest sequence currently in use for an interface.
public abstract int GetMaxProcessSequence (SysId interfaceId, SaInterfaceProcessDirection processDirection, bool completedOnly = true);
// Create new process and imediately store details on database (status set to 'In Progress').
public abstract SaInterfaceProcess StartInterfaceProcess (SysId interfaceId, SaInterfaceProcessDirection eDirection, String batchRef, DateTime batchAt, int sequence, DbUpdateInfo dbUpdateInfo);
// Retrieve a process using its identity (restricted by interface).
public abstract SaInterfaceProcess GetInterfaceProcess (SysId interfaceId, SysId interfaceProcessId);
// Update the details for a process.
public abstract bool StoreInterfaceProcess (SaInterfaceProcess interfaceProcess, DbUpdateInfo dbUpdateInfo);
//---------------------------------------------------------------
// I n t e r f a c e P r o c e s s M e s s a g e s
//---------------------------------------------------------------
public abstract bool LogInterfaceProcessMessage (SysId interfaceProcessId, DbUpdateInfo dbUpdateInfo, String message, SaMessageLevel messageLevel = SaMessageLevel.Info, String Filename = "", int LineNumber = 0, ItemId ItemId = null);
//---------------------------------------------------------------
// I n t e r f a c e D a t a I t e m s
//---------------------------------------------------------------
public abstract bool ItemHasDataItems (ItemId itemId);
public abstract bool InterfaceDataItemExistsByRef (SysId interfaceId, SaInterfaceProcessDirection processDirection, short itemType, String externalReference);
public abstract SaInterfaceDataItem CreateInterfaceDataItem (SysId interfaceId, SaInterfaceProcessDirection processDirection, ItemId itemId, String externalReference);
public abstract SaInterfaceDataItem GetInterfaceDataItem (SysId interfaceDataItemId);
/// <summary><para>Fails if more than 1 matching data item.</para></summary>
public abstract SaInterfaceDataItem GetInterfaceDataItemByItemRef (SysId interfaceId, SaInterfaceProcessDirection processDirection, ItemId itemId, String externalReference);
/// <summary><para>Fails if more than 1 matching data item.</para></summary>
public abstract SaInterfaceDataItem GetInterfaceDataItemByRef (SysId interfaceId, SaInterfaceProcessDirection processDirection, short itemType, String externalReference);
/// <summary><para>Fails if more than 1 matching data item.</para></summary>
public abstract SaInterfaceDataItem GetInterfaceDataItemByItem (SysId interfaceId, SaInterfaceProcessDirection processDirection, ItemId itemId);
public abstract List<SaInterfaceDataItem> GetInterfaceDataItemsByItem (SysId interfaceId, SaInterfaceProcessDirection processDirection, ItemId itemId);
public abstract List<SaInterfaceDataItem> GetInterfaceDataItemsByRef (SysId interfaceId, SaInterfaceProcessDirection processDirection, short itemType, String externalReference);
public abstract bool StoreInterfaceDataItem (SaInterfaceDataItem interfaceDataItem, DbUpdateInfo dbUpdateInfo);
public abstract bool StoreInterfaceDataItems (List<SaInterfaceDataItem> interfaceDataItems, DbUpdateInfo dbUpdateInfo);
public abstract bool ClearInterfaceDataItem (SaInterfaceDataItem interfaceDataItem, DbUpdateInfo dbUpdateInfo);
public abstract bool ClearInterfaceDataItems (ItemId itemId, DbUpdateInfo dbUpdateInfo);
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,383 @@
using System;
using System.Collections.Generic;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.Interfaces
{
//=======================================================================
public class SaInterfaceDataItem : SacsHierarchyItem
{
//-------------------------------------------------------------------
// Columns.
public enum Column
{
Pos = 0,
Id,
// Interface.
InterfaceId,
// Data item.
Direction,
Direction_Desc,
ExternalReference,
ItemType,
ItemType_Desc,
ItemId,
Item_Caption,
ProcessStatus,
ProcessStatus_Desc,
ProcessStatus_Ind,
ProcessStatusBy,
ProcessStatusBy_Name,
ProcessStatusAt,
ProcessStatusAt_Date,
ProcessStatusAt_Time,
ItemStatus,
ItemStatus_Desc,
ItemStatus_Ind,
ItemStatusBy,
ItemStatusBy_Name,
ItemStatusAt,
ItemStatusAt_Date,
ItemStatusAt_Time,
// Common properties.
Notes,
CreateOn_Date,
UpdateOn_Date,
}
//-------------------------------------------------------------------
/// <summary>Properties other than common.</summary>
public class DataProperties
{
public SysId InterfaceId { get; set; } = null;
public SaInterfaceProcessDirection Direction { get; set; } = SaInterfaceProcessDirection.Unknown;
public ItemId ItemId { get; set; } = null;
public String ExternalReference { get; set; } = String.Empty;
public SaStatus ProcessStatus { get; set; } = SaStatus.Unknown;
public SysId ProcessStatusBy { get; set; } = null;
public DateTime ProcessStatusAt { get; set; } = DateTime.MinValue;
public SaStatus ItemStatus { get; set; } = SaStatus.Unknown;
public SysId ItemStatusBy { get; set; } = null;
public DateTime ItemStatusAt { get; set; } = DateTime.MinValue;
}
//-------------------------------------------------------------------
// Data.
public new class Data : SacsHierarchyItem.Data
{
// Data members.
private readonly DataProperties data = new DataProperties();
// Construction.
public Data ()
{
ProcessStatus = DefaultProcessStatus;
ItemStatus = DefaultItemStatus;
}
// Properties.
public SysId InterfaceId
{
get { return data.InterfaceId; }
set { CheckChanged(data.InterfaceId, value); data.InterfaceId = value; }
}
public SaInterfaceProcessDirection Direction
{
get { return data.Direction; }
set { CheckChanged((int)data.Direction, (int)value); data.Direction = value; }
}
public ItemId ItemId
{
get { return data.ItemId; }
set { CheckChanged(data.ItemId, value); data.ItemId = value; }
}
public String ExternalReference
{
get { return data.ExternalReference; }
set { CheckChanged(data.ExternalReference, value); data.ExternalReference = value; }
}
public SaStatus ProcessStatus
{
get { return data.ProcessStatus; }
set { CheckChanged((int)data.ProcessStatus, (int)value); data.ProcessStatus = value; }
}
public SysId ProcessStatusBy
{
get { return data.ProcessStatusBy; }
set { CheckChanged(data.ProcessStatusBy, value); data.ProcessStatusBy = value; }
}
public DateTime ProcessStatusAt
{
get { return data.ProcessStatusAt; }
set { CheckChanged(data.ProcessStatusAt, value); data.ProcessStatusAt = value; }
}
public SaStatus ItemStatus
{
get { return data.ItemStatus; }
set { CheckChanged((int)data.ItemStatus, (int)value); data.ItemStatus = value; }
}
public SysId ItemStatusBy
{
get { return data.ItemStatusBy; }
set { CheckChanged(data.ItemStatusBy, value); data.ItemStatusBy = value; }
}
public DateTime ItemStatusAt
{
get { return data.ItemStatusAt; }
set { CheckChanged(data.ItemStatusAt, value); data.ItemStatusAt = value; }
}
// Population.
internal void PopulateFrom (Data data)
{
InterfaceId = data.InterfaceId;
Direction = data.Direction;
ItemId = data.ItemId;
ExternalReference = data.ExternalReference;
ProcessStatus = data.ProcessStatus;
ProcessStatusBy = data.ProcessStatusBy;
ProcessStatusAt = data.ProcessStatusAt;
ItemStatus = data.ItemStatus;
ItemStatusBy = data.ItemStatusBy;
ItemStatusAt = data.ItemStatusAt;
base.PopulateFrom(data);
}
public Data Clone ()
{
Data data = new Data();
data.PopulateFrom(this);
return data;
}
}
//-------------------------------------------------------------------
// Error field - identify properties with validation error.
public new class ErrField : SacsHierarchyItem.ErrField
{
// Identifies the field that has invalid data.
public ValidValue InterfaceId { get; set; } = ValidValue.Valid;
public ValidValue Direction { get; set; } = ValidValue.Valid;
public ValidValue ItemType { get; set; } = ValidValue.Valid;
public ValidValue ItemId { get; set; } = ValidValue.Valid;
public ValidValue ExternalReference { get; set; } = ValidValue.Valid;
public ValidValue ProcessStatus { get; set; } = ValidValue.Valid;
public ValidValue ProcessStatusBy { get; set; } = ValidValue.Valid;
public ValidValue ProcessStatusAt { get; set; } = ValidValue.Valid;
public ValidValue ItemStatus { get; set; } = ValidValue.Valid;
public ValidValue ItemStatusBy { get; set; } = ValidValue.Valid;
public ValidValue ItemStatusAt { get; set; } = ValidValue.Valid;
}
//-------------------------------------------------------------------
// Data members.
private readonly Data data = new Data();
// Flags.
public static readonly Byte Flag_OnHold = 0x01;
//-------------------------------------------------------------------
// Construction.
public SaInterfaceDataItem (SysId id, SysId interfaceId, SaInterfaceProcessDirection direction, ItemId itemId, String externalReference) :
base(id)
{
base.SetData(data);
InterfaceId = interfaceId;
Direction = direction;
ItemId = itemId;
ExternalReference = externalReference;
ProcessStatus = DefaultProcessStatus;
ItemStatus = DefaultItemStatus;
}
//-------------------------------------------------------------------
// Process statuses.
private static readonly List<SaStatus> validProcessStatuses = new List<SaStatus>()
{
SaStatus.Awaiting,
SaStatus.Completed,
SaStatus.Cancelled,
SaStatus.Failed
};
public static SaStatus DefaultProcessStatus { get { return AwaitingProcessStatus; } }
public static SaStatus AwaitingProcessStatus { get { return SaStatus.Awaiting; } }
public static SaStatus CompletedProcessStatus { get { return SaStatus.Completed; } }
public static SaStatus CancelledProcessStatus { get { return SaStatus.Cancelled; } }
public static SaStatus FailedProcessStatus { get { return SaStatus.Failed; } }
public static List<SaStatus> ValidProcessStatuses { get { return new List<SaStatus>(validProcessStatuses); } }
public static DataValue.TriStateType GetProcessStatusTriState (SaStatus status)
{
switch (status)
{
case SaStatus.Completed:
return Core.DataValue.TriStateType.Green;
case SaStatus.Awaiting:
return Core.DataValue.TriStateType.Amber;
case SaStatus.Cancelled:
return Core.DataValue.TriStateType.Red;
case SaStatus.Failed:
return Core.DataValue.TriStateType.Yellow;
case SaStatus.Deleted:
return Core.DataValue.TriStateType.Purple;
}
return Core.DataValue.TriStateType.Unknown;
}
//-------------------------------------------------------------------
// Item Statuses.
private static readonly List<SaStatus> validItemStatuses = new List<SaStatus>()
{
SaStatus.InProgress,
SaStatus.Imported,
SaStatus.Exported,
SaStatus.Cancelled,
};
public static SaStatus DefaultItemStatus { get { return SaStatus.Unknown; } }
public static List<SaStatus> ValidItemStatuses { get { return new List<SaStatus>(validItemStatuses); } }
public static DataValue.TriStateType GetItemStatusTriState (SaStatus status)
{
switch (status)
{
case SaStatus.Imported:
case SaStatus.Exported:
return Core.DataValue.TriStateType.Green;
case SaStatus.InProgress:
return Core.DataValue.TriStateType.Amber;
case SaStatus.Cancelled:
return Core.DataValue.TriStateType.Red;
case SaStatus.Deleted:
return Core.DataValue.TriStateType.Purple;
}
return Core.DataValue.TriStateType.Unknown;
}
//-------------------------------------------------------------------
// Properties.
public Data GetData () { return data.Clone(); }
public void SetData (Data data)
{
String fn = GetType().Name + ".SetData";
// Properties that cannot be changed if previously specified.
if (SysId.IsValid(Id) && SysId.ToInt(Id) != SysId.ToInt(data.Id))
throw new NotSupportedException(fn + " Id cannot be changed");
if (SysId.IsValid(InterfaceId) && SysId.ToInt(InterfaceId) != SysId.ToInt(data.InterfaceId))
throw new NotSupportedException(fn + " Interface cannot be changed");
if ((int)Direction > 0 && (int)Direction != (int)data.Direction)
throw new NotSupportedException(fn + " Direction cannot be changed");
if (ItemId.Type >= 0 && ItemId.Type != data.ItemId.Type)
throw new NotSupportedException(fn + " Item type cannot be changed");
if (SysId.IsValid(ItemId.Id) && SysId.ToInt(ItemId.Id) != SysId.ToInt(data.ItemId.Id))
throw new NotSupportedException(fn + " Item Id cannot be changed");
if (!StringUtil.IsEmpty(ExternalReference) && !ExternalReference.Equals(data.ExternalReference))
throw new NotSupportedException(fn + " External reference cannot be changed");
// Populate individual properties to ensure that 'Changed' indicator is kept up to date.
this.data.PopulateFrom(data);
}
public SysId InterfaceId
{
get { return data.InterfaceId; }
protected set { data.InterfaceId = value; }
}
public SaInterfaceProcessDirection Direction
{
get { return data.Direction; }
protected set { data.Direction = value; }
}
public ItemId ItemId
{
get { return data.ItemId; }
protected set { data.ItemId = value; }
}
public String ExternalReference
{
get { return data.ExternalReference; }
set { data.ExternalReference = value; }
}
public SaStatus ProcessStatus
{
get { return data.ProcessStatus; }
set { data.ProcessStatus = value; }
}
public SysId ProcessStatusBy
{
get { return data.ProcessStatusBy; }
set { data.ProcessStatusBy = value; }
}
public DateTime ProcessStatusAt
{
get { return data.ProcessStatusAt; }
set { data.ProcessStatusAt = value; }
}
public SaStatus ItemStatus
{
get { return data.ItemStatus; }
set { data.ItemStatus = value; }
}
public SysId ItemStatusBy
{
get { return data.ItemStatusBy; }
set { data.ItemStatusBy = value; }
}
public DateTime ItemStatusAt
{
get { return data.ItemStatusAt; }
set { data.ItemStatusAt = value; }
}
//-------------------------------------------------------------------
// Flags.
public bool OnHold
{
get { return GetFlag(Flag_OnHold); }
set { SetFlag(Flag_OnHold, value); }
}
//-------------------------------------------------------------------
// List parameters.
public enum ProcessStatusCat
{
Any,
AwaitingOnly,
CompletedOnly,
}
public enum ItemStatusCat
{
Any,
InProgressOnly,
ImportedOnly,
ExportedOnly,
CancelledOnly,
}
/// <summary>
/// Item list parameters.
/// </summary>
public new class Params : SacsHierarchyItem.Params
{
public List<SysId> IncludedIds { get; } = new List<SysId>();
public List<SysId> ExcludedIds { get; } = new List<SysId>();
public SysId InterfaceId { get; set; }
public SaInterfaceProcessDirection Direction { get; set; } = SaInterfaceProcessDirection.Unknown;
public short ItemType { get; set; }
public List<ItemId> IncludedItemIds { get; } = new List<ItemId>();
public String ExternalReference { get; set; }
public ProcessStatusCat ProcessStatusCat { get; set; } = ProcessStatusCat.Any;
public ItemStatusCat ItemStatusCat { get; set; } = ItemStatusCat.Any;
}
//-------------------------------------------------------------------
/// <summary>Mode for filtering associated data items.</summary>
public enum FilterMode
{
None,
Exists,
NotExists,
}
//-------------------------------------------------------------------
// L i s t
//-------------------------------------------------------------------
public class List : List<SaInterfaceDataItem>
{
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,455 @@
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
//---------------------------------------------------------------------------
// Code generated by Sarand Database Utilities Thu 3-Oct-2019 15:10:58
//---------------------------------------------------------------------------
namespace Sarand.Net.Standard.Base.Interfaces
{
public class SaInterfaceDbDfn
{
//-------------------------------------------------------------------
// Table identifiers.
public enum TableIdx
{
Unknown = 0,
SaInterface,
SaInterfaceDataItem,
SaInterfaceProcess,
SaInterfaceProcessDataItem,
SaInterfaceProcessMessage,
}
//-------------------------------------------------------------------
// Column identifiers.
public enum ColumnIdx
{
Unknown = 0,
SaInterface_Id,
SaInterface_Context,
SaInterface_Code,
SaInterface_Category,
SaInterface_Type,
SaInterface_Caption,
SaInterface_Notes,
SaInterface_Flags,
SaInterface_Status,
SaInterface_CreateOn,
SaInterface_CreateBy,
SaInterface_CreateHost,
SaInterface_UpdateOn,
SaInterface_UpdateBy,
SaInterface_UpdateHost,
SaInterfaceDataItem_Id,
SaInterfaceDataItem_InterfaceId,
SaInterfaceDataItem_Direction,
SaInterfaceDataItem_ExternalRef,
SaInterfaceDataItem_ItemType,
SaInterfaceDataItem_ItemId,
SaInterfaceDataItem_ProcessStatus,
SaInterfaceDataItem_ProcessStatusBy,
SaInterfaceDataItem_ProcessStatusAt,
SaInterfaceDataItem_ItemStatus,
SaInterfaceDataItem_ItemStatusBy,
SaInterfaceDataItem_ItemStatusAt,
SaInterfaceDataItem_Notes,
SaInterfaceDataItem_Flags,
SaInterfaceDataItem_CreateOn,
SaInterfaceDataItem_CreateBy,
SaInterfaceDataItem_CreateHost,
SaInterfaceDataItem_UpdateOn,
SaInterfaceDataItem_UpdateBy,
SaInterfaceDataItem_UpdateHost,
SaInterfaceProcess_Id,
SaInterfaceProcess_InterfaceId,
SaInterfaceProcess_Direction,
SaInterfaceProcess_ProcRef,
SaInterfaceProcess_ProcSeq,
SaInterfaceProcess_ProcessAt,
SaInterfaceProcess_BatchAt,
SaInterfaceProcess_ElapseSecs,
SaInterfaceProcess_ItemCount,
SaInterfaceProcess_Flags,
SaInterfaceProcess_Status,
SaInterfaceProcess_CreateOn,
SaInterfaceProcess_CreateBy,
SaInterfaceProcess_CreateHost,
SaInterfaceProcess_UpdateOn,
SaInterfaceProcess_UpdateBy,
SaInterfaceProcess_UpdateHost,
SaInterfaceProcessDataItem_ProcessId,
SaInterfaceProcessDataItem_InterfaceDataItemId,
SaInterfaceProcessDataItem_ItemData,
SaInterfaceProcessDataItem_Flags,
SaInterfaceProcessDataItem_Status,
SaInterfaceProcessDataItem_CreateOn,
SaInterfaceProcessDataItem_CreateBy,
SaInterfaceProcessDataItem_CreateHost,
SaInterfaceProcessDataItem_UpdateOn,
SaInterfaceProcessDataItem_UpdateBy,
SaInterfaceProcessDataItem_UpdateHost,
SaInterfaceProcessMessage_Id,
SaInterfaceProcessMessage_ProcessId,
SaInterfaceProcessMessage_MsgType,
SaInterfaceProcessMessage_Msg,
SaInterfaceProcessMessage_FileName,
SaInterfaceProcessMessage_LineNumber,
SaInterfaceProcessMessage_ItemType,
SaInterfaceProcessMessage_ItemId,
SaInterfaceProcessMessage_Flags,
SaInterfaceProcessMessage_Status,
SaInterfaceProcessMessage_CreateOn,
SaInterfaceProcessMessage_CreateBy,
SaInterfaceProcessMessage_CreateHost,
SaInterfaceProcessMessage_UpdateOn,
SaInterfaceProcessMessage_UpdateBy,
SaInterfaceProcessMessage_UpdateHost,
}
//-------------------------------------------------------------------
// Data members.
private static readonly int tableCount = 5;
private static readonly int columnCount = 79;
private static ulong maxTextLen = 0;
//-------------------------------------------------------------------
// Retrieve Table details (eg. name: 'Company').
public static int GetTableCount ()
{
return tableCount;
}
public static String GetTableName (TableIdx table, bool delimitReservedWords = true)
{
int tableIdx = (int)table - 1;
if (tableIdx < 0 || tableIdx >= tableCount) return String.Empty;
String tableName = tables[(int)tableIdx];
if (delimitReservedWords) tableName = DelimitReservedWord(tableName);
return tableName;
}
//-------------------------------------------------------------------
// Identify the table that corresponds with the specified column.
public static TableIdx GetTableForColumn (ColumnIdx column)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return TableIdx.Unknown;
return columns[columnIdx].TableIdx;
}
//-------------------------------------------------------------------
// Retrieve the specified Column's name (Qualified include table name: '(<Table>.Column>)').
public static String GetColumnName (ColumnIdx column, bool delimitReservedWords = true)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return String.Empty;
String columnName = columns[columnIdx].Name;
if (delimitReservedWords) columnName = DelimitReservedWord(columnName);
return columnName;
}
public static String GetColumnNameQual (ColumnIdx column, String tableAlias = "", bool braces = false)
{
String columnName = GetColumnName(column);
if (!String.IsNullOrEmpty(columnName))
{
String tableName = StringUtil.EmptyOnNull(tableAlias);
if (tableName.IsEmpty()) tableName = GetTableName(GetTableForColumn(column));
columnName = tableName + (tableName.Length > 0 ? "." : "") + columnName;
if (braces)
columnName = "(" + columnName + ")";
}
return columnName;
}
//-------------------------------------------------------------------
// Reserved words.
private static DbReservedWordDelim reservedWordDelim = DbReservedWordDelim.SquareBrackets;
public static void SetReservedWordDelim (DbReservedWordDelim reservedWordDelim)
{
SaInterfaceDbDfn.reservedWordDelim = reservedWordDelim;
}
public static bool IsReservedWord (String word)
{
if (String.IsNullOrEmpty(word)) return false;
if (reservedWords.Contains(word, StringComparer.OrdinalIgnoreCase)) return true;
return false;
}
public static String DelimitReservedWord (String word)
{
if (!IsReservedWord(word)) return word;
switch (reservedWordDelim)
{
case DbReservedWordDelim.None:
break;
case DbReservedWordDelim.DoubleQuotes:
word = "\"" + word + "\"";
break;
case DbReservedWordDelim.SquareBrackets:
default:
word = "[" + word + "]";
break;
}
return word;
}
//-------------------------------------------------------------------
// Retrieve information about the specified Column.
public static DbColumnDataType GetColDataType (ColumnIdx column)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return DbColumnDataType.Unknown;
return columns[columnIdx].DataType;
}
public static short GetColNativeDataType (ColumnIdx column)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return 0;
return columns[columnIdx].NativeDataType;
}
public static void SetMaxTextLen (ulong maxTextLen)
{
SaInterfaceDbDfn.maxTextLen = maxTextLen;
}
public static bool IsColFmtText (ColumnIdx column)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return false;
return (columns[columnIdx].DataType == DbColumnDataType.VarChar);
}
public static ulong GetColTextLen (ColumnIdx column)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return 0;
if (columns[columnIdx].DataType == DbColumnDataType.VarChar)
{
ulong textLen = columns[columnIdx].DataLen;
if (maxTextLen > 0 && textLen > maxTextLen) textLen = maxTextLen;
return textLen;
}
return 0;
}
public static bool IsColFmtBool (ColumnIdx column)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return false;
return (columns[columnIdx].DataType == DbColumnDataType.Bit);
}
public static bool IsColFmtNumeric (ColumnIdx column)
{
int columnIdx = (int)column - 1;
if (columnIdx < 0 || columnIdx >= columnCount) return false;
switch (columns[columnIdx].DataType)
{
case DbColumnDataType.TinyInt:
case DbColumnDataType.SmallInt:
case DbColumnDataType.Int:
case DbColumnDataType.BigInt:
case DbColumnDataType.Single:
case DbColumnDataType.Double:
case DbColumnDataType.Decimal:
return true;
}
return false;
}
//-------------------------------------------------------------------
// Table definitions.
private static readonly String[] tables =
{
"SaInterface",
"SaInterfaceDataItem",
"SaInterfaceProcess",
"SaInterfaceProcessDataItem",
"SaInterfaceProcessMessage",
};
//-------------------------------------------------------------------
// Table column definitions.
internal class ColumnDfn
{
internal TableIdx TableIdx { get; set; }
internal String Name { get; set; }
internal DbColumnDataType DataType { get; set; }
internal short NativeDataType { get; set; }
internal bool IsIdentity { get; set; }
internal ulong DataLen { get; set; }
internal ColumnDfn (int tableIdx, String name, int dataType, short nativeDataType, bool isIdentity, ulong dataLen)
{
this.TableIdx = (TableIdx)tableIdx;
this.Name = name;
this.DataType = (DbColumnDataType)dataType;
this.NativeDataType = nativeDataType;
this.IsIdentity = isIdentity;
this.DataLen = dataLen;
}
}
//-------------------------------------------------------------------
private static readonly ColumnDfn[] columns =
{
new ColumnDfn(1,"Id",4,4,false,10),
new ColumnDfn(1,"Context",0,-9,false,32),
new ColumnDfn(1,"Code",0,-9,false,32),
new ColumnDfn(1,"Category",3,5,false,5),
new ColumnDfn(1,"Type",3,5,false,5),
new ColumnDfn(1,"Caption",0,-9,false,50),
new ColumnDfn(1,"Notes",0,-9,false,255),
new ColumnDfn(1,"Flags",3,5,false,5),
new ColumnDfn(1,"Status",0,-9,false,3),
new ColumnDfn(1,"CreateOn",9,93,false,23),
new ColumnDfn(1,"CreateBy",4,4,false,10),
new ColumnDfn(1,"CreateHost",0,-9,false,64),
new ColumnDfn(1,"UpdateOn",9,93,false,23),
new ColumnDfn(1,"UpdateBy",4,4,false,10),
new ColumnDfn(1,"UpdateHost",0,-9,false,64),
new ColumnDfn(2,"Id",4,4,false,10),
new ColumnDfn(2,"InterfaceId",4,4,false,10),
new ColumnDfn(2,"Direction",3,5,false,5),
new ColumnDfn(2,"ExternalRef",0,-9,false,64),
new ColumnDfn(2,"ItemType",3,5,false,5),
new ColumnDfn(2,"ItemId",4,4,false,10),
new ColumnDfn(2,"ProcessStatus",0,-9,false,3),
new ColumnDfn(2,"ProcessStatusBy",4,4,false,10),
new ColumnDfn(2,"ProcessStatusAt",9,93,false,23),
new ColumnDfn(2,"ItemStatus",0,-9,false,3),
new ColumnDfn(2,"ItemStatusBy",4,4,false,10),
new ColumnDfn(2,"ItemStatusAt",9,93,false,23),
new ColumnDfn(2,"Notes",0,-9,false,255),
new ColumnDfn(2,"Flags",3,5,false,5),
new ColumnDfn(2,"CreateOn",9,93,false,23),
new ColumnDfn(2,"CreateBy",4,4,false,10),
new ColumnDfn(2,"CreateHost",0,-9,false,64),
new ColumnDfn(2,"UpdateOn",9,93,false,23),
new ColumnDfn(2,"UpdateBy",4,4,false,10),
new ColumnDfn(2,"UpdateHost",0,-9,false,64),
new ColumnDfn(3,"Id",4,4,false,10),
new ColumnDfn(3,"InterfaceId",4,4,false,10),
new ColumnDfn(3,"Direction",3,5,false,5),
new ColumnDfn(3,"ProcRef",0,-9,false,32),
new ColumnDfn(3,"ProcSeq",4,4,false,10),
new ColumnDfn(3,"ProcessAt",9,93,false,23),
new ColumnDfn(3,"BatchAt",9,93,false,23),
new ColumnDfn(3,"ElapseSecs",4,4,false,10),
new ColumnDfn(3,"ItemCount",4,4,false,10),
new ColumnDfn(3,"Flags",3,5,false,5),
new ColumnDfn(3,"Status",0,-9,false,3),
new ColumnDfn(3,"CreateOn",9,93,false,23),
new ColumnDfn(3,"CreateBy",4,4,false,10),
new ColumnDfn(3,"CreateHost",0,-9,false,64),
new ColumnDfn(3,"UpdateOn",9,93,false,23),
new ColumnDfn(3,"UpdateBy",4,4,false,10),
new ColumnDfn(3,"UpdateHost",0,-9,false,64),
new ColumnDfn(4,"ProcessId",4,4,false,10),
new ColumnDfn(4,"InterfaceDataItemId",4,4,false,10),
new ColumnDfn(4,"ItemData",0,-9,false,64),
new ColumnDfn(4,"Flags",3,5,false,5),
new ColumnDfn(4,"Status",0,-9,false,3),
new ColumnDfn(4,"CreateOn",9,93,false,23),
new ColumnDfn(4,"CreateBy",4,4,false,10),
new ColumnDfn(4,"CreateHost",0,-9,false,64),
new ColumnDfn(4,"UpdateOn",9,93,false,23),
new ColumnDfn(4,"UpdateBy",4,4,false,10),
new ColumnDfn(4,"UpdateHost",0,-9,false,64),
new ColumnDfn(5,"Id",4,4,false,10),
new ColumnDfn(5,"ProcessId",4,4,false,10),
new ColumnDfn(5,"MsgType",0,12,false,3),
new ColumnDfn(5,"Msg",0,-9,false,1024),
new ColumnDfn(5,"FileName",0,-9,false,64),
new ColumnDfn(5,"LineNumber",4,4,false,10),
new ColumnDfn(5,"ItemType",3,5,false,5),
new ColumnDfn(5,"ItemId",4,4,false,10),
new ColumnDfn(5,"Flags",3,5,false,5),
new ColumnDfn(5,"Status",0,12,false,3),
new ColumnDfn(5,"CreateOn",9,93,false,23),
new ColumnDfn(5,"CreateBy",4,4,false,10),
new ColumnDfn(5,"CreateHost",0,-9,false,64),
new ColumnDfn(5,"UpdateOn",9,93,false,23),
new ColumnDfn(5,"UpdateBy",4,4,false,10),
new ColumnDfn(5,"UpdateHost",0,-9,false,64),
};
//-------------------------------------------------------------------
// Reserved words.
private static readonly List<String> reservedWords = new List<String>()
{
"ADD",
"ALL",
"ALTER",
"AND",
"ANY",
"AS",
"ASC",
"AVG",
"BEGIN",
"BETWEEN",
"BY",
"CASE",
"CAST",
"COLUMN",
"COMMIT",
"CONSTRAINT",
"CONVERT",
"COUNT",
"CREATE",
"CURRENCY",
"DEFAULT",
"DELETE",
"DESC",
"DISTINCT",
"DROP",
"ELSE",
"END",
"EXECUTE",
"EXISTS",
"FALSE",
"FROM",
"GRANT",
"GROUP",
"HAVING",
"IN",
"INDEX",
"INNER",
"INSERT",
"INTO",
"IS",
"JOIN",
"KEY",
"LEFT",
"LIKE",
"MAX",
"MIN",
"NOT",
"NULL",
"ON",
"OR",
"ORDER",
"OUTER",
"OUTPUT",
"PRIMARY",
"PROCEDURE",
"REVOKE",
"ROLLBACK",
"ROWCOUNT",
"SELECT",
"SET",
"SQL",
"SUM",
"TABLE",
"THEN",
"TRANSACTION",
"TRUE",
"UNION",
"UNIQUE",
"UPDATE",
"USER",
"VALUES",
"VIEW",
"WHEN",
"WHERE",
};
//-------------------------------------------------------------------
}
}
@@ -0,0 +1,204 @@
using System;
using System.Collections.Generic;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.Interfaces
{
//=======================================================================
public partial class SaInterfaceMgr
{
//*******************************************************************
// I n t e r f a c e D a t a I t e m s
//*******************************************************************
// Create Interface Data Item.
public abstract SaInterfaceDataItem CreateInterfaceDataItem (SysId interfaceId, SaInterfaceProcessDirection direction, ItemId itemId, String externalReference);
// Retrieve individual interface data items.
public abstract bool InterfaceDataItemExistsById (SysId id);
public abstract SaInterfaceDataItem GetInterfaceDataItemById (SysId id);
public abstract SaInterfaceDataItem GetInterfaceDataItem (SaInterfaceDataItem.Params prm);
//-------------------------------------------------------------------
// Validate Interface Data Item.
public bool IsValidInterfaceDataItem (SaInterfaceDataItem interfaceDataItem)
{
ClearErrorState();
if (interfaceDataItem == null) return SetErrorState(ErrorMessage.ItemNotSpecified);
return IsValidInterfaceDataItemData(interfaceDataItem.GetData(), new SaInterfaceDataItem.ErrField());
}
public virtual bool IsValidInterfaceDataItemData (SaInterfaceDataItem.Data data, SaInterfaceDataItem.ErrField errField)
{
ClearErrorState();
// Interface.
if (errField.InterfaceId != SacsHierarchyItem.ValidValue.Ignore && !SysId.IsValid(data.InterfaceId))
{
errField.InterfaceId = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState("Interface must be supplied");
}
if (errField.Direction != SacsHierarchyItem.ValidValue.Ignore && (int)data.Direction < 1)
{
errField.Direction = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState("Direction must be supplied");
}
// Data item.
if (errField.ItemType != SacsHierarchyItem.ValidValue.Ignore && (data.ItemId == null || data.ItemId.Type < 1))
{
errField.ItemType = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState("Invalid item type");
}
if (errField.ItemId != SacsHierarchyItem.ValidValue.Ignore && (data.ItemId == null || !SysId.IsValid(data.ItemId.Id)))
{
errField.ItemId = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState("Invalid item id");
}
if (errField.ExternalReference != SacsHierarchyItem.ValidValue.Ignore && !IsValidValue(data.ExternalReference, "reference", (long)SaInterfaceDbDfn.GetColTextLen(SaInterfaceDbDfn.ColumnIdx.SaInterfaceDataItem_ExternalRef)))
{
errField.ExternalReference = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
// Process status.
if (errField.ProcessStatus != SacsHierarchyItem.ValidValue.Ignore)
{
if (!IsValidValue(SacsSystem.GetStatusCode(data.ProcessStatus), "process status", (long)SaInterfaceDbDfn.GetColTextLen(SaInterfaceDbDfn.ColumnIdx.SaInterfaceDataItem_ProcessStatus), true))
{
errField.ProcessStatus = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
if (!SaInterfaceDataItem.ValidProcessStatuses.Contains(data.ProcessStatus))
{
errField.Status = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState("Invalid process status");
}
}
// Item status.
if (errField.ItemStatus != SacsHierarchyItem.ValidValue.Ignore)
{
if (!IsValidValue(SacsSystem.GetStatusCode(data.ItemStatus), "item status", (long)SaInterfaceDbDfn.GetColTextLen(SaInterfaceDbDfn.ColumnIdx.SaInterfaceDataItem_ItemStatus), true))
{
errField.ItemStatus = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
if (!SaInterfaceDataItem.ValidItemStatuses.Contains(data.ItemStatus))
{
errField.Status = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState("Invalid item status");
}
}
// Common properties.
if (errField.Notes != SacsHierarchyItem.ValidValue.Ignore && !IsValidValue(data.Notes, "notes", (long)SaInterfaceDbDfn.GetColTextLen(SaInterfaceDbDfn.ColumnIdx.SaInterfaceDataItem_Notes)))
{
errField.Notes = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
// NB No Status on Interface Data Item.
return true;
}
//-------------------------------------------------------------------
// Interface Data Item database interface.
public abstract bool InsertInterfaceDataItem (SaInterfaceDataItem interfaceDataItem, DbUpdateInfo dbUpdateInfo);
public abstract bool UpdateInterfaceDataItem (SaInterfaceDataItem interfaceDataItem, DbUpdateInfo dbUpdateInfo);
public virtual bool InterfaceDataItemCanBeDeleted (SaInterfaceDataItem interfaceDataItem)
{
ClearErrorState();
return true;
}
public abstract bool DeleteInterfaceDataItem (SaInterfaceDataItem interfaceDataItem, DbUpdateInfo dbUpdateInfo);
//-------------------------------------------------------------------
// Interface Data Item list.
public abstract ColumnDfn GetInterfaceDataItemColumnDfn (SaInterfaceDataItem.Column columnId, String caption = "", int width = 0);
public abstract List<String> GetInterfaceDataItemQueryFilter (SaInterfaceDataItem.Params prm);
public abstract long GetInterfaceDataItemCount (SaInterfaceDataItem.Params prm);
public abstract List<SaInterfaceDataItem> GetInterfaceDataItemList (SaInterfaceDataItem.Params prm);
public abstract DbResultSet ExecuteInterfaceDataItemQuery (List<ColumnDfn> columns, SaInterfaceDataItem.Params prm);
//-------------------------------------------------------------------
/// <summary>
/// Extract and format result set data value where specific handling is required.
/// <para>Where response is an invalid data value, format value using column.FormatValue(values).</para>
/// </summary>
public virtual DataValue GetInterfaceDataItemDataValue (ColumnDfn column, NamedValue.Map values)
{
DataValue value = new DataValue();
switch ((SaInterfaceDataItem.Column)column.Id)
{
//-----------------------------------------------------------
// Category.
case SaInterfaceDataItem.Column.Direction_Desc:
{
SaInterfaceProcessDirection direction = (SaInterfaceProcessDirection)StringUtil.ToInt(values.GetValue(column.DataName).ToString());
value = new DataValue(ProcessDirections.GetCodeForType(direction, direction.ToString()));
}
break;
//-----------------------------------------------------------
// Item Type.
case SaInterfaceDataItem.Column.ItemType_Desc:
{
short itemType = StringUtil.ToShort(values.GetValue(column.DataName).ToString());
String itemTypeDesc = InterfaceConsole.GetItemTypeDesc(itemType);
value = new DataValue(itemTypeDesc);
}
break;
//-----------------------------------------------------------
// Item Type.
case SaInterfaceDataItem.Column.Item_Caption:
{
short itemType = StringUtil.ToShort(values.GetValue(GetInterfaceProcessMessageColumnDfn(SaInterfaceProcessMessage.Column.ItemType).DataName).ToString());
SysId itemId = new SysId(StringUtil.ToInt(values.GetValue(column.DataName).ToString()));
String itemTypeDesc = InterfaceConsole.GetItemCaption(new ItemId(itemType,itemId));
value = new DataValue(itemTypeDesc);
}
break;
//-----------------------------------------------------------
// Process Status.
case SaInterfaceDataItem.Column.ProcessStatus_Desc:
case SaInterfaceDataItem.Column.ProcessStatus_Ind:
{
String statusCode = values.GetValue(column.DataName).ToString();
SaStatus status = SacsSystem.GetStatusForCode(statusCode);
if (column.Id == (int)SaInterfaceDataItem.Column.ProcessStatus_Desc)
value = new DataValue(SacsSystem.GetStatusDescForCode(statusCode));
else if (column.Id == (int)SaInterfaceDataItem.Column.ProcessStatus_Ind)
value = new DataValue(SaInterfaceDataItem.GetProcessStatusTriState(status));
}
break;
//-----------------------------------------------------------
// Item Status.
case SaInterfaceDataItem.Column.ItemStatus_Desc:
case SaInterfaceDataItem.Column.ItemStatus_Ind:
{
String statusCode = values.GetValue(column.DataName).ToString();
SaStatus status = SacsSystem.GetStatusForCode(statusCode);
if (column.Id == (int)SaInterfaceDataItem.Column.ItemStatus_Desc)
value = new DataValue(SacsSystem.GetStatusDescForCode(statusCode));
else if (column.Id == (int)SaInterfaceDataItem.Column.ItemStatus_Ind)
value = new DataValue(SaInterfaceDataItem.GetItemStatusTriState(status));
}
break;
//-----------------------------------------------------------
// Notes.
case SaInterfaceDataItem.Column.Notes:
{
value = new DataValue(SacsHierarchyItem.FormatForDisplay(values.GetValue(column.DataName).ToString(), 200));
}
break;
//-----------------------------------------------------------
}
return value;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.Interfaces
{
//=======================================================================
public partial class SaInterfaceMgr
{
//*******************************************************************
// I n t e r f a c e P r o c e s s D a t a I t e m s
//*******************************************************************
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,166 @@
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.Interfaces
{
//=======================================================================
public partial class SaInterfaceMgr
{
//*******************************************************************
// I n t e r f a c e P r o c e s s M e s s a g e s
//*******************************************************************
// Retrieve individual interface process messages.
public abstract bool InterfaceProcessMessageExistsById (SysId id);
public abstract SaInterfaceProcessMessage GetInterfaceProcessMessageById (SysId id);
public abstract SaInterfaceProcessMessage GetInterfaceProcessMessage (SaInterfaceProcessMessage.Params prm);
// Create Interface Process Message.
public abstract SaInterfaceProcessMessage CreateInterfaceProcessMessage (SysId interfaceProcessId);
//-------------------------------------------------------------------
// Validate Interface Process Message.
public bool IsValidInterfaceProcessMessage (SaInterfaceProcessMessage interfaceProcessMessage)
{
ClearErrorState();
if (interfaceProcessMessage == null) return SetErrorState(ErrorMessage.ItemNotSpecified);
return IsValidInterfaceProcessMessageData(interfaceProcessMessage.GetData(), new SaInterfaceProcessMessage.ErrField());
}
public virtual bool IsValidInterfaceProcessMessageData (SaInterfaceProcessMessage.Data data, SaInterfaceProcessMessage.ErrField errField)
{
ClearErrorState();
if (errField.InterfaceProcessId != SacsHierarchyItem.ValidValue.Ignore && !SysId.IsValid(data.InterfaceProcessId))
{
errField.InterfaceProcessId = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState("Process must be supplied");
}
if (errField.Level != SacsHierarchyItem.ValidValue.Ignore && (int)data.Level < 1)
{
errField.Level = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState("Level must be supplied");
}
if (errField.Level != SacsHierarchyItem.ValidValue.Ignore && !IsValidValue(messageLevels.GetCodeForType(data.Level), "level", (long)SaInterfaceDbDfn.GetColTextLen(SaInterfaceDbDfn.ColumnIdx.SaInterfaceProcessMessage_MsgType), true, true))
{
errField.Level = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
if (errField.Message != SacsHierarchyItem.ValidValue.Ignore && !IsValidValue(data.Message, "message", (long)SaInterfaceDbDfn.GetColTextLen(SaInterfaceDbDfn.ColumnIdx.SaInterfaceProcessMessage_Msg), true))
{
errField.Message = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
if (errField.Filename != SacsHierarchyItem.ValidValue.Ignore && !IsValidValue(data.Filename, "filename", (long)SaInterfaceDbDfn.GetColTextLen(SaInterfaceDbDfn.ColumnIdx.SaInterfaceProcessMessage_FileName)))
{
errField.Filename = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
if (errField.LineNumber != SacsHierarchyItem.ValidValue.Ignore && data.LineNumber < 0)
{
errField.LineNumber = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState("Invalid line number");
}
// Common properties.
if (errField.Status != SacsHierarchyItem.ValidValue.Ignore && !SaInterfaceProcessMessage.ValidStatuses.Contains(data.Status))
{
errField.Status = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState(ErrorMessage.InvalidStatus);
}
return true;
}
//-------------------------------------------------------------------
// Interface Process Message database interface.
public abstract bool InsertInterfaceProcessMessage (SaInterfaceProcessMessage interfaceProcessMessage, DbUpdateInfo dbUpdateInfo);
public abstract bool UpdateInterfaceProcessMessage (SaInterfaceProcessMessage interfaceProcessMessage, DbUpdateInfo dbUpdateInfo);
public virtual bool InterfaceProcessMessageCanBeDeleted (SaInterfaceProcessMessage interfaceProcessMessage)
{
ClearErrorState();
return true;
}
public abstract bool DeleteInterfaceProcessMessage (SaInterfaceProcessMessage interfaceProcessMessage, DbUpdateInfo dbUpdateInfo);
//-------------------------------------------------------------------
// Interface Process Message list.
public abstract ColumnDfn GetInterfaceProcessMessageColumnDfn (SaInterfaceProcessMessage.Column columnId, String caption = "", int width = 0);
public abstract List<String> GetInterfaceProcessMessageQueryFilter (SaInterfaceProcessMessage.Params prm);
public abstract long GetInterfaceProcessMessageCount (SaInterfaceProcessMessage.Params prm);
public abstract List<SaInterfaceProcessMessage> GetInterfaceProcessMessageList (SaInterfaceProcessMessage.Params prm);
public abstract DbResultSet ExecuteInterfaceProcessMessageQuery (List<ColumnDfn> columns, SaInterfaceProcessMessage.Params prm);
//-------------------------------------------------------------------
/// <summary>
/// Extract and format result set data value where specific handling is required.
/// <para>Where response is an invalid data value, format value using column.FormatValue(values).</para>
/// </summary>
public virtual DataValue GetInterfaceProcessMessageDataValue (ColumnDfn column, NamedValue.Map values)
{
DataValue value = new DataValue();
switch ((SaInterfaceProcessMessage.Column)column.Id)
{
//-----------------------------------------------------------
// Category.
case SaInterfaceProcessMessage.Column.Level_Desc:
{
String level = values.GetValue(column.DataName).ToString();
// Legacy 'Warning' code: 'WAR'.
if (level.EndsWithCI(MessageTypeWarningAlt)) level = messageLevels.GetCodeForType(SaMessageLevel.Warning);
value = new DataValue(messageLevels.GetDescForCode(level, level));
}
break;
//-----------------------------------------------------------
// Item Type.
case SaInterfaceProcessMessage.Column.ItemType_Desc:
{
short itemType = StringUtil.ToShort(values.GetValue(column.DataName).ToString());
String itemTypeDesc = InterfaceConsole.GetItemTypeDesc(itemType);
value = new DataValue(itemTypeDesc);
}
break;
//-----------------------------------------------------------
// Item Type.
case SaInterfaceProcessMessage.Column.Item_Caption:
{
short itemType = StringUtil.ToShort(values.GetValue(GetInterfaceProcessMessageColumnDfn(SaInterfaceProcessMessage.Column.ItemType).DataName).ToString());
SysId itemId = new SysId(StringUtil.ToInt(values.GetValue(column.DataName).ToString()));
String itemTypeDesc = InterfaceConsole.GetItemCaption(new ItemId(itemType,itemId));
value = new DataValue(itemTypeDesc);
}
break;
//-----------------------------------------------------------
// Notes.
case SaInterfaceProcessMessage.Column.Notes:
{
value = new DataValue(SacsHierarchyItem.FormatForDisplay(values.GetValue(column.DataName).ToString(), 200));
}
break;
//-----------------------------------------------------------
// Status.
case SaInterfaceProcessMessage.Column.Status_Desc:
case SaInterfaceProcessMessage.Column.Status_Ind:
{
String statusCode = values.GetValue(column.DataName).ToString();
SaStatus status = SacsSystem.GetStatusForCode(statusCode);
if (column.Id == (int)SaInterfaceProcessMessage.Column.Status_Desc)
value = new DataValue(SacsSystem.GetStatusDescForCode(statusCode));
else if (column.Id == (int)SaInterfaceProcessMessage.Column.Status_Ind)
value = new DataValue(SaInterfaceProcessMessage.GetStatusTriState(status));
}
break;
//-----------------------------------------------------------
}
return value;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,238 @@
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.Net.Standard.Base.Interfaces
{
//=======================================================================
public partial class SaInterfaceMgr
{
//*******************************************************************
// I n t e r f a c e P r o c e s s e s
//*******************************************************************
// Data members.
protected static readonly String colInterfaceProcess_MessageStatusErr = "IntPrcMessageStatusErr";
protected static readonly String colInterfaceProcess_MessageStatusWrn = "IntPrcMessageStatusWrn";
protected static readonly String colInterfaceProcess_MessageStatusInf = "IntPrcMessageStatusInf";
//-------------------------------------------------------------------
// Associated data.
public abstract int GetInterfaceProcessMessageCount (SysId id);
public abstract bool InterfaceProcessHasMessages (SysId id);
public abstract int GetInterfaceProcessDataItemCount (SysId id);
public abstract bool InterfaceProcessHasDataItems (SysId id);
//-------------------------------------------------------------------
// Retrieve individual interface processes.
public abstract bool InterfaceProcessExistsById (SysId id);
public abstract SaInterfaceProcess GetInterfaceProcessById (SysId id);
public abstract SaInterfaceProcess GetInterfaceProcess (SaInterfaceProcess.Params prm);
// Create Interface Process.
public abstract SaInterfaceProcess CreateInterfaceProcess (SysId interfaceId, SaInterfaceProcessDirection direction, String reference, int sequence);
//-------------------------------------------------------------------
// Validate Interface Process.
public bool IsValidInterfaceProcess (SaInterfaceProcess interfaceProcess)
{
ClearErrorState();
if (interfaceProcess == null) return SetErrorState(ErrorMessage.ItemNotSpecified);
return IsValidInterfaceProcessData(interfaceProcess.GetData(), new SaInterfaceProcess.ErrField());
}
public virtual bool IsValidInterfaceProcessData (SaInterfaceProcess.Data data, SaInterfaceProcess.ErrField errField)
{
ClearErrorState();
if (errField.InterfaceId != SacsHierarchyItem.ValidValue.Ignore && !SysId.IsValid(data.InterfaceId))
{
errField.InterfaceId = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState("Interface must be supplied");
}
if (errField.Direction != SacsHierarchyItem.ValidValue.Ignore && (int)data.Direction < 1)
{
errField.Direction = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState("Direction must be supplied");
}
if (errField.Reference != SacsHierarchyItem.ValidValue.Ignore && !IsValidValue(data.Reference, "reference", (long)SaInterfaceDbDfn.GetColTextLen(SaInterfaceDbDfn.ColumnIdx.SaInterfaceProcess_ProcRef)))
{
errField.Reference = SacsHierarchyItem.ValidValue.Invalid;
return false;
}
if (errField.Sequence != SacsHierarchyItem.ValidValue.Ignore && data.Sequence < 1)
{
errField.Sequence = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState("Sequence must be supplied");
}
// Common properties.
if (errField.Status != SacsHierarchyItem.ValidValue.Ignore && !SaInterfaceProcess.ValidStatuses.Contains(data.Status))
{
errField.Status = SacsHierarchyItem.ValidValue.Invalid;
return SetErrorState(ErrorMessage.InvalidStatus);
}
return true;
}
//-------------------------------------------------------------------
// Interface Process database interface.
public abstract bool InsertInterfaceProcess (SaInterfaceProcess interfaceProcess, DbUpdateInfo dbUpdateInfo);
public abstract bool UpdateInterfaceProcess (SaInterfaceProcess interfaceProcess, DbUpdateInfo dbUpdateInfo);
public virtual bool InterfaceProcessCanBeDeleted (SaInterfaceProcess interfaceProcess)
{
ClearErrorState();
if (InterfaceProcessHasMessages(interfaceProcess.Id)) return SetErrorState("Process has messages");
if (InterfaceProcessHasDataItems(interfaceProcess.Id)) return SetErrorState("Process has data items");
return true;
}
public abstract bool ClearInterfaceProcessMessages (List<SysId> interfaceProcessIds, DbUpdateInfo dbUpdateInfo);
public abstract bool ClearInterfaceProcessDataItems (List<SysId> interfaceProcessIds, DbUpdateInfo dbUpdateInfo);
public abstract bool DeleteInterfaceProcess (SaInterfaceProcess interfaceProcess, DbUpdateInfo dbUpdateInfo);
//-------------------------------------------------------------------
// Interface Process list.
public abstract ColumnDfn GetInterfaceProcessColumnDfn (SaInterfaceProcess.Column columnId, String caption = "", int width = 0);
public abstract List<String> GetInterfaceProcessQueryFilter (SaInterfaceProcess.Params prm);
public abstract long GetInterfaceProcessCount (SaInterfaceProcess.Params prm);
public abstract List<SaInterfaceProcess> GetInterfaceProcessList (SaInterfaceProcess.Params prm);
public abstract DbResultSet ExecuteInterfaceProcessQuery (List<ColumnDfn> columns, SaInterfaceProcess.Params prm);
//-------------------------------------------------------------------
/// <summary>
/// Extract and format result set data value where specific handling is required.
/// <para>Where response is an invalid data value, format value using column.FormatValue(values).</para>
/// </summary>
public virtual DataValue GetInterfaceProcessDataValue (ColumnDfn column, NamedValue.Map values)
{
DataValue value = new DataValue();
switch ((SaInterfaceProcess.Column)column.Id)
{
//-----------------------------------------------------------
// Category.
case SaInterfaceProcess.Column.Interface_Category_Desc:
{
SaInterfaceCategory category = (SaInterfaceCategory)StringUtil.ToInt(values.GetValue(column.DataName).ToString());
value = new DataValue(interfaceCategories.GetCodeForType(category, category.ToString()));
break;
}
//-----------------------------------------------------------
// Type.
case SaInterfaceProcess.Column.Interface_Type_Desc:
{
String context = values.GetValue(GetInterfaceProcessColumnDfn(SaInterfaceProcess.Column.Interface_Context).DataName).ToString();
SaInterfaceCategory category = (SaInterfaceCategory)StringUtil.ToInt(values.GetValue(GetInterfaceProcessColumnDfn(SaInterfaceProcess.Column.Interface_Category).DataName).ToString());
short type = StringUtil.ToShort(values.GetValue(column.DataName).ToString());
String typeDesc = InterfaceConsole.GetInterfaceTypeDesc(context,category,type);
if (StringUtil.IsEmpty(typeDesc)) typeDesc = type.ToString();
value = new DataValue(typeDesc);
break;
}
//-----------------------------------------------------------
// Process Direction.
case SaInterfaceProcess.Column.ProcessDirection_Desc:
{
SaInterfaceProcessDirection direction = (SaInterfaceProcessDirection)StringUtil.ToInt(values.GetValue(column.DataName).ToString());
value = new DataValue(processDirections.GetCodeForType(direction, direction.ToString()));
break;
}
//-----------------------------------------------------------
// Processed & Batch DateTimes.
case SaInterfaceProcess.Column.ProcessAt:
case SaInterfaceProcess.Column.ProcessAt_Date:
case SaInterfaceProcess.Column.ProcessAt_Time:
{
// Skip if 'ProcessAt' db column exists.
if (ColumnExists(SaInterfaceDbDfn.ColumnIdx.SaInterfaceProcess_ProcessAt)) break;
int processDate = (int)values.GetValue(GetInterfaceProcessColumnDfn(SaInterfaceProcess.Column.ProcessAt_Date).DataName).AsLong();
int processTime = (int)values.GetValue(GetInterfaceProcessColumnDfn(SaInterfaceProcess.Column.ProcessAt_Time).DataName).AsLong();
DateTime processAt = DateUtil.Int32ToDate(processDate);
processAt = processAt.SetTime(DateUtil.Int32ToTime(processTime));
value = new DataValue(processAt);
break;
}
case SaInterfaceProcess.Column.BatchAt:
case SaInterfaceProcess.Column.BatchAt_Date:
case SaInterfaceProcess.Column.BatchAt_Time:
{
// Skip if 'BatchAt' db column exists.
if (ColumnExists(SaInterfaceDbDfn.ColumnIdx.SaInterfaceProcess_BatchAt)) break;
int batchDate = (int)values.GetValue(GetInterfaceProcessColumnDfn(SaInterfaceProcess.Column.BatchAt_Date).DataName).AsLong();
int batchTime = (int)values.GetValue(GetInterfaceProcessColumnDfn(SaInterfaceProcess.Column.BatchAt_Time).DataName).AsLong();
DateTime batchAt = DateUtil.Int32ToDate(batchDate);
batchAt = batchAt.SetTime(DateUtil.Int32ToTime(batchTime));
value = new DataValue(batchAt);
break;
}
//-----------------------------------------------------------
// Elapsed time.
case SaInterfaceProcess.Column.Elapsed_Fmt:
{
int elapsedSecs = StringUtil.ToInt(values.GetValue(column.DataName).ToString());
value = new DataValue(DateUtil.FormatNumberOfSeconds(elapsedSecs));
break;
}
//-----------------------------------------------------------
// Status.
case SaInterfaceProcess.Column.Message_Status_Desc:
case SaInterfaceProcess.Column.Message_Status_Ind:
{
long messageCountErr = values.GetValue(colInterfaceProcess_MessageStatusErr).AsLong();
long messageCountWrn = values.GetValue(colInterfaceProcess_MessageStatusWrn).AsLong();
long messageCountInf = values.GetValue(colInterfaceProcess_MessageStatusInf).AsLong();
if (column.Id == (int)SaInterfaceProcess.Column.Message_Status_Desc)
{
String statusDesc = "";
if (messageCountErr > 0) statusDesc = messageLevels.GetCodeForType(SaMessageLevel.Error, "Error");
else if (messageCountWrn > 0) statusDesc = messageLevels.GetCodeForType(SaMessageLevel.Warning, "Warning");
else if (messageCountInf > 0) statusDesc = messageLevels.GetCodeForType(SaMessageLevel.Info, "Info");
value = new DataValue(statusDesc);
}
else if (column.Id == (int)SaInterfaceProcess.Column.Message_Status_Ind)
{
DataValue.TriStateType triState = DataValue.TriStateType.Unknown;
if (messageCountErr > 0) triState = DataValue.TriStateType.Red;
else if (messageCountWrn > 0) triState = DataValue.TriStateType.Amber;
else if (messageCountInf > 0) triState = DataValue.TriStateType.Green;
value = new DataValue(triState);
}
break;
}
//-----------------------------------------------------------
// Notes.
case SaInterfaceProcess.Column.Notes:
{
value = new DataValue(SacsHierarchyItem.FormatForDisplay(values.GetValue(column.DataName).ToString(), 200));
break;
}
//-----------------------------------------------------------
// Status.
case SaInterfaceProcess.Column.Status_Desc:
case SaInterfaceProcess.Column.Status_Ind:
{
String statusCode = values.GetValue(column.DataName).ToString();
SaStatus status = SacsSystem.GetStatusForCode(statusCode);
if (column.Id == (int)SaInterfaceProcess.Column.Status_Desc)
value = new DataValue(SacsSystem.GetStatusDescForCode(statusCode));
else if (column.Id == (int)SaInterfaceProcess.Column.Status_Ind)
value = new DataValue(SaInterfaceProcess.GetStatusTriState(status));
break;
}
//-----------------------------------------------------------
}
return value;
}
//-------------------------------------------------------------------
}
//=======================================================================
}

Some files were not shown because too many files have changed in this diff Show More