Saxonite 1.2.11.2

This commit is contained in:
Andy Howard
2024-02-27 15:45:02 +00:00
parent f3350fdc23
commit 2239f37408
54 changed files with 3613 additions and 1175 deletions
+4 -3
View File
@@ -1,9 +1,10 @@
# Sarand
## Common Projects Visual Studio 2019
## Common Projects Visual Studio 2022
### Modules
* NB All projects upgraded to .Net 6, 28Nov23.
* SACS Standard Base
* Base class library. .Net Standard 2.1. C#.
* SACS Standard Dbas
@@ -13,7 +14,7 @@
* SACS Standard Mob
* Base Mobile application library. .Net Standard 2.1. C#.
* SACS Core Web
* Base Web application library. .Net Core 3.1. C#.
* Base Web application library. [C# .Net 6]
* SACS Core Unit Tests
* Unit Tests. .Net Core 3.1. C#.
* Unit Tests. [C# .Net 6]
+8 -2
View File
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31112.23
# Visual Studio Version 17
VisualStudioVersion = 17.8.34316.72
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SacsStandardBase", "SacsStandardBase\SacsStandardBase.csproj", "{3A8EE4B3-2335-420B-9619-D1622AF511FA}"
EndProject
@@ -20,6 +20,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution files", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SacsStandardAccounts", "SacsStandardAccounts\SacsStandardAccounts.csproj", "{C62999E2-1947-4EB7-82D2-A0EDB67D2F10}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SacsStandardProc", "SacsStandardProc\SacsStandardProc.csproj", "{3988EE9F-FFCD-481A-AF5E-AA7E5DB5A415}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -50,6 +52,10 @@ Global
{C62999E2-1947-4EB7-82D2-A0EDB67D2F10}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C62999E2-1947-4EB7-82D2-A0EDB67D2F10}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C62999E2-1947-4EB7-82D2-A0EDB67D2F10}.Release|Any CPU.Build.0 = Release|Any CPU
{3988EE9F-FFCD-481A-AF5E-AA7E5DB5A415}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3988EE9F-FFCD-481A-AF5E-AA7E5DB5A415}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3988EE9F-FFCD-481A-AF5E-AA7E5DB5A415}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3988EE9F-FFCD-481A-AF5E-AA7E5DB5A415}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
@@ -0,0 +1,66 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Sacs.UnitTests.Net.Standard.Base.Utils
{
[TestClass]
public class SerializableUtil_Tester
{
//-------------------------------------------------------------------
// Definitions.
[Serializable]
private class TestData
{
public String stringValue = "String Value";
public long longValue = 123456;
}
[Serializable]
private class TestDataA : TestData
{
public TestDataA()
{
stringValue = "String Value A";
longValue = 654321;
}
}
//-------------------------------------------------------------------
// Data members.
private Random random = new Random();
//-------------------------------------------------------------------
// Deep Copy.
//-------------------------------------------------------------------
[TestMethod]
public void SerializableUtil_DeepCopy()
{
// Clone test data.
TestData data = new TestData();
TestData data_DeepCopy = SerializableUtil.DeepCopy(data);
// Verify results.
Assert.AreEqual(true, data_DeepCopy is TestData);
Assert.AreEqual(data.stringValue, data_DeepCopy.stringValue);
Assert.AreEqual(data.longValue, data_DeepCopy.longValue);
// Clone test data A.
TestDataA dataA = new TestDataA();
TestDataA dataA_DeepCopy = SerializableUtil.DeepCopy(dataA);
// Verify results.
Assert.AreEqual(true, dataA_DeepCopy is TestData);
Assert.AreEqual(true, dataA_DeepCopy is TestDataA);
Assert.AreEqual(dataA.stringValue, dataA_DeepCopy.stringValue);
Assert.AreEqual(dataA.longValue, dataA_DeepCopy.longValue);
Assert.AreNotEqual(data.stringValue, dataA_DeepCopy.stringValue);
Assert.AreNotEqual(data.longValue, dataA_DeepCopy.longValue);
}
//-------------------------------------------------------------------
}
}
@@ -0,0 +1,111 @@
using System;
using System.Diagnostics;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.UnitTests.Core.Base.Utils
{
[TestClass]
public class SysUtil_Tester
{
//-------------------------------------------------------------------
// Data members.
private static readonly String ClassName_GeneratedMemberName_Prefix = "<";
private static readonly String ClassName_CompilerGeneratedClass = ClassName_GeneratedMemberName_Prefix + ">c";
//-------------------------------------------------------------------
// Calling code class name.
//-------------------------------------------------------------------
[TestMethod]
public void SysUtil_GetCallerClassName_Test()
{
// Leading spaces.
Assert.AreNotEqual(null, SysUtil.GetCallerClassName());
Assert.AreNotEqual("", SysUtil.GetCallerClassName());
Assert.AreEqual("SysUtil_Tester", SysUtil.GetCallerClassName());
// Compiler generated context.
Func<string> temp = () => { return SysUtil.GetCallerClassName(); };
Assert.AreEqual(ClassName_CompilerGeneratedClass, temp());
// Anonymous function.
Assert.AreEqual(ClassName_CompilerGeneratedClass, ((Func<string>)(() => SysUtil.GetCallerClassName()))());
Assert.AreEqual(ClassName_CompilerGeneratedClass, new Func<string>(() => SysUtil.GetCallerClassName())());
}
//-------------------------------------------------------------------
// Calling code function name.
//-------------------------------------------------------------------
[TestMethod]
public void SysUtil_GetCallerMemberName_Test()
{
// Leading spaces.
Assert.AreNotEqual(null, SysUtil.GetCallerMemberName());
Assert.AreNotEqual("", SysUtil.GetCallerMemberName());
Assert.AreEqual("SysUtil_GetCallerMemberName_Test", SysUtil.GetCallerMemberName());
Assert.AreEqual(nameof(SysUtil_GetCallerMemberName_Test), SysUtil.GetCallerMemberName());
Assert.AreEqual(MethodBase.GetCurrentMethod().Name, SysUtil.GetCallerMemberName());
// Compiler generated context.
Func<string> temp = () => { return SysUtil.GetCallerMemberName(); };
Assert.AreEqual(MethodBase.GetCurrentMethod().Name, temp());
// Anonymous function.
Assert.AreEqual(MethodBase.GetCurrentMethod().Name, ((Func<string>)(() => SysUtil.GetCallerMemberName()))());
Assert.AreEqual(MethodBase.GetCurrentMethod().Name, new Func<string>(() => SysUtil.GetCallerMemberName())());
}
//-------------------------------------------------------------------
// Calling code file path.
//-------------------------------------------------------------------
[TestMethod]
public void SysUtil_GetCallerFilePath_Test()
{
// Leading spaces.
Assert.AreNotEqual(null, SysUtil.GetCallerFilePath());
Assert.AreNotEqual("", SysUtil.GetCallerFilePath());
Assert.AreEqual(@"D:\Sarand\Projects\Users\AHoward\VS2022\Common-VS\Workspace\SacsCoreUnitTests\Standard\Base\Utils\SysUtil_Tester.cs", SysUtil.GetCallerFilePath());
Assert.AreEqual(true, SysUtil.GetCallerFilePath().EndsWith("SysUtil_Tester.cs"));
}
//-------------------------------------------------------------------
// Calling code line number.
//-------------------------------------------------------------------
[TestMethod]
public void SysUtil_GetCallerLineNumber_Test()
{
// Leading spaces.
Assert.AreNotEqual(0, SysUtil.GetCallerLineNumber());
Assert.AreEqual((new StackFrame(0, true)).GetFileLineNumber(), SysUtil.GetCallerLineNumber());
}
//-------------------------------------------------------------------
// Calling code file path.
//-------------------------------------------------------------------
[TestMethod]
public void SysUtil_GetCallerClassMethod_Test()
{
// Leading spaces.
Assert.AreNotEqual(null, SysUtil.GetCallerClassMethod());
Assert.AreNotEqual("", SysUtil.GetCallerClassMethod());
Assert.AreEqual(GetType().Name + "." + MethodBase.GetCurrentMethod().Name, SysUtil.GetCallerClassMethod());
Assert.AreEqual(nameof(SysUtil_Tester) + "." + nameof(SysUtil_GetCallerClassMethod_Test), SysUtil.GetCallerClassMethod());
}
//-------------------------------------------------------------------
// Calling code file path.
//-------------------------------------------------------------------
[TestMethod]
public void SysUtil_Fn_Test()
{
// Leading spaces.
Assert.AreNotEqual(null, SysUtil.Fn);
Assert.AreNotEqual("", SysUtil.Fn);
Assert.AreEqual(GetType().Name + "." + MethodBase.GetCurrentMethod().Name, SysUtil.Fn);
Assert.AreEqual(nameof(SysUtil_Tester) + "." + nameof(SysUtil_Fn_Test), SysUtil.Fn);
}
//-------------------------------------------------------------------
}
}
+15 -11
View File
@@ -1,16 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Sacs.Net.Core</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Sacs</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SacsStandardBase\SacsStandardBase.csproj" />
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<Folder Include="Web\App\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SacsStandardBase\SacsStandardBase.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Web\App\" />
</ItemGroup>
</Project>
+423
View File
@@ -0,0 +1,423 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Routing;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
namespace Sacs.Web.Components
{
//=======================================================================
public class SaController : Controller
{
//-------------------------------------------------------------------
/// <summary>
/// Protected construction - use derived objects.
/// </summary>
protected SaController()
{
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
}
base.Dispose(disposing);
}
//-------------------------------------------------------------------
// Properties.
private static String ClassName { get; } = nameof(SaController);
public DateTime CreatedAt { get; } = DateTime.Now;
public bool DebugMode { get; } = SacsSystem.DebugMode;
/// <summary>
/// Indicate whether session data should be stored by action.
/// <para>Any action that might modify the session data must store it.</para>
/// <para>Some actions such as scoring a password's complexity don't need to.</para>
/// </summary>
public virtual bool StoreSessionData { get; protected set; } = true;
//-------------------------------------------------------------------
// Culture.
private CultureInfo sessionCulture;
private CultureInfo RequestCulture { get { return Request.HttpContext.Features.Get<IRequestCultureFeature>().RequestCulture.Culture ?? CultureInfo.InvariantCulture; } }
public CultureInfo SessionCulture
{
get
{
if (sessionCulture == null)
{
sessionCulture = RequestCulture;
// ToDo Adjust culture if needed.
}
return sessionCulture;
}
}
private string LanguageIso2
{
get
{
var feature = HttpContext.Features.Get<IRequestCultureFeature>();
return feature.RequestCulture.UICulture.TwoLetterISOLanguageName.ToLower();
}
}
//-------------------------------------------------------------------
// Redirection locations.
//-------------------------------------------------------------------
public static readonly String ParamName_SessionExpired = "SessionExpired";
public enum RedirectLocation
{
WebAppBase,
WebAppHome,
WebAppHome_SessionExpired,
}
public String GetRedirectLocation(RedirectLocation redirectLocation)
{
string protocol = Request.Scheme;
var parameters = new RouteValueDictionary();
String homeAction = Url.Action("Index", "Home", parameters, protocol);
switch (redirectLocation)
{
case RedirectLocation.WebAppBase:
return Regex.Replace(homeAction, "\\/$", "") + "/";
case RedirectLocation.WebAppHome:
return homeAction;
case RedirectLocation.WebAppHome_SessionExpired:
// Set the session expired parameter so that the page can report it.
// NB Paramater must have a value to be included in the URL query string.
parameters.Add(ParamName_SessionExpired, "1");
return homeAction;
}
return "";
}
//-------------------------------------------------------------------
// Action execution events.
//-------------------------------------------------------------------
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//if (DebugMode) SaLogger.Debug(GetType().Name + ".OnActionExecuting" + (ViewData.Model != null ? " Model: " + ViewData.Model : ""));
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
//if (DebugMode) SaLogger.Debug(GetType().Name + ".OnActionExecuted" + (ViewData.Model != null ? " Model: " + ViewData.Model : ""));
base.OnActionExecuted(filterContext);
}
//-------------------------------------------------------------------
public static String EncryptParameters(NameValueCollection parameters, String base64IV, String base64Key)
{
String fn = ClassName + ".EncryptParameters";
try
{
// Copy the list of parameters and sort the keys.
NameValueCollection parametersToEncrypt = new NameValueCollection(parameters);
String[] paramKeys = parametersToEncrypt.AllKeys;
Array.Sort(paramKeys);
// Serialise parameters as if in a URL query string - so that they can be deserialised in DecryptParameters() below.
// Start with a random length parameter name at start of list to obscure the encryption.
string queryString = SacsRandomHelper.getRandomLengthNumber(4) + "&";
queryString += String.Join("&", paramKeys.Select(a => a + "=" + HttpUtility.UrlEncode(parametersToEncrypt[a])));
// Encrypt data.
String encrypted = SacsEncryptionHelper.Encrypt(queryString, base64IV, base64Key);
//if (SacsSystem.DebugMode) SaLogger.Debug(fn + "\n\tData: " + queryString + " \n\tEncrypted: " + encrypted);
return encrypted;
}
catch (Exception exc) { SaLogger.Warn(fn + " " + ExceptionUtil.GetErrorStateMessage(exc)); }
return "";
}
public static NameValueCollection DecryptParameters(String encrypted, String base64IV, String base64Key)
{
String fn = ClassName + ".DecryptParameters";
try
{
String queryString = SacsEncryptionHelper.Decrypt(encrypted, base64IV, base64Key);
//if (SacsSystem.DebugMode) SaLogger.Debug(fn + " \n\tEncrypted: " + encrypted + "\n\tData: " + queryString);
return HttpUtility.ParseQueryString(queryString);
}
catch (Exception exc) { SaLogger.Warn(fn + " " + ExceptionUtil.GetErrorStateMessage(exc)); }
return new NameValueCollection();
}
//-------------------------------------------------------------------
// Cookies.
//-------------------------------------------------------------------
/// <summary>Retrieve the value from the named request cookie.</summary>
public String GetRequestCookie(String key)
{
if (Request.Cookies.ContainsKey(key))
return Request.Cookies[key];
return null;
}
/// <summary>Set the value against the named response cookie.</summary>
public void SetResponseCookie(string key, string value, DateTime? expiresAt = null, bool isEssential = false)
{
CookieOptions options = new CookieOptions();
options.HttpOnly = true;
options.Secure = true;
options.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
if (expiresAt.HasValue)
options.Expires = expiresAt;
if (isEssential)
options.IsEssential = true;
Response.Cookies.Append(key, value, options);
}
//-------------------------------------------------------------------
// Ajax actions.
//-------------------------------------------------------------------
protected static readonly Encoding ajaxResponseEncoding = Encoding.UTF8;
protected IActionResult GetAjaxResult_GoHome(AjaxResponse_Base responseData)
{
responseData.SetGoHome();
return Json(responseData);
}
protected IActionResult GetAjaxResult_InvalidSession(AjaxResponse_Base responseData)
{
responseData.SetGoHome();
return Json(responseData);
}
//-------------------------------------------------------------------
// Response data.
// Null values are ignored by JSON serialisation.
// - See Saxonite.Net.Core.Web.Base.App.SaxStartup.ConfigureServices()
// - Ref. config.services.AddMvc(...).AddJsonOptions(options => { options.JsonSerializerOptions.IgnoreNullValues = true; });
//-------------------------------------------------------------------
// Base response data - for simple responses.
public class AjaxResponse_Base
{
// Response status.
public bool? Success { get; set; } = null;
public bool? Error { get; set; } = null;
public bool? SessionExpired { get; protected set; } = null;
public bool? GoHome { get; protected set; } = null;
public AjaxResponse_Base SetSessionExpired(String title, String text)
{
SessionExpired = true;
return SetMessage(title, text);
}
public AjaxResponse_Base SetGoHome()
{
GoHome = true;
return this;
}
public AjaxResponse_Base SetGoHome(String title, String text)
{
GoHome = true;
return SetMessage(title, text);
}
// Message - displays alert popup.
public class MessageData
{
public String Title { get; set; }
public String Text { get; set; }
}
public MessageData Message { get; private set; }
public AjaxResponse_Base SetMessage(String title, String text)
{
if (!StringUtil.IsEmpty(text))
Message = new MessageData() { Title = title, Text = text };
return this;
}
}
//-------------------------------------------------------------------
public class AjaxResponse_Data : AjaxResponse_Base
{
//---------------------------------------------------------------
// Construction.
protected AjaxResponse_Data(SaModel model)
{
}
//---------------------------------------------------------------
/// <summary>Item data - Common properties for data items.</summary>
public class ItemData_Base
{
// Common item properties.
public String Id { get; set; }
public String Notes { get; set; }
public String StatusCode { get; set; }
public String StatusDesc { get; set; }
// Flags for common status values.
// Return true or null so that property is not serialised when false.
public bool? Awaiting { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Awaiting)) ? true : (bool?)null; } }
public bool? Active { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Active)) ? true : (bool?)null; } }
public bool? Inactive { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Inactive)) ? true : (bool?)null; } }
public bool? Deleted { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Deleted)) ? true : (bool?)null; } }
/// <summary>Created on date - YYYYMMDD</summary>
public String CreatedOn { get; set; }
/// <summary>Created on date - formatted per locale</summary>
public String CreatedOnFmt { get; set; }
/// <summary>Updated on date - YYYYMMDD</summary>
public String UpdatedOn { get; set; }
/// <summary>Updated on date - formatted per locale</summary>
public String UpdatedOnFmt { get; set; }
}
/// <summary>Contact Method properties.</summary>
public class ContactMethodData
{
public String Method { get; set; }
public String MethodCaption { get; set; }
public String Value { get; set; }
}
/// <summary>Address properties.</summary>
public class AddressData : ItemData_Base
{
public String Line1 { get; set; }
public String Line2 { get; set; }
public String Line3 { get; set; }
public String Town { get; set; }
public String RegionIdProvince { get; set; }
public String RegionNameProvince { get; set; }
public String RegionIdCountry { get; set; }
public String RegionNameCountry { get; set; }
public String PostalCode { get; set; }
}
/// <summary>
/// Item data with label and value.
/// </summary>
public class ItemLabelValue
{
public String Label { get; set; }
public String Value { get; set; }
}
/// <summary>
/// Item data with just Id and name.
/// <para>- Id is String to allow for obfuscated values.</para>
/// </summary>
public class ItemIdName
{
public String Id { get; set; }
public String Name { get; set; }
}
/// <summary>Item code and desc values.</summary>
public class ItemCodeDesc
{
public String Code { get; set; }
public String Desc { get; set; }
}
//---------------------------------------------------------------
// Form data.
public class FormControlValue
{
public FormControlValue(String controlName, String value, String error)
{
ControlName = controlName;
Value = value;
Error = error;
}
public String ControlName { get; }
public String Value { get; set; }
public String Error { get; set; }
}
private readonly Dictionary<String, FormControlValue> controlValues = new Dictionary<String, FormControlValue>();
public List<FormControlValue> ControlValues { get { return controlValues.Count > 0 ? controlValues.Values.ToList() : null; } }
public void SetControlValue(String controlName, String value, String error = null)
{
if (controlValues.ContainsKey(controlName))
{
// Modify existing entry in the list.
FormControlValue controlValue = controlValues[controlName];
if (value != null) controlValue.Value = value;
if (error != null) controlValue.Error = error;
}
else
{
// Add new control to list.
controlValues.Add(controlName, new FormControlValue(controlName, value, error));
}
}
/// <summary>Assign the error to the control by name and return this response data.</summary>
public AjaxResponse_Data SetControlError(String controlName, String error)
{
return SetControlError(controlName, error, this);
}
/// <summary>
/// Assign the error to the control by name and return the specified value.
/// <para>- Allows alternative return values to be specified (eg. 'false') to avoid separate return statement in calling routine.</para>
/// </summary>
public T SetControlError<T>(String controlName, String error, T returnValue)
{
SetControlValue(controlName, null, error);
return returnValue;
}
public bool HasControlErrors()
{
foreach (FormControlValue controlValue in controlValues.Values)
{
if (!StringUtil.IsEmpty(controlValue.Error)) return true;
}
return false;
}
//---------------------------------------------------------------
// Notification - briefly displays after response received.
public class NotificationData
{
public String Title { get; set; }
public String Message { get; set; }
}
public NotificationData Notification { get; private set; }
public AjaxResponse_Data SetNotification(String title, String message)
{
Notification = new NotificationData() { Title = title, Message = message };
return this;
}
//---------------------------------------------------------------
// Paged data.
public class PagingData
{
public PagingData(SaModel model) :
this(model.Paging.PageNo, model.Paging.PageLength, model.Paging.ItemCount, model.Paging.ItemTotal)
{ }
public PagingData(uint pageNo, ushort pageLength, ushort itemCount, ulong itemTotal)
{
PageNo = pageNo;
PageLength = pageLength;
ItemCount = itemCount;
ItemTotal = itemTotal;
}
public bool Paged { get { return (PageNo > 0 && PageLength > 0 && ItemTotal > PageLength); } }
public uint PageNo { get; } = 0;
public ushort PageLength { get; } = 0;
public uint PageCount { get { return (PageLength > 0 ? (uint)Math.Ceiling((decimal)ItemTotal / PageLength) : 0); } }
public ushort ItemCount { get; } = 0;
public ulong ItemTotal { get; } = 0;
}
public PagingData Paging { get; private set; }
public void SetPaging(SaModel model)
{
Paging = new PagingData(model);
}
//---------------------------------------------------------------
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+150
View File
@@ -0,0 +1,150 @@
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Sarand.Net.Standard.Base.Bus.SaErrorStateItem;
namespace Sacs.Web.Components
{
//=======================================================================
public class SaModel
{
//-------------------------------------------------------------------
// Data members.
private readonly List<String> infoMessages = new List<string>();
private readonly List<String> warningMessages = new List<string>();
private readonly List<String> errorMessages = new List<string>();
//-------------------------------------------------------------------
/// <summary>
/// Protected construction - use derived objects.
/// </summary>
protected SaModel()
{
}
//---------------------------------------------------------------
// Properties.
public bool DebugMode { get; } = SacsSystem.DebugMode;
public bool OK { get { return !HasMessages(); } }
public virtual bool ProhibitCache { get; set; } = true;
public String PageCaption { get; set; }
public bool IncludeGoogleAnalytics { get; set; } = false;
//-------------------------------------------------------------------
// Page configuration.
// Views can be registered to avoid multiple inclusions in a page.
private HashSet<String> registeredViews = new HashSet<String>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Views can be registered to avoid multiple inclusions in a page.
/// <para>Only generate view content if this method returns true.</para>
/// </summary>
public bool RegisterView(String viewName)
{
#if DEBUG
if (registeredViews.Contains(viewName))
SaLogger.Warn(this.GetType().Name + ".RegisterView View already registered: " + viewName);
#endif
return registeredViews.Add(viewName);
}
//-------------------------------------------------------------------
// Version parameter for includes.
public String HrefVersion
{
get
{
DateTime versionDate = SacsSystem.AssemblyBuildAt;
if (!DateUtil.IsValid(versionDate) || SacsSystem.DebugMode)
versionDate = DateTime.Now;
return DateUtil.Format(versionDate, "yyyyMMdd" /*+ (SacsSystem.Debug ? "HHmm" : "")*/);
}
}
// Additional includes.
public HashSet<String> AdditionalFiles_CSS { get; set; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
public HashSet<String> AdditionalFiles_JS { get; set; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
//-------------------------------------------------------------------
// Redirection locations.
public class RedirectLocationData
{
// Properties populated in Controller_Base.OnActionExecuted.
public String Base { get; set; }
public String Home { get; set; }
public String Home_SessionExpired { get; set; }
}
public RedirectLocationData RedirectLocation { get; } = new RedirectLocationData();
//-------------------------------------------------------------------
// Paging properties.
public class DataPaging
{
private DbQuery.Paging paging { get; } = new DbQuery.Paging();
public void PopulatePagingTo(DbQuery.Paging resultsPaging)
{
resultsPaging.PageLength = paging.PageLength;
resultsPaging.PageNo = paging.PageNo;
}
public bool IsPaged { get { return paging.Paged; } }
public uint PageNo { get { return paging.PageNo; } set { paging.PageNo = value; } }
public String PageNoValue { get { return paging.PageNo.ToString(); } set { paging.PageNo = (uint)StringUtil.ToInt(value); } }
public ushort PageLength { get { return paging.PageLength; } set { paging.PageLength = value; } }
public String PageLengthValue { get { return paging.PageLength.ToString(); } set { paging.PageLength = (ushort)StringUtil.ToShort(value); } }
public uint PageCount { get { return (PageLength > 0 ? (uint)Math.Ceiling((decimal)ItemTotal / paging.PageLength) : 0); } }
public ushort ItemCount { get; set; }
private ulong itemTotal = 0;
public ulong ItemTotal
{
get { return itemTotal; }
set
{
itemTotal = value;
// Check whether the total number of items is less than the current page and reduce page no if needed.
if (IsPaged && (PageNo - 1) * PageLength > itemTotal)
PageNo = (uint)((itemTotal - 1) / PageLength + 1);
}
}
}
public DataPaging Paging { get; } = new DataPaging();
public void PopulatePagingTo(DbQuery.Paging resultsPaging)
{
Paging.PopulatePagingTo(resultsPaging);
}
//-------------------------------------------------------------------
// Messages.
//-------------------------------------------------------------------
public bool HasMessages()
{
return infoMessages.Count > 0 || warningMessages.Count > 0 || errorMessages.Count > 0;
}
public List<String> InfoMessages { get { return new List<string>(infoMessages); } }
public List<String> WarningMessages { get { return new List<string>(warningMessages); } }
public List<String> ErrorMessages { get { return new List<string>(errorMessages); } }
//-------------------------------------------------------------------
// Form messages.
public void AddInfoMessage(String message)
{
if (StringUtil.IsEmpty(StringUtil.TrimAll(message))) return;
infoMessages.Add(message);
}
public void AddWarningMessage(String message)
{
if (StringUtil.IsEmpty(StringUtil.TrimAll(message))) return;
warningMessages.Add(message);
}
public void AddErrorMessage(String message)
{
if (StringUtil.IsEmpty(StringUtil.TrimAll(message))) return;
errorMessages.Add(message);
}
//---------------------------------------------------------------
}
//=======================================================================
}
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Sarand.Net.Standard</RootNamespace>
</PropertyGroup>
+3 -2
View File
@@ -98,7 +98,9 @@ namespace Sarand.Net.Standard.Base.Core
public String DateTimeSeparator { get; set; } = " ";
public String TimeFormat { get; set; } = "HH:mm";
public DataValue.DataType DataType { get; set; } = DataValue.DataType.String;
public bool IsNumeric { get { return DataType == DataValue.DataType.Long || DataType == DataValue.DataType.Double || DataType == DataValue.DataType.Decimal; } }
public bool Resizable { get; set; } = true;
public bool WrapText { get; set; } = false;
public bool Hidden { get; set; } = false;
public bool Frozen { get; set; } = false;
// Drop-down configuration.
@@ -107,12 +109,11 @@ namespace Sarand.Net.Standard.Base.Core
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:
// 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>
+53
View File
@@ -172,6 +172,7 @@ namespace Sarand.Net.Standard.Base.Core
}
return 0.0;
}
/// <summary>Return value interpretted as boolean.</summary>
public bool ToBool ()
{
if (value == null) return false;
@@ -190,6 +191,7 @@ namespace Sarand.Net.Standard.Base.Core
}
return false;
}
/// <summary>Return explicit boolean value or false for other types.</summary>
public bool AsBool ()
{
// Convert to specific type of data - fails if type is not correct:
@@ -369,10 +371,58 @@ namespace Sarand.Net.Standard.Base.Core
}
//=======================================================================
// Data Value list extension functions.
public static class DataValueList
{
//-------------------------------------------------------------------
// Population.
/// <summary>Add a String value to the list.</summary>
public static void Add(this List<DataValue> values, String value)
{
values.Add(new DataValue(value));
}
/// <summary>Add a boolean value to the list.</summary>
public static void Add(this List<DataValue> values, bool value)
{
values.Add(new DataValue(value));
}
/// <summary>Add an integer value to the list.</summary>
public static void Add(this List<DataValue> values, int value)
{
values.Add(new DataValue(value));
}
/// <summary>Add a long value to the list.</summary>
public static void Add(this List<DataValue> values, long value)
{
values.Add(new DataValue(value));
}
/// <summary>Add a double value to the list.</summary>
public static void Add(this List<DataValue> values, double value)
{
values.Add(new DataValue(value));
}
/// <summary>Add a decimal value to the list.</summary>
public static void Add(this List<DataValue> values, decimal value)
{
values.Add(new DataValue(value));
}
/// <summary>Add a date time value to the list.</summary>
public static void Add(this List<DataValue> values, DateTime value)
{
values.Add(new DataValue(value));
}
//-------------------------------------------------------------------
// Conversion.
/// <summary>Convert all data values to strings.</summary>
public static List<String> ToStringList (this List<DataValue> values)
{
@@ -381,6 +431,7 @@ namespace Sarand.Net.Standard.Base.Core
stringValues.Add(values.ToString());
return stringValues;
}
/// <summary>
/// Convert the data values to raw objects.
/// <para>eg. For population to Excel spreadsheet cells.</para>
@@ -409,6 +460,7 @@ namespace Sarand.Net.Standard.Base.Core
//-------------------------------------------------------------------
// Numeric - only with compatible numeric values.
/// <summary>Minimum numeric value in the list.</summary>
public static DataValue GetMinValue (this List<DataValue> values)
{
@@ -425,6 +477,7 @@ namespace Sarand.Net.Standard.Base.Core
}
return minValue;
}
/// <summary>Maximum numeric value in the list.</summary>
public static DataValue GetMaxValue (this List<DataValue> values)
{
+1 -1
View File
@@ -19,7 +19,7 @@ namespace Sarand.Net.Standard.Base.Core
// Disposal.
~SacsDisposable ()
{
Dispose(false);
Dispose(!disposed);
}
public void Dispose ()
{
@@ -38,8 +38,20 @@ namespace Sarand.Net.Standard.Base.Helpers
public new String PreferredContentType { set { base.PreferredContentType = value; } }
}
//-------------------------------------------------------------------
// Definitions.
/// <summary>Excel legacy - xls.</summary>
public static readonly String ContentType_Excel_Legacy = "application/vnd.ms-excel";
/// <summary>Excel open - xlsx.</summary>
public static readonly String ContentType_Excel_Open = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
/// <summary>Excel macro enabled - xlsm.</summary>
public static readonly String ContentType_Excel_MacroEnabled = "application/vnd.ms-excel.sheet.macroEnabled.12";
/// <summary>Excel binary macro enabled - xlsb.</summary>
public static readonly String ContentType_Excel_BinaryMacroEnabled = "application/vnd.ms-excel.sheet.binary.macroEnabled.12";
//-------------------------------------------------------------------
// Data members.
static String ContentType_Generic = "application/octet-stream";
private static readonly List<ContentTypeData> contentTypes;
private static FileExtensionContentTypeProvider provider = null;
private static readonly HashSet<Sacs.MimeType> allowedMimeTypes_CV = new HashSet<Sacs.MimeType>()
@@ -87,8 +99,8 @@ namespace Sarand.Net.Standard.Base.Helpers
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("xls", "Microsoft Excel", ContentType_Excel_Legacy, Sacs.MimeType.MsXls),
new ContentTypeData_Internal("xlsx", "Microsoft Excel (OpenXML)", ContentType_Excel_Open, 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"),
@@ -156,7 +168,7 @@ namespace Sarand.Net.Standard.Base.Helpers
//-------------------------------------------------------------------
// Properties.
/// <summary>Generic content type used for 'unknown' document types.</summary>
public static String ContentTypeDefault { get; } = "application/octet-stream";
public static String ContentTypeDefault { get; } = ContentType_Generic;
/// <summary>Selection of comon content types.</summary>
public static List<ContentTypeData> ContentTypes { get { return new List<ContentTypeData>(contentTypes); } }
public static FileExtensionContentTypeProvider Provider
@@ -223,6 +235,20 @@ namespace Sarand.Net.Standard.Base.Helpers
}
//----------------------------------------------------------------------
/// <summary>Convert the enumerated Mime to content type.</summary>
public static String GetContentTypeFromMimeType(Sacs.MimeType mime)
{
if (mime == Sacs.MimeType.Unknown) return ContentTypeDefault;
ContentTypeData contentTypeData = contentTypes.Find(item => item.Mime.Equals(mime));
if (contentTypeData != null)
return contentTypeData.ContentType;
return ContentTypeDefault;
}
//----------------------------------------------------------------------
// Mime type.
//----------------------------------------------------------------------
/// <summary>Convert the content type to enumerated Mime.</summary>
public static Sacs.MimeType GetMimeTypeFromContentType(this String contentType)
{
+342 -58
View File
@@ -1,29 +1,37 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging.Abstractions;
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 static Sarand.Net.Standard.Base.Procs.SaProcess_Base;
namespace Sarand.Net.Standard.Base.Procs
{
//=======================================================================
public interface ISacsProcessNotifier
{
void OnStartProcess ();
void SetProgressInfo (List<String> progressInfo);
void OnEndProcess ();
}
//=======================================================================
public abstract class SaProcess_Base : SacsDisposable
{
//-------------------------------------------------------------------
// Exceptions.
[Serializable]
public class InProgressException : Exception
{
public InProgressException() { }
public InProgressException(string message) : base(message) { }
public InProgressException(string message, Exception inner) : base(message, inner) { }
}
//-------------------------------------------------------------------
// Process options and groups - for configuration of process to run.
// See Sarand.Win.Forms.Utility.SaForm_SelectOptions.
public class OptionData
{
public OptionData (TypeCodeDesc option)
public OptionData(TypeCodeDesc option)
{
Option = option;
}
@@ -33,7 +41,7 @@ namespace Sarand.Net.Standard.Base.Procs
}
public class GroupData
{
public GroupData (TypeCodeDesc optionGroup)
public GroupData(TypeCodeDesc optionGroup)
{
OptionGroup = optionGroup;
}
@@ -55,13 +63,13 @@ namespace Sarand.Net.Standard.Base.Procs
}
public class Config
{
protected Config ()
protected Config()
{
}
/// <summary>Identifies the type of process.</summary>
public ProcessType ProcessType { get; set; } = ProcessType.Unknown;
/// <summary>Is the process to be run in 'Test' mode.</summary>
public bool TestProcess{ get; set; } = SacsSystem.DebugMode;
public bool TestProcess { get; set; } = SacsSystem.DebugMode;
/// <summary>Process imports data.</summary>
public bool Import { get { return ProcessType == ProcessType.ImportData || ProcessType == ProcessType.ImportExportData; } }
/// <summary>Process exports data.</summary>
@@ -74,28 +82,131 @@ namespace Sarand.Net.Standard.Base.Procs
public bool Automated { get; set; } = false;
}
//-------------------------------------------------------------------
// Process control definitions.
public class ProcessMessage
{
public ProcessMessage(String message, SaMessageLevel level = SaMessageLevel.Info)
{
this.Message = message;
this.Level = level;
}
public String Message { get; }
public SaMessageLevel Level { get; }
public int Instances { get; internal set; }
}
private class ProcessControlData
{
// Interrupt.
private class InterruptData
{
public readonly Object lockable = new object();
private bool interrupted = false;
public bool Interrupted { get { lock (lockable) { return interrupted; } } set { lock (lockable) { interrupted = value; } } }
}
private InterruptData interrupt { get; } = new InterruptData();
public bool Interrupted { get { return interrupt.Interrupted; } set { interrupt.Interrupted = value; } }
// Process messages.
private class ProcessMessagesData
{
public readonly Object lockable = new object();
public List<ProcessMessage> messages { get; } = new List<ProcessMessage>();
}
private ProcessMessagesData processMessages { get; } = new ProcessMessagesData();
/// <summary>Convert the message levele to one of the 3 values used with Process Messages: Info, Warning, Error.</summary>
public static SaMessageLevel GetLevel(SaMessageLevel level)
{
if (level == SaMessageLevel.Error || level == SaMessageLevel.Fatal || level == SaMessageLevel.Exception)
level = SaMessageLevel.Error;
else if (level == SaMessageLevel.Warning)
level = SaMessageLevel.Warning;
else
level = SaMessageLevel.Info;
return level;
}
/// <summary>Add a message of the specified level (Info, Warning, Error).</summary>
public bool AddProcessMessage(String message, SaMessageLevel level)
{
message = StringUtil.TrimWhitespace(message);
if (StringUtil.IsBlank(message)) return false;
level = GetLevel(level);
lock (processMessages.lockable)
{
ProcessMessage procMsg = processMessages.messages.Find(item => item.Message.EqualsCI(message) && item.Level == level);
// Duplicate messages increment the 'Instances' value.
if (procMsg != null)
procMsg.Instances++;
// Otherwise add a new message to the list.
else processMessages.messages.Add(new ProcessMessage(message, level));
}
return true;
}
/// <summary>Are there any message of the specified level (Info, Warning, Error).</summary>
public bool HasProcessMessages(SaMessageLevel level)
{
lock (processMessages.lockable)
return processMessages.messages.Exists(item => item.Level == GetLevel(level));
}
/// <summary>Get the first message of the specified level (Info, Warning, Error).</summary>
public String GetProcessMessage(SaMessageLevel level)
{
lock (processMessages.lockable)
return processMessages.messages.Find(item => item.Level == GetLevel(level))?.Message ?? "";
}
/// <summary>Return all process messages.</summary>
public List<ProcessMessage> GetProcessMessages()
{
List<ProcessMessage> messages = new List<ProcessMessage>();
lock (processMessages.lockable)
messages.AddRange(processMessages.messages);
return messages;
}
/// <summary>Return all process messages of the specified Level (Info, Warning, Error).</summary>
public List<ProcessMessage> GetProcessMessages(SaMessageLevel level)
{
List<ProcessMessage> messages = new List<ProcessMessage>();
lock (processMessages.lockable)
messages.AddRange(processMessages.messages.FindAll(item => item.Level == GetLevel(level)));
return messages;
}
/// <summary>Clear process messages.</summary>
public void ClearProcessMessages()
{
lock (processMessages.lockable)
processMessages.messages.Clear();
}
// Task control data.
private SacsTaskData taskData = null;
public SacsTaskData TaskData { get { return taskData; } set { if (taskData != null) taskData.Dispose(); taskData = value; } }
}
//-------------------------------------------------------------------
// Data members.
private Config config;
private String processError = "";
private ProcessControlData Control { get; } = new ProcessControlData();
//-------------------------------------------------------------------
// Construction.
protected SaProcess_Base (Config config)
protected SaProcess_Base(Config config)
{
this.config = config;
}
protected override void Dispose (bool disposing)
protected override void Dispose(bool disposing)
{
if (disposing)
{
// Clear process data to dispose.
Control.TaskData = null;
}
}
//-------------------------------------------------------------------
// Properties.
public bool DebugMode { get { return SacsSystem.DebugMode; } }
public ISacsProcessNotifier ProcessNotifier { get; set; } = null;
public bool Processing { get; protected set; } = false;
public bool ProcessEnded { get; protected set; } = false;
public DateTime StartedAt { get; private set; } = DateTime.MinValue;
@@ -120,56 +231,195 @@ namespace Sarand.Net.Standard.Base.Procs
public virtual List<StringPair> ProcessingStatistics { get { return new List<StringPair>(); } }
//-------------------------------------------------------------------
// Run process inline.
public bool RunProcess ()
// Run process asynchronously.
private class AsyncProcessData : SacsDisposable
{
String fn = GetType().Name+".RunProcess";
if (ProcessNotifier == null)
throw new NotSupportedException("Process Notifier must be specified");
public AsyncProcessData(Task task)
{
this.Task = task;
}
protected override void Dispose(bool disposing)
{
if (disposing) { }
}
public Task Task { get; }
}
private AsyncProcessData AsyncData { get; set; }
public bool IsAsync { get { return AsyncData != null; } }
private void ClearAsyncData()
{
if (AsyncData != null)
{
lock (AsyncData)
{
AsyncData.Dispose();
AsyncData = null;
}
}
}
/// <summary>
/// Run the process asynchronously.
/// </summary>
/// <returns></returns>
/// <exception cref="InProgressException">If process is already running.</exception>
public async void RunAsync()
{
if (AsyncData != null)
throw new InProgressException();
Control.TaskData = new SacsTaskData();
// Run on separate thread.
Task task = new Task(() =>
{
try
{
// Execute the process.
RunProcess();
}
catch (OperationCanceledException exc)
{
SaLogger.Warn(exc.GetType().Name + " during asynchronous processing on " + GetType().Name);
}
finally
{
// Clear processing data.
ClearAsyncData();
}
}, Control.TaskData.CancellationToken);
AsyncData = new AsyncProcessData(task);
task.Start();
await task;
}
//-------------------------------------------------------------------
// Run process inline.
public bool RunProcess()
{
String fn = GetType().Name + ".RunProcess";
// Prepare process.
bool runProcess = false;
Reset();
if (Control.TaskData == null)
Control.TaskData = new SacsTaskData();
Processing = true;
StartedAt = DateTime.Now;
// Process starting.
try
{
// Notify start of process and log/ignore exceptions.
ProcessNotifier.OnStartProcess();
InvokeProcessStarting();
}
catch (Exception exc)
{
SaLogger.Error(fn + " OnStartProcess: " + GetProcessError() + exc.GetStackEntries(6).Concat("", "\n\tat "), true);
String msg = exc.GetType().Name + ": " + exc.Message;
AddErrorMessage(msg);
SaLogger.Error(fn + " ProcessStarting: " + msg + exc.GetStackEntries(6).Concat("", "\n\tat "), true);
}
// Process running.
try
{
// Run process.
OnProcessStarting();
runProcess = Run();
OnProcessEnding();
}
catch (Exception exc)
{
SetProcessError(exc.GetType().Name + ": " + exc.Message);
SaLogger.Error(fn + " " + GetProcessError() + exc.GetStackEntries(6).Concat("", "\n\tat "), true);
String msg = exc.GetType().Name + ": " + exc.Message;
AddErrorMessage(msg);
SaLogger.Error(fn + " " + msg + exc.GetStackEntries(6).Concat("", "\n\tat "), true);
}
// Processing completed.
FinishedAt = DateTime.Now;
Processing = false;
ProcessEnded = true;
finally
{
// Processing completed.
FinishedAt = DateTime.Now;
Processing = false;
ProcessEnded = true;
Control.TaskData = null;
}
// Process completed.
try
{
// Notify end of process and log/ignore exceptions.
ProcessNotifier.OnEndProcess();
// Notify completion of process and log/ignore exceptions.
InvokeProcessCompleted();
}
catch (Exception exc)
{
SaLogger.Error(fn + " OnEndProcess: " + GetProcessError() + exc.GetStackEntries(6).Concat("", "\n\tat "), true);
String msg = exc.GetType().Name + ": " + exc.Message;
AddErrorMessage(msg);
SaLogger.Error(fn + " ProcessCompleted: " + msg + exc.GetStackEntries(6).Concat("", "\n\tat "), true);
}
return runProcess;
}
protected abstract bool Run ();
protected virtual void OnProcessStarting () { }
protected virtual void OnProcessEnding () { }
protected abstract bool Run();
//-------------------------------------------------------------------
public async Task StopProcessAsync(int timeout = 50)
{
if (!Processing) return;
if (AsyncData != null)
{
try
{
// Cancel the task.
Control.TaskData.TriggerCancellation();
// Wait for task termination or timeout.
if (await Task.WhenAny(AsyncData.Task, Task.Delay(Math.Max(20, Math.Min(1000, timeout)))) != AsyncData.Task)
{
// Timeout completed before task - force termination.
Control.TaskData.ThrowIfCancellationRequested();
}
}
finally
{
Control.TaskData = null;
ClearAsyncData();
}
}
}
//-------------------------------------------------------------------
// Processing events.
// Processing Starting.
public class ProcessStartingEventArgs : EventArgs { }
public event EventHandler<ProcessStartingEventArgs> OnProcessStarting;
protected virtual void InvokeProcessStarting()
{
if (OnProcessStarting != null)
{
ProcessStartingEventArgs args = new ProcessStartingEventArgs();
OnProcessStarting(this, args);
}
}
// Process Info Updated.
public class ProgressInfoUpdatedEventArgs : EventArgs
{
public List<String> ProgressInfo { get; } = new List<string>();
}
public event EventHandler<ProgressInfoUpdatedEventArgs> OnProgressInfoUpdated;
protected virtual void InvokeProgressInfoUpdated(List<String> progressInfo)
{
if (OnProgressInfoUpdated != null)
{
ProgressInfoUpdatedEventArgs args = new ProgressInfoUpdatedEventArgs();
args.ProgressInfo.AddRange(progressInfo);
OnProgressInfoUpdated(this, args);
}
}
// Processing Completed.
public class ProcessCompletedEventArgs : EventArgs { }
public event EventHandler<ProcessCompletedEventArgs> OnProcessCompleted;
protected virtual void InvokeProcessCompleted()
{
if (OnProcessCompleted != null)
{
ProcessCompletedEventArgs args = new ProcessCompletedEventArgs();
OnProcessCompleted(this, args);
}
}
//-------------------------------------------------------------------
// Process control.
@@ -177,15 +427,15 @@ namespace Sarand.Net.Standard.Base.Procs
{
get
{
if (HasProcessError()) return false;
if (isInterrupted) return false;
if (HasErrorMessage()) return false;
if (Control.Interrupted) return false;
return true;
}
}
private void Reset ()
private void Reset()
{
ClearProcessError();
isInterrupted = false;
Control.ClearProcessMessages();
Control.Interrupted = false;
ProcessEnded = false;
StartedAt = DateTime.MinValue;
FinishedAt = DateTime.MinValue;
@@ -193,36 +443,70 @@ namespace Sarand.Net.Standard.Base.Procs
//-------------------------------------------------------------------
// Progress information.
protected void SetProgressInfo (String progressInfo)
protected void SetProgressInfo(String progressInfo)
{
SetProgressInfo(StringListUtil.CreateList(progressInfo));
}
protected void SetProgressInfo (List<String> progressInfo)
protected void SetProgressInfo(List<String> progressInfo)
{
if (ProcessNotifier != null)
ProcessNotifier.SetProgressInfo(progressInfo);
InvokeProgressInfoUpdated(progressInfo);
}
//-------------------------------------------------------------------
// Error handling.
public bool HasProcessError () { return !StringUtil.IsEmpty(processError); }
public String GetProcessError () { return StringUtil.EmptyOnNull(processError); }
public void ClearProcessError () { processError = ""; }
/// <summary>Record the processing error - always returns false.</summary>
public bool SetProcessError (String error)
// Process messages.
/// <summary>Add a message of the specified level (Info, Warning, Error).</summary>
protected void AddMessage(String message, SaMessageLevel level)
{
processError = StringUtil.EmptyOnNull(error);
if (Control.AddProcessMessage(message, level))
SaLogger.Log(GetType().Name + " " + message, ProcessControlData.GetLevel(level));
}
protected void AddInfoMessage(String message)
{
AddMessage(message, SaMessageLevel.Info);
}
protected void AddWarningMessage(String message)
{
AddMessage(message, SaMessageLevel.Warning);
}
/// <summary>Add the error message and always return false.</summary>
protected bool AddErrorMessage(String message)
{
AddMessage(message, SaMessageLevel.Error);
return false;
}
/// <summary>Are there any error messages.</summary>
public bool HasErrorMessage()
{
return Control.HasProcessMessages(SaMessageLevel.Error);
}
/// <summary>Get the first error message.</summary>
public String GetErrorMessage()
{
return Control.GetProcessMessage(SaMessageLevel.Error);
}
/// <summary>Return all process messages.</summary>
public List<ProcessMessage> GetProcessMessages()
{
return Control.GetProcessMessages();
}
/// <summary>Return all process messages of the specified Level (Info, Warning, Error).</summary>
public List<ProcessMessage> GetProcessMessages(SaMessageLevel level)
{
return Control.GetProcessMessages(level);
}
/// <summary>Clear process messages.</summary>
public void ClearProcessMessages()
{
Control.ClearProcessMessages();
}
//-------------------------------------------------------------------
// Interruption.
private bool isInterrupted = false;
public bool IsInterrupted
{
get { return isInterrupted; }
get { return Control.Interrupted; }
}
public bool Interrupt ()
public bool Interrupt()
{
if (!config.AllowInterruption)
{
@@ -230,10 +514,10 @@ namespace Sarand.Net.Standard.Base.Procs
return false;
}
if (config.ConfirmInterruption && !IsInterruptionConfirmed()) return false;
isInterrupted = true;
Control.Interrupted = true;
return true;
}
protected virtual bool IsInterruptionConfirmed ()
protected virtual bool IsInterruptionConfirmed()
{
return true;
}
+7 -3
View File
@@ -17,7 +17,7 @@ namespace Sarand.Net.Standard.Base.Tasks
//-------------------------------------------------------------------
// Construction.
public SacsTaskData (CancellationToken? cancellationToken)
public SacsTaskData (CancellationToken? cancellationToken = null)
{
if (cancellationToken != null)
{
@@ -49,18 +49,22 @@ namespace Sarand.Net.Standard.Base.Tasks
// Properties.
public bool Debug { get; set; }
/// <summary>Token to allow the async request to be cancelled.</summary>
public CancellationToken CancellationToken { get; }
//-------------------------------------------------------------------
// C a n c e l l a t i o n
//-------------------------------------------------------------------
/// <summary>Token to allow the async request to be cancelled.</summary>
public CancellationToken CancellationToken { get; }
/// <summary>Gets whether cancellation has been requested.</summary>
public bool IsCancellationRequested { get { return CancellationToken.IsCancellationRequested; } }
/// <summary>Throws a System.OperationCanceledException if this token has had cancellation requested.</summary>
public void ThrowIfCancellationRequested()
{
CancellationToken.ThrowIfCancellationRequested();
}
/// <summary>Can cancellation be triggered here?</summary>
public bool CanBeCancelled { get { return cancellationTokenSource != null; } }
public bool TriggerCancellation ()
+3 -2
View File
@@ -79,7 +79,8 @@ namespace Sarand.Net.Standard.Base.Utils
/// </summary>
public static SixLabors.ImageSharp.Image LoadImage(byte[] imageData, out IImageFormat format)
{
var image = Image.Load<Rgba32>(imageData, out format);
var image = Image.Load<Rgba32>(imageData);
format = Image.DetectFormat(imageData);
return image;
}
@@ -90,7 +91,7 @@ namespace Sarand.Net.Standard.Base.Utils
{
using (MemoryStream ms = new MemoryStream())
{
var imageEncoder = image.GetConfiguration().ImageFormatsManager.FindEncoder(imageFormat);
var imageEncoder = image.Configuration.ImageFormatsManager.GetEncoder(imageFormat);
image.Save(ms, imageEncoder);
return ms.ToArray();
}
+13 -20
View File
@@ -1,4 +1,5 @@
using System;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
@@ -26,32 +27,24 @@ namespace Sarand.Net.Standard.Base.Utils
if (Object.ReferenceEquals(source, null))
return default(T);
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
// initialize inner objects individually
// for example in default constructor some list property initialized with some values,
// but in 'source' these items are cleaned -
// without ObjectCreationHandling.Replace default constructor values will be added to result
var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
//-------------------------------------------------------------------
// Serialize.
public static String Serialize<T>(T source)
// Serialize using JSON.
public static String ToJson<T>(T source)
{
if (!typeof(T).IsSerializable) throw new ArgumentException("The type must be serializable.", "source");
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null)) return "";
// Serialise to String.
IFormatter formatter = new BinaryFormatter();
using (Stream stream = new MemoryStream())
using (StreamReader streamReader = new StreamReader(stream))
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return streamReader.ReadToEnd();
}
// Serialise to JSON String.
return JsonConvert.SerializeObject(source);
}
//-------------------------------------------------------------------
+14
View File
@@ -231,7 +231,20 @@ namespace Sarand.Net.Standard.Base.Utils
Regex regexMultipleInternal = new Regex("[" + Regex.Escape(new String(trimChars)) + "]{2,}", RegexOptions.None);
return regexMultipleInternal.Replace(value, replacement);
}
private static Regex regexWhitespace = new Regex("\\s+", RegexOptions.Compiled);
/// <summary>
/// Trim all leading and trailling whitespace and replace internal whitespace with a single space.
/// </summary>
public static String TrimWhitespace(this String value)
{
return regexWhitespace.Replace(value, " ").Trim();
}
private static Regex regexMultipleSpaces = new Regex("[ ]{2,}", RegexOptions.Compiled);
/// <summary>
/// Replace multiple spaces with a single space.
/// </summary>
public static String TrimInternalSpaces(this String value)
{
return regexMultipleSpaces.Replace(value, " ");
@@ -540,6 +553,7 @@ namespace Sarand.Net.Standard.Base.Utils
if (value.ToDouble() != 0.0) return true;
if (value.Left(1).EqualsCI("Y")) return true;
if (value.EqualsCI("True")) return true;
if (value.EqualsCI("On")) return true;
return false;
}
+74 -5
View File
@@ -1,13 +1,82 @@
namespace Sarand.Net.Standard.Base.Utils
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Sarand.Net.Standard.Base.Utils
{
//=======================================================================
public class SysUtil
public static class SysUtil
{
//-------------------------------------------------------------------
// Data members.
/// <summary>Get calling code's class name.</summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.NoInlining)]
public static string GetCallerClassName()
{
try
{
var stackFrame = new StackTrace().GetFrame(1);
var methodInfo = stackFrame.GetMethod();
return methodInfo.ReflectedType.Name.TrimWhitespace();
}
catch (Exception) { }
return "";
}
/// <summary>Get calling code's fucntion name.</summary>
[MethodImpl(MethodImplOptions.NoInlining)]
public static string GetCallerMemberName([CallerMemberName] string memberName = "")
{
return memberName;
}
/// <summary>Get calling code's file path.</summary>
[MethodImpl(MethodImplOptions.NoInlining)]
public static string GetCallerFilePath([CallerFilePath] string filePath = "")
{
return filePath;
}
/// <summary>Get calling code's line number.</summary>
[MethodImpl(MethodImplOptions.NoInlining)]
public static int GetCallerLineNumber([CallerLineNumber] int lineNumber = 0)
{
return lineNumber;
}
//-------------------------------------------------------------------
// Construction.
private SysUtil () { }
/// <summary>Shortcut to GetCallerClassMethod() - returns [class.method] for calling code.</summary>
public static string Fn { get { return GetCallerClassMethod(2); } }
/// <summary>Return identity of calling method as [class.method].</summary>
[MethodImpl(MethodImplOptions.NoInlining)]
public static string GetCallerClassMethod(int stackFrameIdx = 1)
{
try
{
var stackFrame = new StackTrace().GetFrame(stackFrameIdx);
var methodInfo = stackFrame.GetMethod();
return (methodInfo.ReflectedType.Name + "." + methodInfo.Name).TrimWhitespace().Trim('.');
}
catch (Exception) { }
return "";
}
//-------------------------------------------------------------------
/// <summary>Return identity of calling method as [class.method:line].</summary>
[MethodImpl(MethodImplOptions.NoInlining)]
public static string GetCallerClassMethodLine()
{
try
{
var stackFrame = new StackTrace().GetFrame(1);
var methodInfo = stackFrame.GetMethod();
return (methodInfo.ReflectedType.Name + "." + methodInfo.Name + ":" + stackFrame.GetFileLineNumber()).TrimWhitespace().Trim('.');
}
catch (Exception) { }
return "";
}
//-------------------------------------------------------------------
}
//=======================================================================
+4 -4
View File
@@ -1,17 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Sarand.Net.Standard</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CsvHelper" Version="19.0.0" />
<PackageReference Include="MailKit" Version="2.11.1" />
<PackageReference Include="MailKit" Version="2.15.0" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.2" />
</ItemGroup>
<ItemGroup>
@@ -17,7 +17,7 @@ namespace Sarand.Net.Standard.Dbas.Base
//-------------------------------------------------------------------
// Construction.
public SaDbDfnOdbcDriver (String name)
public SaDbDfnOdbcDriver(String name)
{
Name = name;
}
@@ -25,28 +25,30 @@ namespace Sarand.Net.Standard.Dbas.Base
//-------------------------------------------------------------------
// Properties.
public String Name { get; }
public Dictionary<String,String> Attributes { get; } = new Dictionary<String, String>();
public Dictionary<String, String> Attributes { get; } = new Dictionary<String, String>();
//-------------------------------------------------------------------
// Utilities.
/// <summary>Add 'Driver' attribute from registry. Fails if driver name not found.</summary>
public bool AddDriverConfig ()
public bool AddDriverConfig()
{
if (StringUtil.IsEmpty(Name)) return false;
try
{
#pragma warning disable CA1416
// Retrieve 'Driver' (location of DLL) parameter for this driver:
using (RegistryKey localMachineHive = Registry.LocalMachine)
using (RegistryKey odbcDriverKey = localMachineHive.OpenSubKey(@"SOFTWARE\ODBC\ODBCINST.INI\" + Name))
{
if (odbcDriverKey != null)
{
object driverName = odbcDriverKey.GetValue("Driver");
object driverName = odbcDriverKey.GetValue("Driver");
if (driverName != null && driverName is String)
Attributes["Driver"] = driverName.ToString();
}
return true;
}
#pragma warning restore CA1416
}
catch (Exception) { }
return true;
+37 -33
View File
@@ -22,7 +22,7 @@ namespace Sarand.Net.Standard.Dbas.Base
//-------------------------------------------------------------------
// Construction.
protected SacsDatabase (DbmsType dbms, String databaseName, String connectionString)
protected SacsDatabase(DbmsType dbms, String databaseName, String connectionString)
{
DbmsType = dbms;
DatabaseName = databaseName;
@@ -36,9 +36,9 @@ namespace Sarand.Net.Standard.Dbas.Base
public String DatabaseName { get; }
public SelectLockMode SelectLockMode { get; set; } = SelectLockMode.Default;
public bool DisplayErrors { get; set; } = true;
public abstract void OnConnectionChanged ();
public abstract DbConnection CreateConnection (bool forceOdbc = false);
public abstract DbDataAdapter CreateDataAdapter ();
public abstract void OnConnectionChanged();
public abstract DbConnection CreateConnection(bool forceOdbc = false);
public abstract DbDataAdapter CreateDataAdapter();
// Profile properties.
public static String ProfileSection_DbUtilities { get; } = "DbUtilities";
public static String ProfileValue_DbExportFile { get; } = "DbExportFile";
@@ -57,21 +57,21 @@ namespace Sarand.Net.Standard.Dbas.Base
protected String NextIdTable { get; set; } = "NextId";
protected String NextIdColumn_Object { get; set; } = "Object";
protected String NextIdColumn_NextId { get; set; } = "NextId";
public abstract SysId GetNextId (String objectName, DbUpdateInfo dbUpdateInfo, bool databaseTable = true, String idColumn = "Id");
public abstract SysId GetNextId(String objectName, DbUpdateInfo dbUpdateInfo, bool databaseTable = true, String idColumn = "Id");
public abstract int GetNextIdFromTable(string objectName, string idColumn = "Id");
public abstract bool SetMinNextId (String objectName, SysId id, DbUpdateInfo dbUpdateInfo, bool databaseTable = true, String idColumn = "Id", bool force = false);
public abstract bool SetMinNextId(String objectName, SysId id, DbUpdateInfo dbUpdateInfo, bool databaseTable = true, String idColumn = "Id", bool force = false);
//-------------------------------------------------------------------
// 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);
private static extern bool SQLGetInstalledDriversW(char[] lpszBuf, ushort cbufMax, out ushort pcbBufOut);
/// <summary>
/// Gets the ODBC driver names from the SQLGetInstalledDrivers function.
/// <para>Ref. https://stackoverflow.com/questions/6457973/odbc-driver-list-from-net</para>
/// </summary>
/// <returns>List of ODBC driver names, if successfull.</returns>
public static List<String> GetOdbcDriverNames ()
public static List<String> GetOdbcDriverNames()
{
try
{
@@ -93,13 +93,16 @@ namespace Sarand.Net.Standard.Dbas.Base
catch (Exception) { }
try
{
#pragma warning disable CA1416
// Alternatively, use the registry.
// Validate platform compatibility
using (RegistryKey localMachineHive = Registry.LocalMachine)
using (RegistryKey odbcDriversKey = localMachineHive.OpenSubKey(@"SOFTWARE\ODBC\ODBCINST.INI\ODBC Drivers"))
{
if (odbcDriversKey != null)
return new List<String>(odbcDriversKey.GetValueNames()).OrderBy(item => item).ToList();
}
// Validate platform compatibility
// 64-BitConverter machines.
using (RegistryKey localMachineHive = Registry.LocalMachine)
using (RegistryKey odbcDriversKey = localMachineHive.OpenSubKey(@"SOFTWARE\Wow6432Node\ODBC\ODBCINST.INI\ODBC Drivers"))
@@ -107,6 +110,7 @@ namespace Sarand.Net.Standard.Dbas.Base
if (odbcDriversKey != null)
return new List<String>(odbcDriversKey.GetValueNames()).OrderBy(item => item).ToList();
}
#pragma warning restore CA1416
}
catch (Exception) { }
return new List<String>();
@@ -116,13 +120,13 @@ namespace Sarand.Net.Standard.Dbas.Base
// O D B C D a t a S o u r c e s
//-------------------------------------------------------------------
[DllImport("odbc32.dll")]
public static extern int SQLDataSources (int EnvHandle, int Direction, StringBuilder ServerName, int ServerNameBufferLenIn, ref int ServerNameBufferLenOut, StringBuilder Driver, int DriverBufferLenIn, ref int DriverBufferLenOut);
public static extern int SQLDataSources(int EnvHandle, int Direction, StringBuilder ServerName, int ServerNameBufferLenIn, ref int ServerNameBufferLenOut, StringBuilder Driver, int DriverBufferLenIn, ref int DriverBufferLenOut);
[DllImport("odbc32.dll")]
public static extern int SQLAllocEnv (ref int EnvHandle);
public static List<SaDbDfnOdbcDataSource> GetOdbcDatasources ()
public static extern int SQLAllocEnv(ref int EnvHandle);
public static List<SaDbDfnOdbcDataSource> GetOdbcDatasources()
{
List<SaDbDfnOdbcDataSource> dataSources = new List<SaDbDfnOdbcDataSource>();
int envHandle=0;
int envHandle = 0;
const int SQL_FETCH_NEXT = 1;
const int SQL_FETCH_FIRST_SYSTEM = 32;
@@ -147,7 +151,7 @@ namespace Sarand.Net.Standard.Dbas.Base
//-------------------------------------------------------------------
// Q u e r y D a t a b a s e
//-------------------------------------------------------------------
public String GetSelectQueryPrefix ()
public String GetSelectQueryPrefix()
{
// Query prefix.
switch (SelectLockMode)
@@ -180,16 +184,16 @@ namespace Sarand.Net.Standard.Dbas.Base
}
return "";
}
public abstract IDbDataTable ExecuteQuery (String query);
public abstract int ExecuteNonQuery (String query);
public abstract int ExecuteInsert (String tableName, List<StringPair> columnNameValues);
public abstract int ExecuteUpdate (String tableName, List<StringPair> columnNameValues, String filter);
public abstract int ExecuteDelete (String tableName, String filter);
public abstract IDbDataTable ExecuteQuery(String query);
public abstract int ExecuteNonQuery(String query);
public abstract int ExecuteInsert(String tableName, List<StringPair> columnNameValues);
public abstract int ExecuteUpdate(String tableName, List<StringPair> columnNameValues, String filter);
public abstract int ExecuteDelete(String tableName, String filter);
/// <summary>
/// Add value pair - used to build value lists for insert and update.
/// <para>Caters for delimiting text values and uses 'NULL' for empty text values.</para>
/// </summary>
public static void AddValuePair (List<StringPair> columnNameValues, String columnName, String value, bool delimitString = false)
public static void AddValuePair(List<StringPair> columnNameValues, String columnName, String value, bool delimitString = false)
{
if (delimitString)
{
@@ -200,42 +204,42 @@ namespace Sarand.Net.Standard.Dbas.Base
columnNameValues.Add(columnName, value);
}
/// <summary>Add SysId value pair - used to build value lists for insert and update.</summary>
public static void AddValuePair (List<StringPair> columnNameValues, String columnName, SysId value)
public static void AddValuePair(List<StringPair> columnNameValues, String columnName, SysId value)
{
columnNameValues.Add(columnName, SysId.ToInt(value).ToString());
}
/// <summary>Add numeric value pair - used to build value lists for insert and update.</summary>
public static void AddValuePair (List<StringPair> columnNameValues, String columnName, long value)
public static void AddValuePair(List<StringPair> columnNameValues, String columnName, long value)
{
columnNameValues.Add(columnName, value.ToString());
}
/// <summary>Add decimal value pair - used to build value lists for insert and update.</summary>
public static void AddValuePair (List<StringPair> columnNameValues, String columnName, decimal value)
public static void AddValuePair(List<StringPair> columnNameValues, String columnName, decimal value)
{
columnNameValues.Add(columnName, value.ToString());
}
/// <summary>Add date/time value pair - used to build value lists for insert and update.</summary>
public static void AddValuePair (List<StringPair> columnNameValues, String columnName, DateTime value)
public static void AddValuePair(List<StringPair> columnNameValues, String columnName, DateTime value)
{
AddValuePair(columnNameValues, columnName, DateUtil.Format(value, "yyyy/MM/dd HH:mm:ss"), true);
}
// Specific queries.
public abstract long GetRowCount (String tableName, String filter);
public abstract DataValue GetRowValue (String tableName, String columnName, String filter);
public abstract DataValue GetRowValue (String tableName, String columnName, String filter, DbGetValueType getValueType);
public abstract long GetRowCount(String tableName, String filter);
public abstract DataValue GetRowValue(String tableName, String columnName, String filter);
public abstract DataValue GetRowValue(String tableName, String columnName, String filter, DbGetValueType getValueType);
//-------------------------------------------------------------------
// D a t a b a s e S c h e m a
//-------------------------------------------------------------------
public abstract List<String> GetTableNames (bool refresh = false);
public abstract List<DbDfnTable> GetTableDfns (bool refresh = false);
public abstract DbDfnTable GetTableDfn (String name, bool refresh = false);
public bool TableExists (String tableName)
public abstract List<String> GetTableNames(bool refresh = false);
public abstract List<DbDfnTable> GetTableDfns(bool refresh = false);
public abstract DbDfnTable GetTableDfn(String name, bool refresh = false);
public bool TableExists(String tableName)
{
if (StringUtil.IsEmpty(tableName)) return false;
return GetTableNames().Exists(table => table.EqualsCI(tableName));
}
public bool ColumnExists (String tableName, String columnName)
public bool ColumnExists(String tableName, String columnName)
{
if (StringUtil.IsEmpty(tableName) || StringUtil.IsEmpty(columnName)) return false;
DbDfnTable table = GetTableDfn(tableName);
@@ -243,7 +247,7 @@ namespace Sarand.Net.Standard.Dbas.Base
return table.HasColumnByName(columnName);
return false;
}
public bool IndexExists (String tableName, String indexName)
public bool IndexExists(String tableName, String indexName)
{
if (StringUtil.IsEmpty(tableName) || StringUtil.IsEmpty(indexName)) return false;
DbDfnTable table = GetTableDfn(tableName);
@@ -263,7 +267,7 @@ namespace Sarand.Net.Standard.Dbas.Base
// 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)
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);
@@ -5,7 +5,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
+13 -21
View File
@@ -173,10 +173,9 @@ namespace Sarand.Net.Standard.Dbas.Data
// Query records.
public override IDbDataTable ExecuteQuery(String query)
{
String fn = GetType().Name + ".ExecuteQuery";
ClearErrorState();
DataTable dataTable = null;
Log(fn, "Query: " + query, SaMessageLevel.Debug);
Log(SysUtil.Fn, "Query: " + query, SaMessageLevel.Debug);
try
{
using (DbConnection dbConnection = CreateConnection())
@@ -191,7 +190,7 @@ namespace Sarand.Net.Standard.Dbas.Data
}
catch (Exception exc)
{
SaLogger.Error(fn + " " + ExceptionUtil.GetLogMessage(exc) + "\n\tQuery: " + query, true);
SaLogger.Error(SysUtil.Fn + " " + ExceptionUtil.GetLogMessage(exc) + "\n\tQuery: " + query, true);
SetErrorState(exc.Message);
}
return new DbDataTable(dataTable);
@@ -200,9 +199,8 @@ namespace Sarand.Net.Standard.Dbas.Data
/// <summary>Execute a non-query statement and return the row count.</summary>
public override int ExecuteNonQuery(string query)
{
String fn = GetType().Name + ".ExecuteNonQuery";
ClearErrorState();
Log(fn, "Query: " + query, SaMessageLevel.Debug);
Log(SysUtil.Fn, "Query: " + query, SaMessageLevel.Debug);
using (var dbConnection = CreateConnection())
using (var dbCommand = dbConnection.CreateCommand())
{
@@ -215,7 +213,7 @@ namespace Sarand.Net.Standard.Dbas.Data
}
catch (Exception exc)
{
SaLogger.Error(fn + " " + ExceptionUtil.GetLogMessage(exc) + "\n\tQuery: " + query, true);
SaLogger.Error(SysUtil.Fn + " " + ExceptionUtil.GetLogMessage(exc) + "\n\tQuery: " + query, true);
SetErrorState(exc.Message);
}
}
@@ -225,13 +223,12 @@ namespace Sarand.Net.Standard.Dbas.Data
/// <summary>Compile and execute an insert statement and return the row count.</summary>
public override int ExecuteInsert(String tableName, List<StringPair> columnNameValues)
{
String fn = GetType().Name + ".ExecuteInsert";
ClearErrorState();
String query =
"INSERT INTO " + tableName + " " +
"(" + columnNameValues.GetValues(StringPair.Value.Value1).Concat(", ") + ") " +
"VALUES (" + columnNameValues.GetValues(StringPair.Value.Value2).Concat(", ") + ")";
Log(fn, "Query: " + query, SaMessageLevel.Debug);
Log(SysUtil.Fn, "Query: " + query, SaMessageLevel.Debug);
using (var dbConnection = CreateConnection())
using (var dbCommand = dbConnection.CreateCommand())
{
@@ -244,7 +241,7 @@ namespace Sarand.Net.Standard.Dbas.Data
}
catch (Exception exc)
{
SaLogger.Error(fn + " " + ExceptionUtil.GetLogMessage(exc) + "\n\tQuery: " + query, true);
SaLogger.Error(SysUtil.Fn + " " + ExceptionUtil.GetLogMessage(exc) + "\n\tQuery: " + query, true);
SetErrorState(exc.Message);
}
}
@@ -254,14 +251,13 @@ namespace Sarand.Net.Standard.Dbas.Data
/// <summary>Compile and execute an update statement and return the row count.</summary>
public override int ExecuteUpdate(String tableName, List<StringPair> columnNameValues, String filter)
{
String fn = GetType().Name + ".ExecuteUpdate";
ClearErrorState();
String query =
"UPDATE " + tableName + " " +
"SET " + columnNameValues.Concat("=").Concat(", ");
if (!StringUtil.IsEmpty(filter))
query += (String)" WHERE " + filter;
Log(fn, "Query: " + query, SaMessageLevel.Debug);
Log(SysUtil.Fn, "Query: " + query, SaMessageLevel.Debug);
using (var dbConnection = CreateConnection())
using (var dbCommand = dbConnection.CreateCommand())
{
@@ -274,7 +270,7 @@ namespace Sarand.Net.Standard.Dbas.Data
}
catch (Exception exc)
{
SaLogger.Error(fn + " " + ExceptionUtil.GetLogMessage(exc) + "\n\tQuery: " + query, true);
SaLogger.Error(SysUtil.Fn + " " + ExceptionUtil.GetLogMessage(exc) + "\n\tQuery: " + query, true);
SetErrorState(exc.Message);
}
}
@@ -284,12 +280,11 @@ namespace Sarand.Net.Standard.Dbas.Data
/// <summary>Compile and execute a delete statement and return the row count.</summary>
public override int ExecuteDelete(String tableName, String filter)
{
String fn = GetType().Name + ".ExecuteDelete";
ClearErrorState();
String query = "DELETE FROM " + tableName;
if (!StringUtil.IsEmpty(filter))
query += " WHERE " + filter;
Log(fn, "Query: " + query, SaMessageLevel.Debug);
Log(SysUtil.Fn, "Query: " + query, SaMessageLevel.Debug);
using (var dbConnection = CreateConnection())
using (var dbCommand = dbConnection.CreateCommand())
{
@@ -302,7 +297,7 @@ namespace Sarand.Net.Standard.Dbas.Data
}
catch (Exception exc)
{
SaLogger.Error(fn + " " + ExceptionUtil.GetLogMessage(exc) + "\n\tQuery: " + query, true);
SaLogger.Error(SysUtil.Fn + " " + ExceptionUtil.GetLogMessage(exc) + "\n\tQuery: " + query, true);
SetErrorState(exc.Message);
}
}
@@ -449,8 +444,7 @@ namespace Sarand.Net.Standard.Dbas.Data
{
if (StringUtil.IsEmpty(tableNameRestriction) && tables.Count > 0) return;
if (!StringUtil.IsEmpty(tableNameRestriction) && tables.Exists(item => item.Name.Equals(tableNameRestriction))) return;
String fn = GetType().Name + ".PopulateTableNames";
if (SacsSystem.DebugMode) SaLogger.Debug(fn + (!StringUtil.IsEmpty(tableNameRestriction) ? " Table: " + tableNameRestriction : " "));
if (SacsSystem.DebugMode) SaLogger.Debug(SysUtil.Fn + (!StringUtil.IsEmpty(tableNameRestriction) ? " Table: " + tableNameRestriction : " "));
String[] restrictions = new String[] { null, null, tableNameRestriction }; // database, owner, table name, type
using (DataTable tableSchema = dbConnection.GetSchema(OdbcMetaDataCollectionNames.Tables, restrictions))
{
@@ -490,7 +484,7 @@ namespace Sarand.Net.Standard.Dbas.Data
else if (colName.EqualsCI("REMARKS"))
remarks = value;
else
SaLogger.Warn(fn + " Unrecognised column name: '" + colName + "'");
SaLogger.Warn(SysUtil.Fn + " Unrecognised column name: '" + colName + "'");
}
// TODO Why is Type sometimes "BASE TABLE"?
if (!StringUtil.IsEmpty(tableName) && (type.EqualsCI("Table") || type.EqualsCI("Base Table")) && (!schema.EqualsCI("sys") || !tableName.Left(9).EqualsCI("trace_xe_")))
@@ -513,8 +507,7 @@ namespace Sarand.Net.Standard.Dbas.Data
//-------------------------------------------------------------------
private void PopulateTableColumns(DbConnection dbConnection, DbDfnTable table)
{
String fn = GetType().Name + ".PopulateTableColumns";
if (SacsSystem.DebugMode) SaLogger.Debug(fn + " Table: " + table.Name);
if (SacsSystem.DebugMode) SaLogger.Debug(SysUtil.Fn + " Table: " + table.Name);
short columnPos = 0;
String[] restrictions = new String[] { null, table.Schema, table.Name, null }; // database, owner, table name, column name
using (DataTable columnSchema = dbConnection.GetSchema(OdbcMetaDataCollectionNames.Columns, restrictions))
@@ -718,7 +711,6 @@ namespace Sarand.Net.Standard.Dbas.Data
//-------------------------------------------------------------------
private void PopulateTableIndices(DbConnection dbConnection, DbDfnTable table)
{
String fn = GetType().Name + ".PopulateTableIndices";
String[] restrictions = new String[] { null, table.Schema, table.Name }; // database, owner, table name
using (DataTable columnSchema = dbConnection.GetSchema(OdbcMetaDataCollectionNames.Indexes, restrictions))
{
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
@@ -41,10 +41,6 @@ namespace Sarand.Net.Standard.Dbas.Proc
//-------------------------------------------------------------------
// Definitions.
public interface IProcessNotifier : ISacsProcessNotifier
{
void OnSchemaComparisonItemAdded(SaDbProcess_CompareDatabaseSchemas proc);
}
public class SchemaComparisonItem
{
internal SchemaComparisonItem(String tableName, String columnName, int columnPosition, String indexName, String comment)
@@ -68,7 +64,6 @@ namespace Sarand.Net.Standard.Dbas.Proc
//-------------------------------------------------------------------
// Data members.
private Config config;
private IProcessNotifier processNotifier = null;
private readonly List<SchemaComparisonItem> schemaComparisonItems = new List<SchemaComparisonItem>();
//-------------------------------------------------------------------
@@ -88,13 +83,19 @@ namespace Sarand.Net.Standard.Dbas.Proc
//-------------------------------------------------------------------
// Properties.
public override string ProcessDescription { get; } = "Database Schema Comparison";
public new IProcessNotifier ProcessNotifier
//-------------------------------------------------------------------
// Events.
// Processing Completed.
public class SchemaComparisonItemAddedEventArgs : EventArgs { }
public event EventHandler<SchemaComparisonItemAddedEventArgs> OnSchemaComparisonItemAdded;
protected virtual void InvokeSchemaComparisonItemAdded()
{
get { return processNotifier; }
set
if (OnSchemaComparisonItemAdded != null)
{
processNotifier = value;
base.ProcessNotifier = value;
SchemaComparisonItemAddedEventArgs args = new SchemaComparisonItemAddedEventArgs();
OnSchemaComparisonItemAdded(this, args);
}
}
@@ -112,7 +113,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
protected override bool Run()
{
schemaComparisonItems.Clear();
if (config.SourceDatabase == null && config.SourceTables == null) return SetProcessError("Source database must be supplied.");
if (config.SourceDatabase == null && config.SourceTables == null) return AddErrorMessage("Source database must be supplied.");
CompareTables();
return OK;
@@ -393,7 +394,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
private void AddSchemaComparisonItem(String tableName, String columnName, int position, String indexName, String comment)
{
schemaComparisonItems.Add(new SchemaComparisonItem(tableName, columnName, position, indexName, comment));
if (ProcessNotifier != null) ProcessNotifier.OnSchemaComparisonItemAdded(this);
InvokeSchemaComparisonItemAdded();
}
//-------------------------------------------------------------------
@@ -32,7 +32,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
}
public new class Config : SaProcess_Base.Config
{
public Config ()
public Config()
{
}
public SourceDefinitionType SourceDefinitionType { get; set; }
@@ -43,9 +43,6 @@ namespace Sarand.Net.Standard.Dbas.Proc
//-------------------------------------------------------------------
// Definitions.
public interface IProcessNotifier : ISacsProcessNotifier
{
}
private class Statistics
{
}
@@ -53,17 +50,16 @@ namespace Sarand.Net.Standard.Dbas.Proc
//-------------------------------------------------------------------
// Data members.
private Config config;
private IProcessNotifier processNotifier;
private readonly List<DbDfnTable> tableDfns = new List<DbDfnTable>();
private Statistics stats;
//-------------------------------------------------------------------
// Construction.
public SaDbProcess_ConvertDbDefinition (Config config) : base(config)
public SaDbProcess_ConvertDbDefinition(Config config) : base(config)
{
this.config = config;
}
protected override void Dispose (bool disposing)
protected override void Dispose(bool disposing)
{
if (disposing)
{
@@ -74,15 +70,6 @@ namespace Sarand.Net.Standard.Dbas.Proc
//-------------------------------------------------------------------
// Properties.
public override string ProcessDescription { get; } = "Database Definition Convertor";
public new IProcessNotifier ProcessNotifier
{
get { return processNotifier; }
set
{
processNotifier = value;
base.ProcessNotifier = value;
}
}
/// <summary>Processing statistics.</summary>
public override List<StringPair> ProcessingStatistics
{
@@ -102,7 +89,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
//-------------------------------------------------------------------
// P r o c e s s i n g
//-------------------------------------------------------------------
protected override bool Run ()
protected override bool Run()
{
// Initialise process.
tableDfns.Clear();
@@ -115,32 +102,32 @@ namespace Sarand.Net.Standard.Dbas.Proc
return OK;
}
protected bool ValidateConfiguration ()
protected bool ValidateConfiguration()
{
// Source file.
if (config.SourceDefinitionType <= 0)
return SetProcessError("Source definition type must be specified.");
return AddErrorMessage("Source definition type must be specified.");
if (StringUtil.IsEmpty(config.SourceDefinitionFilePath))
return SetProcessError("Source definition file must be specified.");
return AddErrorMessage("Source definition file must be specified.");
if (!FileUtil.IsFile(config.SourceDefinitionFilePath))
return SetProcessError("Source definition file not found.");
return AddErrorMessage("Source definition file not found.");
// Target file.
if (config.TargetDefinitionType <= 0)
return SetProcessError("Target definition type must be specified.");
return AddErrorMessage("Target definition type must be specified.");
if (StringUtil.IsEmpty(config.TargetDefinitionFilePath))
return SetProcessError("Target definition file must be specified.");
return AddErrorMessage("Target definition file must be specified.");
if (!DirectoryUtil.DirectoryExists(DirectoryUtil.GetDirectoryName(config.TargetDefinitionFilePath)))
return SetProcessError("Target folder not found.");
return AddErrorMessage("Target folder not found.");
if (config.SourceDefinitionFilePath.EqualsCI(config.TargetDefinitionFilePath))
return SetProcessError("Target file must not be the same as the source file.");
return AddErrorMessage("Target file must not be the same as the source file.");
return OK;
}
//-------------------------------------------------------------------
private bool ConvertCppDatabaseDefinition ()
private bool ConvertCppDatabaseDefinition()
{
if (!OK) return false;
@@ -169,7 +156,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
}
private class SourceDefinition_Params
{
protected SourceDefinition_Params () { }
protected SourceDefinition_Params() { }
public int LineNumber { get; set; } = -1;
public int TableIdx { get; set; } = -1;
public int ColumnIdx { get; set; } = -1;
@@ -186,7 +173,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
public Dictionary<StringPair, ColumnData> ColumnMap { get; } = new Dictionary<StringPair, ColumnData>();
}
//-------------------------------------------------------------------
private bool ExtractDatabaseDefinition ()
private bool ExtractDatabaseDefinition()
{
if (!OK) return false;
@@ -245,7 +232,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
file.Close();
}
}
catch (Exception exc) { SaLogger.Warn(ExceptionUtil.GetLogMessage(exc)); SetProcessError(ExceptionUtil.GetErrorStateMessage(exc)); }
catch (Exception exc) { SaLogger.Warn(ExceptionUtil.GetLogMessage(exc)); AddErrorMessage(ExceptionUtil.GetErrorStateMessage(exc)); }
return OK;
}
@@ -253,7 +240,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
//-------------------------------------------------------------------
// C + + C o d e S o u r c e
//-------------------------------------------------------------------
private readonly Dictionary<int,String> cppColumnDataTypes = new Dictionary<int, string>()
private readonly Dictionary<int, String> cppColumnDataTypes = new Dictionary<int, string>()
{
{ 1, "VarChar" },
{ -7, "Bit" },
@@ -265,7 +252,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
{ 8, "Double" },
{ 93, "DateTime" }
};
private bool ExtractDatabaseDefinitionLine_CppCode (SourceDefinition_Params_CppCode sourceDfnParams, String line)
private bool ExtractDatabaseDefinitionLine_CppCode(SourceDefinition_Params_CppCode sourceDfnParams, String line)
{
if (!OK) return false;
@@ -276,7 +263,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
{
// Table enum start.
if (sourceDfnParams.TablesEnumFound)
return SetProcessError("Found multiple table enums at line " + sourceDfnParams.LineNumber + ": " + line + ".");
return AddErrorMessage("Found multiple table enums at line " + sourceDfnParams.LineNumber + ": " + line + ".");
sourceDfnParams.TablesEnumFound = true;
sourceDfnParams.InTablesEnum = true;
return OK;
@@ -289,13 +276,13 @@ namespace Sarand.Net.Standard.Dbas.Proc
else if (!line.ContainsCI("eSaDbTblUnknown"))
{
if (!line.ContainsCI("eSaDbTbl"))
return SetProcessError("Invalid table definition on line " + sourceDfnParams.LineNumber + ": " + line + ".");
return AddErrorMessage("Invalid table definition on line " + sourceDfnParams.LineNumber + ": " + line + ".");
// Extract table name.
String tableName = line.ReplaceAll("eSaDbTbl","").ReplaceAll(",","");
String tableName = line.ReplaceAll("eSaDbTbl", "").ReplaceAll(",", "");
if (StringUtil.IsEmpty(tableName))
return SetProcessError("Blank table name on line " + sourceDfnParams.LineNumber + ": " + line + ".");
return AddErrorMessage("Blank table name on line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (tableDfns.Exists(item => item.Name.EqualsCI(tableName)))
return SetProcessError("Duplicate table name on line " + sourceDfnParams.LineNumber + ": " + line + ".");
return AddErrorMessage("Duplicate table name on line " + sourceDfnParams.LineNumber + ": " + line + ".");
// Add this table to the list.
sourceDfnParams.TableIdx++;
//SaLogger.Debug("Table #" + sourceDfnParams.TableIdx + ": " + tableName);
@@ -310,7 +297,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
{
// Column enum start.
if (sourceDfnParams.ColumnsEnumFound)
return SetProcessError("Found multiple columns enums at line " + sourceDfnParams.LineNumber + ": " + line + ".");
return AddErrorMessage("Found multiple columns enums at line " + sourceDfnParams.LineNumber + ": " + line + ".");
sourceDfnParams.ColumnsEnumFound = true;
sourceDfnParams.InColumnsEnum = true;
return OK;
@@ -323,7 +310,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
else if (!line.ContainsCI("eSaDbColUnknown"))
{
if (!line.ContainsCI("eSaDbCol"))
return SetProcessError("Invalid column definition on line " + sourceDfnParams.LineNumber + ": " + line + ".");
return AddErrorMessage("Invalid column definition on line " + sourceDfnParams.LineNumber + ": " + line + ".");
// Column definitions:
// eSaDbColUnknown = -1,
// #define eSaDbColVendorType_ListID (eSaDbColumn)4101
@@ -345,7 +332,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
// Split table and column names on Underscore.
String[] tableColumnNameParts = tableColumnName.Split('_');
if (tableColumnNameParts.Length != 2)
return SetProcessError("Invalid column definition on line (missing underscore?) " + sourceDfnParams.LineNumber + ": " + line + ".");
return AddErrorMessage("Invalid column definition on line (missing underscore?) " + sourceDfnParams.LineNumber + ": " + line + ".");
sourceDfnParams.ColumnIdx++;
String tableName = tableColumnNameParts[0];
String columnName = tableColumnNameParts[1];
@@ -360,7 +347,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
{
// Column definitions start.
if (sourceDfnParams.ColumnsDfnsFound)
return SetProcessError("Found multiple column definitions at line " + sourceDfnParams.LineNumber + ": " + line + ".");
return AddErrorMessage("Found multiple column definitions at line " + sourceDfnParams.LineNumber + ": " + line + ".");
sourceDfnParams.ColumnsDfnsFound = true;
sourceDfnParams.InColumnsDfns = true;
sourceDfnParams.ColumnIdx = -1;
@@ -388,40 +375,40 @@ namespace Sarand.Net.Standard.Dbas.Proc
// eTime = 93 SQL_C_TYPE_TIMESTAMP
foreach (String columnDfnDataRaw in line.Split(new String[] { "}," }, StringSplitOptions.RemoveEmptyEntries))
{
String columnDfnData = columnDfnDataRaw.ReplaceAll("{","").TrimAll();
String columnDfnData = columnDfnDataRaw.ReplaceAll("{", "").TrimAll();
//SaLogger.Debug("Column dfn: " + columnDfnData);
String[] columnDfnParts = columnDfnData.Split(',');
if (columnDfnParts.Length != 4) return SetProcessError("Invalid column definition (expecting 4 parts) at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (columnDfnParts.Length != 4) return AddErrorMessage("Invalid column definition (expecting 4 parts) at line " + sourceDfnParams.LineNumber + ": " + line + ".");
String tableIdxRaw = columnDfnParts[0].TrimAll();
int tableIdx = StringUtil.ToInt(tableIdxRaw);
String columnName = columnDfnParts[1].TrimAll().ReplaceAll("\"","");
String columnName = columnDfnParts[1].TrimAll().ReplaceAll("\"", "");
String dataTypeRaw = columnDfnParts[2].TrimAll();
int dataType = StringUtil.ToInt(dataTypeRaw);
String columnLengthRaw = columnDfnParts[3].TrimAll();
int columnLength = StringUtil.ToInt(columnLengthRaw);
if (!tableIdxRaw.IsNumeric()) return SetProcessError("Invalid table index in column definition (" + tableIdxRaw + ") at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (StringUtil.IsEmpty(columnName)) return SetProcessError("Blank name in column definition at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (!dataTypeRaw.IsNumeric(true) || !cppColumnDataTypes.ContainsKey(dataType)) return SetProcessError("Invalid data type in column definition (" + dataTypeRaw + ") at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (!columnLengthRaw.IsNumeric()) return SetProcessError("Invalid column length in column definition (" + columnLengthRaw + ") at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (!tableIdxRaw.IsNumeric()) return AddErrorMessage("Invalid table index in column definition (" + tableIdxRaw + ") at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (StringUtil.IsEmpty(columnName)) return AddErrorMessage("Blank name in column definition at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (!dataTypeRaw.IsNumeric(true) || !cppColumnDataTypes.ContainsKey(dataType)) return AddErrorMessage("Invalid data type in column definition (" + dataTypeRaw + ") at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (!columnLengthRaw.IsNumeric()) return AddErrorMessage("Invalid column length in column definition (" + columnLengthRaw + ") at line " + sourceDfnParams.LineNumber + ": " + line + ".");
// Table definition.
String tableName = sourceDfnParams.TableMap.Values.ToList().Find(item => item.TableIdx == tableIdx)?.TableName ?? "";
if (StringUtil.IsEmpty(tableName)) return SetProcessError("Unable to identify table name for Table idx " + tableIdx + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (StringUtil.IsEmpty(tableName)) return AddErrorMessage("Unable to identify table name for Table idx " + tableIdx + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
DbDfnTable tableDfn = tableDfns.GetTableByName(tableName);
if (tableDfn == null) return SetProcessError("Unable to retrieve table definition for Table name " + tableName + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (tableDfn == null) return AddErrorMessage("Unable to retrieve table definition for Table name " + tableName + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
// Column data.
sourceDfnParams.ColumnIdx++;
ColumnData columnData = sourceDfnParams.ColumnMap.Values.ToList().Find(item => item.TableName.EqualsCI(tableName) && item.ColumnIdx == sourceDfnParams.ColumnIdx);
if (columnData == null) return SetProcessError("Unable to column data for table " + tableName + " and column " + columnName + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (columnData.ColumnName.EqualsCI(columnName)) return SetProcessError("Inconsistent column name for table " + tableName + " and column " + columnName + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (columnData == null) return AddErrorMessage("Unable to column data for table " + tableName + " and column " + columnName + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (columnData.ColumnName.EqualsCI(columnName)) return AddErrorMessage("Inconsistent column name for table " + tableName + " and column " + columnName + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
// Column definition.
DbDfnColumn columnDfn = new DbDfnColumn()
{
Table = tableName,
Name = columnName,
DataType = EnumUtil.ParseEnum(cppColumnDataTypes.GetValue(dataType,""),DbColumnDataType.Unknown),
DataType = EnumUtil.ParseEnum(cppColumnDataTypes.GetValue(dataType, ""), DbColumnDataType.Unknown),
Size = columnLength
};
if (columnDfn.DataType < 0) return SetProcessError("Invalid data type for table " + tableName + " and column " + columnName + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (columnDfn.DataType < 0) return AddErrorMessage("Invalid data type for table " + tableName + " and column " + columnName + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
tableDfn.AddColumn(columnDfn);
}
return OK;
@@ -62,7 +62,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
//-------------------------------------------------------------------
public List<DbDfnTable> ConvertXmlToTables(String xmlSchemaData)
{
ClearProcessError();
ClearProcessMessages();
String fn = GetType().Name + ".ConvertXmlToTables";
Dictionary<String, DbDfnTable> tables = new Dictionary<String, DbDfnTable>(StringComparer.OrdinalIgnoreCase);
@@ -169,7 +169,7 @@ namespace Sarand.Net.Standard.Dbas.Proc
}
catch (Exception exc)
{
SetProcessError(exc.GetErrorStateMessage());
AddErrorMessage(exc.GetErrorStateMessage());
SaLogger.Warn(fn + " " + ExceptionUtil.GetLogMessage(exc));
}
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
@@ -3,7 +3,6 @@ using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using Sarand.Net.Standard.Base.Dbas;
+2 -2
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Sarand.Net.Standard</RootNamespace>
</PropertyGroup>
@@ -12,7 +12,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" Version="2.1.2" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="System.Data.Odbc" Version="5.0.0" />
<PackageReference Include="System.Data.Odbc" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
@@ -54,6 +54,7 @@ namespace Sarand.Net.Standard.Mobile.Forms.Templates
{
base.OnRemoved(view);
}
[Obsolete("OnChildRemoved(Element) is obsolete as of version 4.8.0. Please use OnChildRemoved(Element, int) instead.")]
protected override void OnChildRemoved (Element child)
{
base.OnChildRemoved(child);
@@ -66,6 +66,7 @@ namespace Sarand.Net.Standard.Mobile.Forms.Panels
{
base.OnChildAdded(child);
}
[Obsolete("OnChildRemoved(Element) is obsolete as of version 4.8.0. Please use OnChildRemoved(Element, int) instead.")]
protected override void OnChildRemoved (Element child)
{
base.OnChildRemoved(child);
+3 -3
View File
@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Sarand.Net.Standard</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2012" />
<PackageReference Include="Xamarin.Essentials" Version="1.8.1" />
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2622" />
</ItemGroup>
<ItemGroup>
@@ -0,0 +1,38 @@
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace Sarand.Net.Standard.Proc.Excel
{
//=======================================================================
public static class ExcelHelper
{
//-------------------------------------------------------------------
/// <summary>
/// Validate worksheet name.
/// <para>Worksheet names cannot:</para>
/// <para>- Be blank.</para>
/// <para>- Contain more than 31 characters.</para>
/// <para>- Contain any of the following characters: / \ ? * : [ ]</para>
/// <para>- For example, 02/17/2016 would not be a valid worksheet name, but 02-17-2016 would work fine.</para>
/// <para>- Begin or end with an apostrophe ('), but they can be used in between text or numbers in a name.</para>
/// <para>- Be named "History". This is a reserved word Excel uses internally.</para>
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static bool IsValidSheetName(String name)
{
if (Base.Utils.StringUtil.IsBlank(name)) return false;
if (name.Length > 31) return false;
if (Regex.IsMatch(name, "[/\\\\?*:[\\]]")) return false;
if (name.StartsWith("'") || name.EndsWith("'")) return false;
if (name.EqualsCI("History")) return false;
return true;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -140,7 +140,7 @@ namespace Sarand.Net.Standard.Proc.Excel
Yellow = 6,
Pink = 7,
Magenta = 7,
Turqoise = 8,
Turquoise = 8,
Cyan = 8,
DarkRed = 9,
Green = 10,
@@ -151,7 +151,7 @@ namespace Sarand.Net.Standard.Proc.Excel
Gray25 = 15,
Gray50 = 16,
SkyBlue = 33,
LightTurqoise = 34,
LightTurquoise = 34,
LightGreen = 35,
LightYellow = 36,
PaleBlue = 37,
+295 -153
View File
@@ -4,6 +4,10 @@ using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Utils;
using DocumentFormat.OpenXml.Spreadsheet;
using NPOI.SS.UserModel;
using NPOI.SS.Formula.Functions;
using System.Text.RegularExpressions;
namespace Sarand.Net.Standard.Proc.Excel
{
@@ -16,24 +20,24 @@ namespace Sarand.Net.Standard.Proc.Excel
//=======================================================================
public class SaMsExcelRange
{
public uint ColFrom { get; set; } = 0;
public uint ColTo { get; set; } = 0;
public uint RowFrom { get; set; } = 0;
public uint RowTo { get; set; } = 0;
public uint ColFromPos { get; set; } = 0;
public uint ColToPos { get; set; } = 0;
public uint RowFromPos { get; set; } = 0;
public uint RowToPos { get; set; } = 0;
public SaMsExcelRange() { }
public SaMsExcelRange(uint colPos, uint rowPos)
{
ColFrom = colPos;
ColTo = colPos;
RowFrom = rowPos;
RowTo = rowPos;
ColFromPos = colPos;
ColToPos = colPos;
RowFromPos = rowPos;
RowToPos = rowPos;
}
public SaMsExcelRange(uint colPosFrom, uint rowPosFrom, uint colPosTo, uint rowPosTo)
{
ColFrom = colPosFrom;
ColTo = colPosTo;
RowFrom = rowPosFrom;
RowTo = rowPosTo;
ColFromPos = colPosFrom;
ColToPos = colPosTo;
RowFromPos = rowPosFrom;
RowToPos = rowPosTo;
}
}
//=======================================================================
@@ -78,17 +82,29 @@ namespace Sarand.Net.Standard.Proc.Excel
//=======================================================================
public class SaMsExcelColumnHeading_Config
{
public double ColumnWidthPixelsPerCm { get; set; } = 43.0; // Between 10 and 100
public uint FirstHeadingRow { get; set; } = 1; // Must be > 0
public uint FirstHeadingColumn { get; set; } = 1; // Must be > 0
public uint FreezeColumn { get; set; } = 0; // 0 = don't freeze
public short HeadingFontSize { get; set; } = 9;
public bool HeadingTextBold { get; set; } = true;
public bool HeadingTextWrap { get; set; } = true;
/// <summary>Cell font colour index, ref. XlColorIndex</summary>
public SaXlColourIndex InteriorColourIndex { get; set; } = SaXlColourIndex.NotSpecified;
/// <summary>Cell RGB colour value, ref. XlRgbColor. Overrides colour index if changed from default value.</summary>
public int? InteriorColour { get; set; } = null;
// Heading configuration.
/// <summary>Pixels per cm. Default 43. Min 10, max 100.</summary>
public double ColumnWidthPixelsPerCm { get; set; } = 43.0;
/// <summary>First heading column position (from 1). Default 1. Must be > 0.</summary>
public uint FirstHeadingColPos { get; set; } = 1;
/// <summary>First heading row position (from 1). Default 1. Must be > 0.</summary>
public uint FirstHeadingRowPos { get; set; } = 1;
/// <summary>First data row position. Reset by SetColumnHeadings().</summary>
public uint FirstDataRowPos { get; set; } = 2;
/// <summary>Freeze column position - cell in first data row. 0 = don't freeze.</summary>
public uint FreezeColPos { get; set; } = 0;
/// <summary>Font attributes. Default Arial 9 bold.</summary>
public SaMsExcelFontAttr FontAttr { get; } = new SaMsExcelFontAttr() { Name = "Arial", HeightInPoints = 9, Bold = true };
/// <summary>Whether to wrap column header cells.</summary>
public bool HeadingTextWrap { get; set; } = false;
/// <summary>Cell vertical alignment. XlVAlign.xlTop, xlCenter, xlBottom, xlJustify, xlDistributed etc.</summary>
public short? VerticalAlignment { get; set; } = null;
/// <summary>Cell foreground colour index, ref. XlColorIndex</summary>
public SaXlColourIndex ForegroundColourIndex { get; set; } = SaXlColourIndex.NotSpecified;
/// <summary>Cell foreground RGB colour value, ref. XlRgbColor. Overrides foreground colour index if changed from default value.</summary>
public int? ForegroundColourRGB { get; set; } = null;
/// <summary>Cell background colour index, ref. XlColorIndex</summary>
public SaXlColourIndex BackgroundColourIndex { get; set; } = SaXlColourIndex.NotSpecified;
public SaXlBorderWeight BorderWeight { get; set; } = SaXlBorderWeight.NotSpecified;
public bool SetColumnWidths { get; set; } = true;
public bool SetColumnAlignment { get; set; } = true;
@@ -147,29 +163,55 @@ namespace Sarand.Net.Standard.Proc.Excel
public class SaMsExcelFontAttr
{
/// <summary>Font name. Arial etc.</summary>
public String FontName { get; set; } = String.Empty;
public String Name { get; set; } = String.Empty;
/// <summary>Font style. Regular etc.</summary>
public String FontStyle { get; set; } = String.Empty;
public short? FontSize { get; set; } = null;
public double? HeightInPoints { get; set; } = null;
/// <summary>Font colour index, ref. XlColorIndex.</summary>
public SaXlColourIndex FontColourIndex { get; set; } = SaXlColourIndex.NotSpecified;
/// <summary>Font RGB colour value, ref. XlRgbColor. Overrides colour index if changed from default value.</summary>
public int? FontColor { get; set; } = null;
public SaXlColourIndex ColourIndex { get; set; } = SaXlColourIndex.NotSpecified;
/// <summary>Font RGB colour value, ref. XlRgbColor.</summary>
public int? ColourRGB { get; set; } = null;
public bool? Bold { get; set; } = null;
public bool? Strikethrough { get; set; } = null;
public bool? Superscript { get; set; } = null;
public bool? Subscript { get; set; } = null;
public bool? OutlineFont { get; set; } = null;
public bool? Outline { get; set; } = null;
public bool? Shadow { get; set; } = null;
public SaXlUnderlineStyle? Underline { get; set; } = null;
public void CopyFrom(SaMsExcelFontAttr fontAttr)
{
Name = fontAttr.Name;
FontStyle = fontAttr.FontStyle;
HeightInPoints = fontAttr.HeightInPoints;
ColourIndex = fontAttr.ColourIndex;
ColourRGB = fontAttr.ColourRGB;
Bold = fontAttr.Bold;
Strikethrough = fontAttr.Strikethrough;
Superscript = fontAttr.Superscript;
Subscript = fontAttr.Subscript;
Outline = fontAttr.Outline;
Shadow = fontAttr.Shadow;
Underline = fontAttr.Underline;
}
public bool IsSpecified()
{
if (HeightInPoints != null && HeightInPoints >= SaMsSpreadsheet.FontHeightInPointsMin && HeightInPoints <= SaMsSpreadsheet.FontHeightInPointsMax) return true;
if (ColourRGB != null || ColourIndex != SaXlColourIndex.NotSpecified) return true;
if ((Bold ?? false) || (Strikethrough ?? false) || (Superscript ?? false) || (Subscript ?? false) || (Outline ?? false) || (Shadow ?? false) || (Underline != null)) return true;
return false;
}
}
//=======================================================================
public class SaMsExcelItemAttr : SaMsExcelFontAttr
/// <summary>
/// Style attributes.
/// </summary>
public class SaMsExcelStyleAttr
{
// Item attributes.
public SaMsExcelFontAttr FontAttr { get; } = new SaMsExcelFontAttr();
// Attributes that are changed from their default (generally null) are set against the specified range.
/// <summary>Cell data format. "@"=Text etc.</summary>
public String NumberFormat { get; set; } = String.Empty;
public String DataFormat { get; set; } = String.Empty;
/// <summary>Cell horizontal alignment. XlHAlign.xlHAlignLeft, xlHAlignCenter, xlHAlignRight etc.</summary>
public short? HorizontalAlignment { get; set; } = null;
/// <summary>Cell indentation. Tabbed - left, right or distributed alignment [0 to 6]</summary>
@@ -180,16 +222,16 @@ namespace Sarand.Net.Standard.Proc.Excel
public bool? WrapText { get; set; } = null;
/// <summary>Whether cells are merged.</summary>
public bool? MergeCells { get; set; } = null;
/// <summary>Font colour index, ref. XlColorIndex. Black = 1 etc.</summary>
public SaXlColourIndex InteriorColourIndex { get; set; } = SaXlColourIndex.NotSpecified;
/// <summary>Cell RGB colour value, ref. XlRgbColor. Overrides colour index if changed from default value.</summary>
public int? InteriorColour { get; set; } = null;
/// <summary>Cell pattern, ref. XlPattern. xlSolid etc.</summary>
/// <summary>Font pattern foreground colour index, ref. XlColorIndex. Black = 1 etc.</summary>
public SaXlColourIndex ForegroundColourIndex { get; set; } = SaXlColourIndex.NotSpecified;
/// <summary>Cell pattern foreground RGB colour value, ref. XlRgbColor. Overrides colour index if changed from default value.</summary>
public int? ForegroundColourRGB { get; set; } = null;
/// <summary>Cell pattern, ref. XlPattern. xlSolid etc. NB Defaults to Solid if foreground colour specified.</summary>
public SaXlPattern InteriorPattern { get; set; } = SaXlPattern.NotSpecified;
/// <summary>Cell pattern colour index, ref. XlColorIndex. xlAutomatic etc.</summary>
public SaXlColourIndex InteriorPatternColourIndex { get; set; } = SaXlColourIndex.NotSpecified;
/// <summary>Cell pattern RBG Colour value, ref. XlRgbColor. Overrides colour index if changed from default value.</summary>
public int? InteriorPatternColour { get; set; } = null;
/// <summary>Cell pattern background colour index, ref. XlColorIndex. xlAutomatic etc.</summary>
public SaXlColourIndex BackgroundColourIndex { get; set; } = SaXlColourIndex.NotSpecified;
/// <summary>Cell pattern background RBG Colour value, ref. XlRgbColor. Overrides colour index if changed from default value.</summary>
public int? BackgroundPatternColour { get; set; } = null;
// Borders.
public SaMsExcelBorderAttr BorderLeft { get; } = new SaMsExcelBorderAttr(XlBordersIndex.xlEdgeLeft);
public SaMsExcelBorderAttr BorderTop { get; } = new SaMsExcelBorderAttr(XlBordersIndex.xlEdgeTop);
@@ -201,12 +243,7 @@ namespace Sarand.Net.Standard.Proc.Excel
{
get
{
List<SaMsExcelBorderAttr> borders = new List<SaMsExcelBorderAttr>();
borders.Add(BorderLeft);
borders.Add(BorderTop);
borders.Add(BorderRight);
borders.Add(BorderBottom);
return borders;
return new List<SaMsExcelBorderAttr> { BorderLeft, BorderTop, BorderRight, BorderBottom };
}
}
}
@@ -215,9 +252,12 @@ namespace Sarand.Net.Standard.Proc.Excel
{
//-------------------------------------------------------------------
// Definitions.
public static int FontSizeMin { get; } = 4;
public static int FontSizeMax { get; } = 48;
public static double FontHeightInPointsMin { get; } = 4;
public static double FontHeightInPointsMax { get; } = 48;
public static int NullColour { get; } = 0x4a4d4120; /* AFX_RFX_LONG_PSEUDO_NULL */
public const String Msg_NotSupported = "Not Supported";
public const String Msg_WorkbookNotOpen = "Workbook is not open";
public const String Msg_WorksheetNotOpen = "Worksheet is not open";
//-------------------------------------------------------------------
// Data members.
@@ -235,37 +275,55 @@ namespace Sarand.Net.Standard.Proc.Excel
}
//-------------------------------------------------------------------
// W o r k b o o k
//-------------------------------------------------------------------
// Workbook.
public abstract bool IsWorkbookValid();
public abstract bool CreateWorkbook();
public abstract bool OpenWorkbook(String filePath);
/// <summary>Cleanse workbook - remove unreferenced styles etc.</summary>
public abstract bool CleanseWorkbook();
public abstract bool SaveDocument(String filePath, int fileFormat = (int)SaXlFileFormat.Default /* Default: XlFileFormat.xlOpenXMLWorkbook */, bool bAutomaticallyOverwrite = false);
public abstract bool CloseWorkbook();
public abstract byte[] GetDocumentData();
// Default style.
public abstract void ApplyDefaultStyle(SaMsExcelStyleAttr attr);
//-------------------------------------------------------------------
// W o r k s h e e t
//-------------------------------------------------------------------
/// <summary>Is the current worksheet valid.</summary>
public abstract bool IsWorksheetValid();
// Worksheets.
/// <summary>Number of worksheets in the workbook.</summary>
public abstract uint WorksheetCount { get; }
/// <summary>Set the worksheets in the workbook - sheets are added or removed to match the value.</summary>
public abstract bool SetWorksheetCount(int worksheetCount);
/// <summary>Names of worksheets in the workbook.</summary>
public abstract List<String> WorksheetNames { get; }
/// <summary>Add a worksheet to the workbook.</summary>
public abstract bool AddWorksheet(String name);
/// <summary>Remove the nth worksheet - position from 1.</summary>
public abstract bool RemoveWorksheet(uint worksheetPos);
//-------------------------------------------------------------------
// Worksheet.
/// <summary>Open the nth worksheet - position from 1.</summary>
public abstract bool OpenWorksheet(uint worksheetPos);
/// <summary>Open the worksheet with the specified name.</summary>
public abstract bool OpenWorksheet(String name);
/// <summary>Close the current worksheet.</summary>
public abstract bool CloseWorksheet();
/// <summary>Is the current worksheet valid.</summary>
public abstract bool IsWorksheetValid();
/// <summary>Get the position of the current worksheet. 0 = not open.</summary>
public abstract uint WorksheetPos { get; }
/// <summary>Get the name of the current worksheet.</summary>
public abstract String WorksheetName { get; set; }
/// <summary>Set the default attributes for the entire worksheet</summary>
public abstract bool SetWorksheetAttr(SaMsExcelItemAttr attr);
/// <summary>Close the current worksheet.</summary>
public abstract bool CloseWorksheet();
public abstract bool SetWorksheetAttr(SaMsExcelStyleAttr attr);
// Worksheet Page Setup.
/// <summary>Configure the Page Setup properties.</summary>
public abstract bool SetPageSetup(SaMsExcelPageSetup_Config pageSetupConfig);
@@ -273,110 +331,175 @@ namespace Sarand.Net.Standard.Proc.Excel
public abstract bool SetPageBreak(uint colPos, uint rowPos);
//-------------------------------------------------------------------
// C o l u m n s
//-------------------------------------------------------------------
public abstract uint GetActiveColPos();
// Columns.
public abstract uint ActiveColPos { get; }
public abstract double GetColWidth(uint colPos, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point);
public abstract bool SetColWidth(uint colPos, double fWidth, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point);
//-------------------------------------------------------------------
// R o w s
//-------------------------------------------------------------------
public abstract uint GetActiveRowPos();
public abstract List<DataValue> GetRowDataValues(uint rowPos, int maxColumns = 0);
// Rows.
public abstract uint FirstRowPos { get; }
public abstract uint LastRowPos { get; }
public abstract uint ActiveRowPos { get; }
public abstract List<DataValue> GetRowValues(uint rowPos, int maxColumns = 0);
public abstract double GetRowHeight(uint rowPos, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point);
public abstract bool SetRowHeight(uint rowPos, double fHeight, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point);
//-------------------------------------------------------------------
// C e l l s
//-------------------------------------------------------------------
// Cells.
public abstract void SetActiveCell(uint colPos, uint rowPos);
public abstract String GetCellFormula(uint colPos, uint rowPos);
public abstract DataValue GetCellDataValue(uint colPos, uint rowPos);
public abstract bool SetCellFormula(uint colPos, uint rowPos, String value);
public abstract bool SetCellFormula(uint colPos, uint rowPos, String value, SaMsExcelItemAttr attr);
public abstract bool SetRowFormulas(uint colStart, uint row, List<Object> values);
public abstract bool SetRangeFormulas(SaMsExcelRange range, List<Object> values);
public abstract String GetCellText(uint colPos, uint rowPos, bool trimWhitespace = true);
public abstract DataValue GetCellValue(uint colPos, uint rowPos);
public abstract bool SetCellFormula(uint colPos, uint rowPos, String value, SaMsExcelStyleAttr attr = null);
public abstract bool SetCellValue(uint colPos, uint rowPos, DataValue value, SaMsExcelStyleAttr attr = null);
/// <summary>Set cell formulas across a row - override if more efficient method available.</summary>
public virtual bool SetRowFormulas(uint colPosStart, uint rowPos, List<String> values)
{
for (uint colPos = Math.Max(1, colPosStart); colPos <= values.Count; colPos++)
SetCellFormula(colPos, rowPos, values[(int)(colPos - 1)]);
return !HasErrorState();
}
/// <summary>Set cell values across a row - override if more efficient method available.</summary>
public virtual bool SetRowValues(uint colPosStart, uint rowPos, List<DataValue> values)
{
for (uint colPos = Math.Max(1, colPosStart); colPos <= values.Count; colPos++)
SetCellValue(colPos, rowPos, values[(int)(colPos - 1)]);
return !HasErrorState();
}
/// <summary>
/// Set cell formulas across a range of cells - override if more efficient logic available.
/// <para>- Values will wrap if more cells to fill than values.</para>
public virtual bool SetRangeFormulas(SaMsExcelRange range, List<String> values)
{
if (values.IsEmpty()) return SetErrorState(SysUtil.Fn + " No values supplied");
int valueIdx = 0;
for (uint colPos = Math.Max(1, range.ColFromPos); colPos <= range.ColToPos; colPos++)
{
for (uint rowPos = Math.Max(1, range.RowFromPos); rowPos <= range.RowToPos; rowPos++)
{
// Wrap values if more cells to fill.
if (valueIdx >= values.Count)
valueIdx = 0;
SetCellFormula(colPos, rowPos, values[valueIdx++]);
}
}
return !HasErrorState();
}
/// <summary>
/// Set cell values across a range of cells - override if more efficient logic available.
/// <para>- Values will wrap if more cells to fill than values.</para>
public virtual bool SetRangeValues(SaMsExcelRange range, List<DataValue> values)
{
if (values.IsEmpty()) return SetErrorState(SysUtil.Fn + " No values supplied");
int valueIdx = 0;
for (uint colPos = Math.Max(1, range.ColFromPos); colPos <= range.ColToPos; colPos++)
{
for (uint rowPos = Math.Max(1, range.RowFromPos); rowPos <= range.RowToPos; rowPos++)
{
// Wrap values if more cells to fill.
if (valueIdx >= values.Count)
valueIdx = 0;
SetCellValue(colPos, rowPos, values[valueIdx++]);
}
}
return !HasErrorState();
}
//-------------------------------------------------------------------
// Populate headings.
public bool SetColumnHeadings(List<ColumnDfn> columns, SaMsExcelColumnHeading_Config config)
public bool SetColumnHeadings(List<ColumnDfn> columns, SaMsExcelColumnHeading_Config headingConfig)
{
ClearErrorState();
double columnWidthPixelsPerCm = Math.Min(100.0, Math.Max(10.0, config.ColumnWidthPixelsPerCm));
uint firstHeadingRow = (uint)Math.Max(1, config.FirstHeadingRow);
double columnWidthPixelsPerCm = Math.Min(100.0, Math.Max(10.0, headingConfig.ColumnWidthPixelsPerCm));
uint firstHeadingRowPos = (uint)Math.Max(1, headingConfig.FirstHeadingRowPos);
// Assume only a single header row.
headingConfig.FirstDataRowPos = firstHeadingRowPos + 1;
try
{
// Populate default heading cell attributes.
SaMsExcelItemAttr attrHdrDefault = new SaMsExcelItemAttr();
attrHdrDefault.Bold = config.HeadingTextBold;
attrHdrDefault.FontSize = (short)Math.Min(80, Math.Max(2, (int)config.HeadingFontSize));
attrHdrDefault.InteriorColourIndex = config.InteriorColourIndex;
if (config.InteriorColour != null)
attrHdrDefault.InteriorColour = config.InteriorColour;
if (config.BorderWeight != SaXlBorderWeight.NotSpecified)
SaMsExcelStyleAttr attrHdrDefault = new SaMsExcelStyleAttr();
attrHdrDefault.FontAttr.CopyFrom(headingConfig.FontAttr);
attrHdrDefault.WrapText = headingConfig.HeadingTextWrap ? true : null;
attrHdrDefault.VerticalAlignment = headingConfig.VerticalAlignment;
attrHdrDefault.ForegroundColourIndex = headingConfig.ForegroundColourIndex;
attrHdrDefault.BackgroundColourIndex = headingConfig.BackgroundColourIndex;
if (headingConfig.ForegroundColourRGB != null)
attrHdrDefault.ForegroundColourRGB = headingConfig.ForegroundColourRGB;
if (headingConfig.BorderWeight != SaXlBorderWeight.NotSpecified)
{
attrHdrDefault.BorderLeft.Weight = config.BorderWeight;
attrHdrDefault.BorderTop.Weight = config.BorderWeight;
attrHdrDefault.BorderRight.Weight = config.BorderWeight;
attrHdrDefault.BorderBottom.Weight = config.BorderWeight;
attrHdrDefault.BorderInsideVertical.Weight = config.BorderWeight;
attrHdrDefault.BorderLeft.Weight = headingConfig.BorderWeight;
attrHdrDefault.BorderTop.Weight = headingConfig.BorderWeight;
attrHdrDefault.BorderRight.Weight = headingConfig.BorderWeight;
attrHdrDefault.BorderBottom.Weight = headingConfig.BorderWeight;
}
SaMsExcelRange rangeHeadings = new SaMsExcelRange(config.FirstHeadingColumn, firstHeadingRow, (uint)(config.FirstHeadingColumn + columns.Count - 1), firstHeadingRow);
SaMsExcelRange rangeHeadings = new SaMsExcelRange(headingConfig.FirstHeadingColPos, firstHeadingRowPos, (uint)(headingConfig.FirstHeadingColPos + columns.Count - 1), firstHeadingRowPos);
SetRangeAttr(rangeHeadings, attrHdrDefault);
// Set default column attributes.
bool defaultColumnAttributesPopulated = false;
SaMsExcelItemAttr attrColDefault = new SaMsExcelItemAttr();
if (config.SetColumnAlignment)
SaMsExcelStyleAttr attrColDefault = new SaMsExcelStyleAttr();
if (headingConfig.SetColumnAlignment)
{
attrColDefault.HorizontalAlignment = (short)XlHAlign.xlHAlignLeft;
defaultColumnAttributesPopulated = true;
}
if (config.SetColumnDataTypeFormat)
if (headingConfig.SetColumnDataTypeFormat)
{
attrColDefault.NumberFormat = "@"; // '@' = 'Text' format.
attrColDefault.DataFormat = "@"; // '@' = 'Text' format.
defaultColumnAttributesPopulated = true;
}
if (defaultColumnAttributesPopulated)
{
SaMsExcelRange rangeColumns = new SaMsExcelRange(config.FirstHeadingColumn, 0, (uint)(config.FirstHeadingColumn + columns.Count - 1), 0);
SaMsExcelRange rangeColumns = new SaMsExcelRange(headingConfig.FirstHeadingColPos, 0, (uint)(headingConfig.FirstHeadingColPos + columns.Count - 1), 0);
SetRangeAttr(rangeColumns, attrColDefault);
}
// Populate column headings and specific styles.
List<Object> values = new List<Object>();
// Populate column styling and headings.
List<DataValue> columnHeadings = new List<DataValue>();
for (int columnIdx = 0; columnIdx < columns.Count; columnIdx++)
{
ColumnDfn column = columns[columnIdx];
uint col = (uint)(Math.Max(1, config.FirstHeadingColumn) + columnIdx);
SaMsExcelRange range = new SaMsExcelRange(col, firstHeadingRow);
uint colPos = (uint)(Math.Max(1, headingConfig.FirstHeadingColPos) + columnIdx);
SaMsExcelRange range = new SaMsExcelRange(colPos, firstHeadingRowPos);
// Specific heading cell attributes - none supported as yet.
// Specific heading cell attributes.
bool headingCellHasSpecificAttr = false;
SaMsExcelItemAttr attrHdr = new SaMsExcelItemAttr();
SaMsExcelStyleAttr attrHdr = new SaMsExcelStyleAttr();
if (headingCellHasSpecificAttr)
SetRangeAttr(range, attrHdr);
// Column width and caption:
if (config.SetColumnWidths) SetColWidth(col, column.Width / columnWidthPixelsPerCm, SaExcelLogicalUnit.Cm);
values.Add(column.Caption);
if (headingConfig.SetColumnWidths) SetColWidth(colPos, column.Width * 1.5 / columnWidthPixelsPerCm, SaExcelLogicalUnit.Cm);
columnHeadings.Add(column.Caption);
// Column attributes:
bool columnHasSpecificAttributes = false;
SaMsExcelItemAttr attrCol = new SaMsExcelItemAttr();
if (config.SetColumnAlignment && column.Alignment != ColumnAlignment.Left && column.Alignment != ColumnAlignment.Default)
bool columnHasSpecificStyle = false;
SaMsExcelStyleAttr attrCol = new SaMsExcelStyleAttr();
if (headingConfig.SetColumnAlignment && column.Alignment != ColumnAlignment.Left && column.Alignment != ColumnAlignment.Default)
{
columnHasSpecificAttributes = true;
columnHasSpecificStyle = true;
switch (column.Alignment)
{
case ColumnAlignment.Center: attrCol.HorizontalAlignment = (short)XlHAlign.xlHAlignCenter; break;
case ColumnAlignment.Right: attrCol.HorizontalAlignment = (short)XlHAlign.xlHAlignRight; break;
}
}
if (config.SetColumnDataTypeFormat && column.DataType != DataValue.DataType.String)
if (column.WrapText)
{
columnHasSpecificAttributes = true;
columnHasSpecificStyle = true;
attrCol.WrapText = true;
}
if (headingConfig.SetColumnDataTypeFormat && column.DataType != DataValue.DataType.String)
{
columnHasSpecificStyle = true;
if (column.DataType == DataValue.DataType.Long || column.DataType == DataValue.DataType.Double || column.DataType == DataValue.DataType.Decimal)
{
// Numeric format: "#,##0.00"
@@ -388,63 +511,66 @@ namespace Sarand.Net.Standard.Proc.Excel
}
if ((column.DataType == DataValue.DataType.Double || column.DataType == DataValue.DataType.Decimal) && column.NumDecimals > 0)
numberFormat += "." + "0".PadRight(column.NumDecimals, '0');
attrCol.NumberFormat = numberFormat + ";[Red]-" + numberFormat;
attrCol.DataFormat = numberFormat + ";[Red]-" + numberFormat;
}
else if (column.DataType == DataValue.DataType.Date) attrCol.NumberFormat = config.CellFormatDate;
else if (column.DataType == DataValue.DataType.Time) attrCol.NumberFormat = config.CellFormatTime;
else if (column.DataType == DataValue.DataType.DateTime) attrCol.NumberFormat = config.CellFormatDate + config.CellFormatDateTimeDelim + config.CellFormatTime;
else if (column.DataType == DataValue.DataType.Date) attrCol.DataFormat = headingConfig.CellFormatDate;
else if (column.DataType == DataValue.DataType.Time) attrCol.DataFormat = headingConfig.CellFormatTime;
else if (column.DataType == DataValue.DataType.DateTime) attrCol.DataFormat = headingConfig.CellFormatDate + headingConfig.CellFormatDateTimeDelim + headingConfig.CellFormatTime;
}
if (columnHasSpecificAttributes)
SetRangeAttr(new SaMsExcelRange(col, 0), attrCol);
if (columnHasSpecificStyle)
SetRangeAttr(new SaMsExcelRange(colPos, 0), attrCol);
// Freeze the window below if a colmn has been specified:
if (config.FreezeColumn > 0) FreezePanes(config.FreezeColumn, firstHeadingRow + 1);
if (headingConfig.FreezeColPos > 0) FreezePanes(headingConfig.FreezeColPos, firstHeadingRowPos + 1);
}
// Populate header captions.
SetRowFormulas(config.FirstHeadingColumn, firstHeadingRow, values);
// Set header row to wrap text:
if (config.HeadingTextWrap)
{
SaMsExcelItemAttr attrRow = new SaMsExcelItemAttr();
attrRow.WrapText = true;
SetRangeAttr(new SaMsExcelRange(0, firstHeadingRow), attrRow);
// Auto fit the header row (set height to fit text):
AutoFit(new SaMsExcelRange(0, firstHeadingRow));
}
SetRowValues(headingConfig.FirstHeadingColPos, firstHeadingRowPos, columnHeadings);
// Auto fit header row if text is wrapped:
if (headingConfig.HeadingTextWrap)
AutoFitRows(rangeHeadings);
}
catch (Exception exc) { HandleException("SetColumnHeadings", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return !HasErrorState();
}
//-------------------------------------------------------------------
// Cell attributes.
public abstract bool SetRangeAttr(SaMsExcelRange range, SaMsExcelItemAttr attr);
public abstract bool SetRangeAttr(SaMsExcelRange range, SaMsExcelStyleAttr attr);
//-------------------------------------------------------------------
// Add image to sheet.
public abstract bool InsertImage(SaMsExcelImage_Config imageConfig);
//-------------------------------------------------------------------
/// <summary>Freeze sheet at specified cell.</summary>
public abstract bool FreezePanes(uint colPos, uint rowPos);
//-------------------------------------------------------------------
public abstract bool AutoFit(SaMsExcelRange range);
/// <summary>Auto-fit row heights to contents.</summary>
public abstract bool AutoFitRows(SaMsExcelRange range);
//-------------------------------------------------------------------
/// <summary>Select range of cells.</summary>
public abstract bool SelectRange(SaMsExcelRange range);
//-------------------------------------------------------------------
// Utilities.
/// <summary>
/// Convert cell range to Id.
/// <para>- Not fixed: "A9:AB34"</para>
/// <para>- Fixed cols: "$A9:$AB34"</para>
/// <para>- Fixed rows: "A$9:AB$34"</para>
/// <para>- Fixed: "$A$9:$AB$34"</para>
/// </summary>
public static String GetRangeId(SaMsExcelRange range, bool fixedCols = false, bool fixedRows = false)
{
// Not fixed: "A9:AB34"
// Fixed cols: "$A9:$AB34"
// Fixed rows: "A$9:AB$34"
// Fixed: "$A$9:$AB$34"
return (fixedCols ? "$" : "") + GetColumnPrefix(range.ColFrom) + (fixedRows ? "$" : "") + GetRowSuffix(range.RowFrom) + ":" + (fixedCols ? "$" : "") + GetColumnPrefix(range.ColTo) + (fixedRows ? "$" : "") + GetRowSuffix(range.RowTo);
return GetColumnIdPrefix(range.ColFromPos, fixedCols) + GetRowIdSuffix(range.RowFromPos, fixedRows) + ":" + GetColumnIdPrefix(range.ColToPos, fixedCols) + GetRowIdSuffix(range.RowToPos, fixedRows);
}
public static String GetColumnPrefix(uint colPos)
/// <summary>
/// Convert column position to Id prefix. 1=A,2=B,3=C...26=Z,27=AA,28=AB etc.
/// <para>- Fixed prefixes with '$'.</para>
/// </summary>
public static String GetColumnIdPrefix(uint colPos, bool fixedCol = false)
{
// 1=A,2=B,3=C...26=Z,27=AA,28=AB etc
uint dividend = colPos;
string columnName = String.Empty;
uint modulo = 0;
@@ -454,35 +580,51 @@ namespace Sarand.Net.Standard.Proc.Excel
columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
dividend = (uint)((dividend - modulo) / 26);
}
return columnName;
return (fixedCol ? "$" : "") + columnName;
}
public static int GetIndexForCol(String column)
/// <summary>Convert column Id to position. A=1,B=2,C=3...Z=26,AA=27 etc.</summary>
public static int GetPosForColId(String colId)
{
// A=1,B=2,C=3...Z=26,AA=27 etc
int col = (column[0] - 'A');
if (column.Length > 1)
col = (column[1] * 26) + column[0];
colId = StringUtil.TrimWhitespace(StringUtil.OnBlank(colId)).ToUpper();
if (!colId.Matches(new Regex("[A-Z]+")))
{
SaLogger.Warn(SysUtil.Fn + " Invalid column Id supplied: '" + colId + "'");
return -1;
}
int col = (colId[0] - 'A');
if (colId.Length > 1)
col = ((colId[1] - 'A') * 26) + (colId[0] - 'A');
return col;
}
/// <summary>Column Id - To reference entire column: A:A,B:A,C:A...Z:Z,AA:AA etc.</summary>
public static String GetColumnId(uint colPos)
{
// To reference entire column: A:A,B:A,C:A...Z:Z,AA:AA etc
return GetColumnPrefix(colPos) + ":" + GetColumnPrefix(colPos);
return GetColumnIdPrefix(colPos) + ":" + GetColumnIdPrefix(colPos);
}
public static String GetRowSuffix(uint rowPos)
/// <summary>
/// Convert row pos to id suffix. 1=1,2=2,3=3...26=26,27=27,28=28 etc.
/// <para>- Fixed prefixes with '$'.</para>
/// </summary>
public static String GetRowIdSuffix(uint rowPos, bool fixedRow = false)
{
// 1,2,3...26,27,28 etc
return (rowPos > 0 ? rowPos.ToString() : "");
return (rowPos > 0 ? (fixedRow ? "$" : "") + rowPos.ToString() : "");
}
/// <summary>Row id - To reference entire row: 1:1,2:2,3:3 etc.</summary>
public static String GetRowId(uint rowPos)
{
// To reference entire row: 1:1,2:2,3:3 etc
return GetRowSuffix(rowPos) + ":" + GetRowSuffix(rowPos);
return GetRowIdSuffix(rowPos) + ":" + GetRowIdSuffix(rowPos);
}
/// <summary>Convert cell by column and row positions to Id.</summary>
public static String GetCellId(uint colPos, uint rowPos)
{
return GetColumnPrefix(colPos) + GetRowSuffix(rowPos);
return GetColumnIdPrefix(colPos) + GetRowIdSuffix(rowPos);
}
public static DateTime ExtractDate(String value)
{
DateTime date = DateTime.MinValue;
@@ -502,7 +644,7 @@ namespace Sarand.Net.Standard.Proc.Excel
//-------------------------------------------------------------------
public static double PointsPerInch { get; } = 72;
public static double PixelsPerInch { get; } = 96;
public static double CmsPerInch { get; } = 2.54f;
public static double CmsPerInch { get; } = 2.54;
public double ConvertLength(double fLength, SaExcelLogicalUnit eUnitFrom, SaExcelLogicalUnit eUnitTo, bool bVertical)
{
// Convert length to points:
@@ -559,7 +701,7 @@ namespace Sarand.Net.Standard.Proc.Excel
//-------------------------------------------------------------------
protected void HandleException(String fn, Exception exc)
{
SaLogger.Error(GetType().Name + "." + fn + " " + ExceptionUtil.GetLogMessage(exc));
SaLogger.Error(fn + " " + ExceptionUtil.GetLogMessage(exc));
SetErrorState("Problems were encountered interfacing to Excel - " + ExceptionUtil.GetErrorStateMessage(exc));
}
@@ -18,7 +18,7 @@ namespace Sarand.Net.Standard.Proc.Excel
/// <summary>Style properties retrieved directly from spreadsheet xml data.</summary>
public class SaMsExcelStyle
{
public SaMsExcelStyle () { }
public SaMsExcelStyle() { }
public int NumFmtId { get; set; }
public int FontId { get; set; }
public int FillId { get; set; }
@@ -51,7 +51,7 @@ namespace Sarand.Net.Standard.Proc.Excel
internal SaMsXmlDocument StylesDoc { get; set; }
internal uint WorksheetCount { get; set; }
internal uint ActiveSheetPos { get; set; }
internal bool IsValid () { return (WorkbookDoc != null && WorkbookDoc.GetRootElement() != null); }
internal bool IsValid() { return (WorkbookDoc != null && WorkbookDoc.GetRootElement() != null); }
}
private Workbook workbook;
private class Worksheet
@@ -71,17 +71,17 @@ namespace Sarand.Net.Standard.Proc.Excel
internal SaMsXmlElement ActiveRow { get; set; }
}
internal NodesData Nodes { get; } = new NodesData();
bool IsValid () { return (WorksheetDoc != null && WorksheetDoc.GetRootElement() != null); }
bool IsValid() { return (WorksheetDoc != null && WorksheetDoc.GetRootElement() != null); }
}
private Worksheet worksheet;
//-------------------------------------------------------------------
// Construction.
public SaMsSpreadsheet_Direct () : base()
public SaMsSpreadsheet_Direct() : base()
{
}
protected override void Dispose (bool disposing)
protected override void Dispose(bool disposing)
{
if (disposing)
{
@@ -96,11 +96,11 @@ namespace Sarand.Net.Standard.Proc.Excel
//-------------------------------------------------------------------
// W o r k b o o k
//-------------------------------------------------------------------
public override bool IsWorkbookValid ()
public override bool IsWorkbookValid()
{
return (workbook != null && workbook.IsValid());
}
public override bool CreateWorkbook ()
public override bool CreateWorkbook()
{
CloseWorkbook();
ClearErrorState();
@@ -113,7 +113,7 @@ namespace Sarand.Net.Standard.Proc.Excel
return SetErrorState("Creating workbook not supported");
}
public override bool OpenWorkbook (String filePath)
public override bool OpenWorkbook(String filePath)
{
CloseWorkbook();
ClearErrorState();
@@ -138,7 +138,7 @@ namespace Sarand.Net.Standard.Proc.Excel
catch (Exception exc) { SetErrorState(ExceptionUtil.GetErrorStateMessage(exc)); }
return !HasErrorState();
}
public bool OpenWorkbook (byte[] workbookData)
public bool OpenWorkbook(byte[] workbookData)
{
CloseWorkbook();
ClearErrorState();
@@ -152,7 +152,7 @@ namespace Sarand.Net.Standard.Proc.Excel
catch (Exception exc) { SetErrorState(ExceptionUtil.GetErrorStateMessage(exc)); }
return !HasErrorState();
}
public bool OpenWorkbook (Stream workbookStream)
public bool OpenWorkbook(Stream workbookStream)
{
CloseWorkbook();
ClearErrorState();
@@ -172,7 +172,7 @@ namespace Sarand.Net.Standard.Proc.Excel
catch (Exception exc) { SetErrorState(ExceptionUtil.GetErrorStateMessage(exc)); }
return !HasErrorState();
}
private bool ExtractWorkbookElementsFromArchive ()
private bool ExtractWorkbookElementsFromArchive()
{
ClearErrorState();
try
@@ -214,19 +214,22 @@ namespace Sarand.Net.Standard.Proc.Excel
catch (Exception exc) { SetErrorState(ExceptionUtil.GetErrorStateMessage(exc)); }
return !HasErrorState();
}
public override bool SaveDocument (String filePath, int fileFormat, bool bAutomaticallyOverwrite)
public override bool CleanseWorkbook()
{
throw new NotSupportedException(SysUtil.Fn);
}
public override bool SaveDocument(String filePath, int fileFormat, bool bAutomaticallyOverwrite)
{
ClearErrorState();
if (fileFormat == 0)
fileFormat = (int)XlFileFormat.xlOpenXMLWorkbook;
if (fileFormat != (int)XlFileFormat.xlOpenXMLWorkbook)
SetErrorState("Unsupported file format: " + ((XlFileFormat)fileFormat).ToString());
if (!HasErrorState() && !workbook.IsValid())
SetErrorState("Workbook is not open");
return SetErrorState("Unsupported file format: " + ((XlFileFormat)fileFormat).ToString());
if (!IsWorkbookValid()) SetErrorState(Msg_WorkbookNotOpen);
SetErrorState("Saving workbook not supported");
return !HasErrorState();
}
public override bool CloseWorkbook ()
public override bool CloseWorkbook()
{
ClearErrorState();
CloseWorksheet();
@@ -247,43 +250,60 @@ namespace Sarand.Net.Standard.Proc.Excel
return !HasErrorState();
}
//-------------------------------------------------------------------
// W o r k s h e e t
//-------------------------------------------------------------------
public override bool IsWorksheetValid ()
/// <summary>Extract the document as a byte array.</summary>
/// <returns>Document data as byte array.</returns>
public override byte[] GetDocumentData()
{
return (worksheet != null && worksheet.WorksheetDoc != null);
throw new NotSupportedException(SysUtil.Fn);
}
// Default style.
public override void ApplyDefaultStyle(SaMsExcelStyleAttr attr)
{
throw new NotSupportedException(SysUtil.Fn);
}
//-------------------------------------------------------------------
// W o r k s h e e t s
//-------------------------------------------------------------------
public override uint WorksheetCount { get { return workbook.WorksheetCount; } }
private void PopulateWorksheetCount ()
private void PopulateWorksheetCount()
{
if (!IsWorkbookValid()) return;
try
{
// Count worksheets in workbook.
SaMsXmlElement nodeWorkbook = workbook.WorkbookDoc.GetRootElement();
SaMsXmlElement nodeSheets = nodeWorkbook.GetFirstChildElement("sheets");
SaMsXmlElement nodeSheets = nodeWorkbook.GetFirstChildElement("sheets");
workbook.WorksheetCount = (uint)nodeSheets.GetChildElements("sheet").Count;
}
catch (Exception exc) { HandleException("GetWorksheetCount", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
}
public override bool SetWorksheetCount (int worksheetCount)
public override bool SetWorksheetCount(int worksheetCount)
{
ClearErrorState();
return SetErrorState("Modifying workbook not supported");
}
public override bool AddWorksheet(String name)
//-------------------------------------------------------------------
public override List<String> WorksheetNames
{
return SetErrorState("Modifying workbook not supported");
get
{
List<String> names = new List<String>();
return names;
}
}
public override bool RemoveWorksheet(uint worksheetPos)
{
return SetErrorState("Modifying workbook not supported");
}
public override bool OpenWorksheet (uint worksheetPos)
//-------------------------------------------------------------------
/// <summary>
/// Open worksheet by position (from 1).
/// </summary>
public override bool OpenWorksheet(uint worksheetPos)
{
ClearErrorState();
if (!workbook.IsValid()) return SetErrorState("Workbook is not open");
if (!IsWorkbookValid()) return SetErrorState(Msg_WorkbookNotOpen);
if (worksheetPos > WorksheetCount) return SetErrorState("Invalid sheet specified");
if (worksheetPos <= 0) worksheetPos = Math.Max(1, workbook.ActiveSheetPos);
CloseWorksheet();
@@ -292,7 +312,7 @@ namespace Sarand.Net.Standard.Proc.Excel
worksheet = new Worksheet();
// Locate worksheet in workbook by 0-index.
SaMsXmlElement workbookElement = workbook.WorkbookDoc.GetRootElement();
SaMsXmlElement sheetsElement = workbookElement.GetFirstChildElement("sheets");
SaMsXmlElement sheetsElement = workbookElement.GetFirstChildElement("sheets");
long sheetCount = 0;
foreach (SaMsXmlElement sheetElement in sheetsElement.GetChildElements("sheet"))
{
@@ -329,20 +349,70 @@ namespace Sarand.Net.Standard.Proc.Excel
}
if (worksheet.WorksheetDoc == null) return SetErrorState("Unable to load worksheet " + worksheetPos.ToString());
}
catch (Exception exc) { HandleException("OpenWorksheet", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return !HasErrorState();
}
public override uint WorksheetPos { get { return workbook.ActiveSheetPos; } }
public override String WorksheetName
//-------------------------------------------------------------------
/// <summary>
/// Open worksheet by position (from 1).
/// </summary>
public override bool OpenWorksheet(String name)
{
get { return ""; }
set { }
ClearErrorState();
if (!IsWorkbookValid()) return SetErrorState(Msg_WorkbookNotOpen);
CloseWorksheet();
name = StringUtil.TrimWhitespace(name);
try
{
worksheet = new Worksheet();
// Locate worksheet in workbook by 0-index.
SaMsXmlElement workbookElement = workbook.WorkbookDoc.GetRootElement();
SaMsXmlElement sheetsElement = workbookElement.GetFirstChildElement("sheets");
uint worksheetPos = 0;
foreach (SaMsXmlElement sheetElement in sheetsElement.GetChildElements("sheet"))
{
worksheetPos++;
String sheetName = sheetElement.GetAttributeValue("name");
if (StringUtil.TrimWhitespace(sheetName).EqualsCI(name))
{
// Extract the worksheet data.
uint sheetId = (uint)StringUtil.ToInt(sheetElement.GetAttributeValue("sheetId"));
String worksheetArchivePath = "xl/worksheets/sheet" + worksheetPos.ToString() + ".xml";
using (MemoryStream worksheetFile = workbook.WorkbookArchive.ExtractFileToStream(worksheetArchivePath))
{
if (worksheetFile == null) return SetErrorState("Failed to load worksheet data");
worksheet.WorksheetDoc = new SaMsXmlDocument();
if (worksheet.WorksheetDoc.LoadXml(worksheetFile))
{
workbook.ActiveSheetPos = worksheetPos;
worksheet.WorksheetPos = worksheetPos;
worksheet.WorksheetId = sheetId;
worksheet.WorksheetArchivePath = worksheetArchivePath;
worksheet.WorksheetName = sheetName;
worksheet.ActiveColPos = 1;
worksheet.ActiveRowPos = 1;
SaMsXmlElement nodeWorksheet = worksheet.WorksheetDoc.GetRootElement();
worksheet.Nodes.Cols = nodeWorksheet.GetFirstChildElement("cols");
worksheet.Nodes.ActiveCol = worksheet.Nodes.Cols.GetFirstChildElement("col");
worksheet.Nodes.Rows = nodeWorksheet.GetFirstChildElement("sheetData");
worksheet.Nodes.ActiveRow = worksheet.Nodes.Rows.GetFirstChildElement("row");
workbook.ActiveSheetPos = worksheetPos;
}
else return SetErrorState("Failed to read data for worksheet " + worksheetPos.ToString());
}
break;
}
}
if (worksheet.WorksheetDoc == null) return SetErrorState("Unable to load worksheet " + worksheetPos.ToString());
}
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return !HasErrorState();
}
public override bool SetWorksheetAttr (SaMsExcelItemAttr attr)
{
return SetErrorState("Not supported");
}
public override bool CloseWorksheet ()
//-------------------------------------------------------------------
public override bool CloseWorksheet()
{
ClearErrorState();
@@ -364,24 +434,51 @@ namespace Sarand.Net.Standard.Proc.Excel
}
//-------------------------------------------------------------------
// Worksheet Page Setup.
public override bool SetPageSetup (SaMsExcelPageSetup_Config pageSetupConfig)
public override bool AddWorksheet(String name)
{
return SetErrorState("Not supported");
return SetErrorState("Modifying workbook not supported");
}
public override bool SetPageBreak (uint col, uint row)
public override bool RemoveWorksheet(uint worksheetPos)
{
return SetErrorState("Not supported");
return SetErrorState("Modifying workbook not supported");
}
//-------------------------------------------------------------------
// W o r k s h e e t
//-------------------------------------------------------------------
public override bool IsWorksheetValid()
{
return (worksheet != null && worksheet.WorksheetDoc != null);
}
//-------------------------------------------------------------------
public override uint WorksheetPos { get { return workbook.ActiveSheetPos; } }
public override String WorksheetName
{
get { return ""; }
set { }
}
public override bool SetWorksheetAttr(SaMsExcelStyleAttr attr)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
// Worksheet Page Setup.
public override bool SetPageSetup(SaMsExcelPageSetup_Config pageSetupConfig)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
public override bool SetPageBreak(uint col, uint row)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
// C o l u m n s
//-------------------------------------------------------------------
public override uint GetActiveColPos ()
{
return worksheet.ActiveColPos;
}
private SaMsXmlElement GetColElement (uint colPos)
public override uint ActiveColPos { get { return worksheet.ActiveColPos; } }
private SaMsXmlElement GetColElement(uint colPos)
{
ClearErrorState();
try
@@ -399,10 +496,10 @@ namespace Sarand.Net.Standard.Proc.Excel
}
}
}
catch (Exception exc) { HandleException("GetColElement", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return new SaMsXmlElement(null);
}
public override double GetColWidth (uint colPos, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point)
public override double GetColWidth(uint colPos, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point)
{
ClearErrorState();
try
@@ -416,22 +513,35 @@ namespace Sarand.Net.Standard.Proc.Excel
if (unit == SaExcelLogicalUnit.Inch) return PointsToInches(width);
return width;
}
catch (Exception exc) { HandleException("GetColWidth", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return 0.0;
}
public override bool SetColWidth (uint colPos, double fWidth, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point)
public override bool SetColWidth(uint colPos, double fWidth, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point)
{
return SetErrorState("Not supported");
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
// R o w s
//-------------------------------------------------------------------
public override uint GetActiveRowPos ()
public override uint FirstRowPos
{
return worksheet.ActiveRowPos;
get
{
SaMsXmlElement rowElement = worksheet.Nodes.Rows.GetChildElements("row").First();
return (uint)(rowElement?.GetAttributeValue("r").ToLong() ?? 0);
}
}
SaMsXmlElement GetRowElement (uint rowPos)
public override uint LastRowPos
{
get
{
SaMsXmlElement rowElement = worksheet.Nodes.Rows.GetChildElements("row").Last();
return (uint)(rowElement?.GetAttributeValue("r").ToLong() ?? 0);
}
}
public override uint ActiveRowPos { get { return worksheet.ActiveRowPos; } }
SaMsXmlElement GetRowElement(uint rowPos)
{
ClearErrorState();
try
@@ -448,10 +558,10 @@ namespace Sarand.Net.Standard.Proc.Excel
}
}
}
catch (Exception exc) { HandleException("GetRowElement", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return new SaMsXmlElement(null);
}
public override double GetRowHeight (uint rowPos, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point)
public override double GetRowHeight(uint rowPos, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point)
{
ClearErrorState();
try
@@ -465,18 +575,18 @@ namespace Sarand.Net.Standard.Proc.Excel
if (unit == SaExcelLogicalUnit.Inch) return PointsToInches(height);
return height;
}
catch (Exception exc) { HandleException("GetRowHeight", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return 0.0;
}
public override bool SetRowHeight (uint rowPos, double fHeight, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point)
public override bool SetRowHeight(uint rowPos, double fHeight, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point)
{
return SetErrorState("Not supported");
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
// R o w s
//-------------------------------------------------------------------
public override List<DataValue> GetRowDataValues (uint rowPos, int maxColumns = 0)
public override List<DataValue> GetRowValues(uint rowPos, int maxColumns = 0)
{
ClearErrorState();
List<DataValue> values = new List<DataValue>();
@@ -492,17 +602,17 @@ namespace Sarand.Net.Standard.Proc.Excel
if (maxColumns > 0 && values.Count >= maxColumns) break;
}
}
catch (Exception exc) { HandleException("GetRowDataValues", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return values;
}
//-------------------------------------------------------------------
// C e l l s
//-------------------------------------------------------------------
SaMsXmlElement GetCellElement (uint colPos, uint rowPos)
SaMsXmlElement GetCellElement(uint colPos, uint rowPos)
{
ClearErrorState();
String cellId = GetCellId(colPos,rowPos);
String cellId = GetCellId(colPos, rowPos);
try
{
SaMsXmlElement rowElement = GetRowElement(rowPos);
@@ -512,88 +622,99 @@ namespace Sarand.Net.Standard.Proc.Excel
return cellElement;
}
}
catch (Exception exc) { HandleException("GetCellElement", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return new SaMsXmlElement(null);
}
public override void SetActiveCell (uint colPos, uint rowPos)
public override void SetActiveCell(uint colPos, uint rowPos)
{
// ToDo SetActiveCell.
SetErrorState("Not supported");
SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
private bool IsCellFormula (uint colPos, uint rowPos)
private bool IsCellFormula(uint colPos, uint rowPos)
{
ClearErrorState();
bool bIsFormula = false;
try
{
SaMsXmlElement cellElement = GetCellElement(colPos,rowPos);
SaMsXmlElement cellElement = GetCellElement(colPos, rowPos);
bIsFormula = !cellElement.GetAttributeValue("f").IsEmpty();
}
catch (Exception exc) { HandleException("IsCellFormula", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return bIsFormula;
}
private bool IsCellNumeric (uint colPos, uint rowPos)
private bool IsCellNumeric(uint colPos, uint rowPos)
{
ClearErrorState();
try
{
SaMsXmlElement cellElement = GetCellElement(colPos,rowPos);
SaMsExcelStyle cellStyle = GetCellStyle(cellElement);
SaMsXmlElement cellElement = GetCellElement(colPos, rowPos);
SaMsExcelStyle cellStyle = GetCellStyle(cellElement);
return IsCellNumeric(cellElement, cellStyle);
}
catch (Exception exc) { HandleException("IsCellNumeric", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return false;
}
private bool IsCellNumeric (SaMsXmlElement cellElement)
private bool IsCellNumeric(SaMsXmlElement cellElement)
{
ClearErrorState();
SaMsExcelStyle cellStyle = GetCellStyle(cellElement);
SaMsExcelStyle cellStyle = GetCellStyle(cellElement);
return IsCellNumeric(cellElement, cellStyle);
}
private bool IsCellNumeric (SaMsXmlElement cellElement, SaMsExcelStyle cellStyle)
private bool IsCellNumeric(SaMsXmlElement cellElement, SaMsExcelStyle cellStyle)
{
ClearErrorState();
return (cellElement.GetAttributeValue("t").IsEmpty() && !IsDateTimeStyle(cellStyle.NumFmtCode));
}
private bool IsCellDate (uint colPos, uint rowPos)
private bool IsCellDate(uint colPos, uint rowPos)
{
ClearErrorState();
bool bIsDate = false;
try
{
SaMsXmlElement cellElement = GetCellElement(colPos,rowPos);
SaMsExcelStyle cellStyle = GetCellStyle(cellElement);
SaMsXmlElement cellElement = GetCellElement(colPos, rowPos);
SaMsExcelStyle cellStyle = GetCellStyle(cellElement);
return IsCellDate(cellElement, cellStyle);
}
catch (Exception exc) { HandleException("IsCellDate", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return bIsDate;
}
private bool IsCellDate (SaMsXmlElement cellElement)
private bool IsCellDate(SaMsXmlElement cellElement)
{
return IsCellDate(cellElement, GetCellStyle(cellElement));
}
private bool IsCellDate (SaMsXmlElement cellElement, SaMsExcelStyle cellStyle)
private bool IsCellDate(SaMsXmlElement cellElement, SaMsExcelStyle cellStyle)
{
ClearErrorState();
return IsDateTimeStyle(cellStyle.NumFmtCode);
}
//-------------------------------------------------------------------
public override String GetCellFormula (uint colPos, uint rowPos)
public override String GetCellFormula(uint colPos, uint rowPos)
{
ClearErrorState();
try
{
SaMsXmlElement cellElement = GetCellElement(colPos,rowPos);
SaMsXmlElement cellElement = GetCellElement(colPos, rowPos);
String formula = cellElement.GetAttributeValue("f");
if (formula.IsEmpty())
formula = GetCellText(cellElement, true);
return formula;
}
catch (Exception exc) { HandleException("GetCellFormula", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return "";
}
private String GetCellText (SaMsXmlElement cellElement, bool trimValue = true)
public override String GetCellText(uint colPos, uint rowPos, bool trimWhitespace = true)
{
ClearErrorState();
try
{
SaMsXmlElement cellElement = GetCellElement(colPos, rowPos);
return GetCellText(cellElement, true);
}
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return "";
}
private String GetCellText(SaMsXmlElement cellElement, bool trimValue = true)
{
ClearErrorState();
String value = "";
@@ -605,14 +726,14 @@ namespace Sarand.Net.Standard.Proc.Excel
value = cellElement.GetChildText("v");
if (trimValue) value.TrimAll();
}
catch (Exception exc) { HandleException("GetCellText", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return value;
}
public override DataValue GetCellDataValue (uint colPos, uint rowPos)
public override DataValue GetCellValue(uint colPos, uint rowPos)
{
return GetCellDataValue(GetCellElement(colPos, rowPos), false);
}
private DataValue GetCellDataValue (SaMsXmlElement cellElement, bool trimTextValue = true)
private DataValue GetCellDataValue(SaMsXmlElement cellElement, bool trimTextValue = true)
{
ClearErrorState();
try
@@ -645,12 +766,12 @@ namespace Sarand.Net.Standard.Proc.Excel
return new DataValue(value);
}
}
catch (Exception exc) { HandleException("GetCellDataValue", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return new DataValue();
}
//-------------------------------------------------------------------
// Shared string for cell.
private String GetSharedString (SaMsXmlElement nodeElement)
private String GetSharedString(SaMsXmlElement nodeElement)
{
ClearErrorState();
try
@@ -658,10 +779,10 @@ namespace Sarand.Net.Standard.Proc.Excel
SaMsXmlElement sharedStringElement = GetSharedStringElement(nodeElement);
return sharedStringElement.GetChildText("t");
}
catch (Exception exc) { HandleException("GetSharedString", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return "";
}
private SaMsXmlElement GetSharedStringElement (SaMsXmlElement cellElement)
private SaMsXmlElement GetSharedStringElement(SaMsXmlElement cellElement)
{
ClearErrorState();
if (workbook.SharedStringsDoc == null || cellElement == null || !cellElement.Valid) return null;
@@ -678,31 +799,23 @@ namespace Sarand.Net.Standard.Proc.Excel
return siElement;
}
}
catch (Exception exc) { HandleException("GetSharedStringNode", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return new SaMsXmlElement(null);
}
//-------------------------------------------------------------------
public override bool SetCellFormula (uint colPos, uint rowPos, String value)
public override bool SetCellFormula(uint colPos, uint rowPos, String value, SaMsExcelStyleAttr attr)
{
return SetErrorState("Not supported");
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
public override bool SetCellFormula (uint colPos, uint rowPos, String value, SaMsExcelItemAttr attr)
public override bool SetCellValue(uint colPos, uint rowPos, DataValue value, SaMsExcelStyleAttr attr)
{
return SetErrorState("Not supported");
}
public override bool SetRowFormulas (uint colPosStart, uint rowPos, List<Object> values)
{
return SetErrorState("Not supported");
}
public override bool SetRangeFormulas (SaMsExcelRange range, List<Object> values)
{
return SetErrorState("Not supported");
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
// C e l l S t y l e
//-------------------------------------------------------------------
private String GetBuiltInStyleFormat (int numFmtId)
private String GetBuiltInStyleFormat(int numFmtId)
{
// Ref. http://stackoverflow.com/questions/4730152/what-indicates-an-office-open-xml-cell-contains-a-date-time-value
switch (numFmtId)
@@ -758,7 +871,7 @@ namespace Sarand.Net.Standard.Proc.Excel
}
return "";
}
private SaMsExcelStyle GetCellStyle (SaMsXmlElement cellElement)
private SaMsExcelStyle GetCellStyle(SaMsXmlElement cellElement)
{
ClearErrorState();
SaMsExcelStyle style = new SaMsExcelStyle();
@@ -769,7 +882,7 @@ namespace Sarand.Net.Standard.Proc.Excel
{
long cellStyleIdx = cellElement.GetAttributeValue("s").ToLong();
SaMsXmlElement styleSheetElement = workbook.SharedStringsDoc.GetRootElement();
SaMsXmlElement cellXfsElement = styleSheetElement.GetFirstChildElement("cellXfs");
SaMsXmlElement cellXfsElement = styleSheetElement.GetFirstChildElement("cellXfs");
foreach (SaMsXmlElement xfElement in styleSheetElement.GetChildElements("xf"))
{
if (++styleIdx == cellStyleIdx)
@@ -845,10 +958,10 @@ namespace Sarand.Net.Standard.Proc.Excel
}
}
}
catch (Exception exc) { HandleException("GetStyleNode", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return style;
}
private bool IsDateStyle (String numFormat)
private bool IsDateStyle(String numFormat)
{
// Ref. http://stackoverflow.com/questions/19034805/how-to-distinguish-inline-numbers-from-ole-automation-date-numbers-in-openxml-sp/19582685#19582685
if (IsTimeStyle(numFormat)) return false;
@@ -857,7 +970,7 @@ namespace Sarand.Net.Standard.Proc.Excel
if (numFormat.Contains("y")) return true;
return false;
}
private bool IsTimeStyle (String numFormat)
private bool IsTimeStyle(String numFormat)
{
if (numFormat.Contains("h")) return true;
if (numFormat.Contains(":mm")) return true;
@@ -865,41 +978,41 @@ namespace Sarand.Net.Standard.Proc.Excel
if (numFormat.Contains(":ss")) return true;
return false;
}
private bool IsDateTimeStyle (String numFormat)
private bool IsDateTimeStyle(String numFormat)
{
return (IsDateStyle(numFormat) || IsTimeStyle(numFormat));
}
//-------------------------------------------------------------------
// Cell attributes.
public override bool SetRangeAttr (SaMsExcelRange range, SaMsExcelItemAttr attr)
public override bool SetRangeAttr(SaMsExcelRange range, SaMsExcelStyleAttr attr)
{
return SetErrorState("Not supported");
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
// Add image to sheet.
public override bool InsertImage (SaMsExcelImage_Config imageConfig)
public override bool InsertImage(SaMsExcelImage_Config imageConfig)
{
return SetErrorState("Not supported");
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
public override bool FreezePanes (uint colPos, uint rowPos)
public override bool FreezePanes(uint colPos, uint rowPos)
{
return SetErrorState("Not supported");
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
public override bool AutoFit (SaMsExcelRange range)
public override bool AutoFitRows(SaMsExcelRange range)
{
return SetErrorState("Not supported");
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
public override bool SelectRange (SaMsExcelRange range)
public override bool SelectRange(SaMsExcelRange range)
{
return SetErrorState("Not supported");
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
@@ -1,15 +1,13 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
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.Linq;
namespace Sarand.Net.Standard.Proc.Excel
{
@@ -118,6 +116,10 @@ namespace Sarand.Net.Standard.Proc.Excel
catch (Exception exc) { SaLogger.Warn(exc.GetType().Name + ".OpenWorkbook " + ExceptionUtil.GetErrorStateMessage(exc)); }
return (workbook != null);
}
public override bool CleanseWorkbook()
{
throw new NotSupportedException(SysUtil.Fn);
}
public override bool SaveDocument(String filePath, int fileFormat, bool bAutomaticallyOverwrite)
{
ClearErrorState();
@@ -156,13 +158,22 @@ namespace Sarand.Net.Standard.Proc.Excel
return !HasErrorState();
}
//-------------------------------------------------------------------
// W o r k s h e e t
//-------------------------------------------------------------------
public override bool IsWorksheetValid()
/// <summary>Extract the document as a byte array.</summary>
/// <returns>Document data as byte array.</returns>
public override byte[] GetDocumentData()
{
return (worksheet != null);
throw new NotSupportedException(SysUtil.Fn);
}
// Default style.
public override void ApplyDefaultStyle(SaMsExcelStyleAttr attr)
{
throw new NotSupportedException(SysUtil.Fn);
}
//-------------------------------------------------------------------
// W o r k s h e e t s
//-------------------------------------------------------------------
public override uint WorksheetCount
{
get
@@ -187,6 +198,11 @@ namespace Sarand.Net.Standard.Proc.Excel
else SetErrorState("SetWorksheetCount - Requested number of worksheets (" + worksheetCount.ToString() + ") must be between 1 and 10");
return !HasErrorState();
}
//-------------------------------------------------------------------
public override List<String> WorksheetNames { get { return new List<String>(); } }
//-------------------------------------------------------------------
public override bool AddWorksheet(String sheetName)
{
ClearErrorState();
@@ -214,6 +230,8 @@ namespace Sarand.Net.Standard.Proc.Excel
return (worksheet != null);
}
//-------------------------------------------------------------------
public override bool RemoveWorksheet(uint worksheetPos)
{
ClearErrorState();
@@ -243,9 +261,12 @@ namespace Sarand.Net.Standard.Proc.Excel
}
return false;
}
//-------------------------------------------------------------------
// W o r k s h e e t
//-------------------------------------------------------------------
public override bool OpenWorksheet(uint worksheetPos)
{
String fn = GetType().Name + ".OpenWorksheet";
ClearErrorState();
CloseWorksheet();
@@ -267,31 +288,49 @@ namespace Sarand.Net.Standard.Proc.Excel
if (worksheetPart != null && sheet != null)
worksheet = new WorksheetData(worksheetPos, worksheetPart, sheet);
}
if (worksheet != null)
{
SaLogger.Warn(fn + " Failed to open worksheet " + worksheetPos);
SaLogger.Warn(SysUtil.Fn + " Failed to open worksheet " + worksheetPos);
SetErrorState("Failed to open worksheet " + worksheetPos);
}
return (worksheet != null);
}
public override uint WorksheetPos { get { return activeWorksheetPos; } }
public override String WorksheetName
{
get { return worksheet.Sheet.Name; }
set { worksheet.Sheet.Name = StringUtil.Left(value, 31); }
}
public override bool SetWorksheetAttr(SaMsExcelItemAttr attr)
public override bool OpenWorksheet(String name)
{
ClearErrorState();
try
CloseWorksheet();
name = StringUtil.TrimWhitespace(name);
// Extract Sheet by position.
uint worksheetPos = 0;
Sheet sheet = null;
foreach (Sheet sheetCandidate in Sheets)
{
// ToDo OpenXml SetWorksheetAttr
++worksheetPos;
if (StringUtil.TrimWhitespace(sheetCandidate.Name).EqualsCI(name))
{
sheet = sheetCandidate;
break;
}
}
catch (Exception exc) { HandleException("SetWorksheetAttr", exc); }
return !HasErrorState();
if (sheet != null)
{
WorksheetPart worksheetPart = (WorksheetPart)(WorkbookPart.GetPartById(sheet.Id));
if (worksheetPart != null && sheet != null)
worksheet = new WorksheetData(worksheetPos, worksheetPart, sheet);
}
if (worksheet != null)
{
String msg = "Failed to open worksheet: " + name;
SetErrorState(msg);
SaLogger.Warn(SysUtil.Fn + " " + msg);
}
return (worksheet != null);
}
//-------------------------------------------------------------------
public override bool CloseWorksheet()
{
ClearErrorState();
@@ -303,12 +342,38 @@ namespace Sarand.Net.Standard.Proc.Excel
}
worksheet = null;
}
catch (Exception exc) { HandleException("CloseWorksheet", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
worksheet = null;
activeWorksheetPos = 0;
return !HasErrorState();
}
//-------------------------------------------------------------------
public override bool IsWorksheetValid()
{
return (worksheet != null);
}
//-------------------------------------------------------------------
public override uint WorksheetPos { get { return activeWorksheetPos; } }
public override String WorksheetName
{
get { return worksheet.Sheet.Name; }
set { worksheet.Sheet.Name = StringUtil.Left(value, 31); }
}
//-------------------------------------------------------------------
public override bool SetWorksheetAttr(SaMsExcelStyleAttr attr)
{
ClearErrorState();
try
{
// ToDo OpenXml SetWorksheetAttr
}
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return !HasErrorState();
}
//-------------------------------------------------------------------
// Worksheet Page Setup.
public override bool SetPageSetup(SaMsExcelPageSetup_Config pageSetupConfig)
@@ -428,7 +493,7 @@ namespace Sarand.Net.Standard.Proc.Excel
}
*/
}
catch (Exception exc) { HandleException("SetPageSetup", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return !HasErrorState();
}
public override bool SetPageBreak(uint colPos, uint rowPos)
@@ -445,7 +510,7 @@ namespace Sarand.Net.Standard.Proc.Excel
worksheet.VPageBreaks.Add(range);
*/
}
catch (Exception exc) { HandleException("SetPageSetup", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return !HasErrorState();
}
@@ -459,7 +524,7 @@ namespace Sarand.Net.Standard.Proc.Excel
//-------------------------------------------------------------------
// C o l u m n s
//-------------------------------------------------------------------
private Column GetColumnByPos (uint columnPos)
private Column GetColumnByPos(uint columnPos)
{
if (worksheet.ActiveColumnPos != columnPos)
{
@@ -468,15 +533,18 @@ namespace Sarand.Net.Standard.Proc.Excel
}
return worksheet.ActiveColumn;
}
public override uint GetActiveColPos()
public override uint ActiveColPos
{
if (worksheet == null) return 0;
try
get
{
return worksheet.ActiveColumnPos;
if (worksheet == null) return 0;
try
{
return worksheet.ActiveColumnPos;
}
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return 0;
}
catch (Exception exc) { HandleException("GetActiveCol", exc); }
return 0;
}
public override double GetColWidth(uint colPos, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point)
{
@@ -485,7 +553,7 @@ namespace Sarand.Net.Standard.Proc.Excel
{
// ToDo OpenXML GetColWidth.
}
catch (Exception exc) { HandleException("GetColWidth", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return columnWidth;
}
public override bool SetColWidth(uint colPos, double columnWidth, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point)
@@ -495,23 +563,30 @@ namespace Sarand.Net.Standard.Proc.Excel
{
// ToDo OpenXML SetColWidth.
}
catch (Exception exc) { HandleException("SetColWidth", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return !HasErrorState();
}
//-------------------------------------------------------------------
// R o w s
//-------------------------------------------------------------------
public override uint GetActiveRowPos()
public override uint FirstRowPos { get { return (uint)(0); } }
public override uint LastRowPos { get { return (uint)(0); } }
//-------------------------------------------------------------------
public override uint ActiveRowPos
{
try
get
{
// ToDo OpenXML GetActiveRowPos.
try
{
// ToDo OpenXML GetActiveRowPos.
}
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return 0;
}
catch (Exception exc) { HandleException("GetActiveRowPos", exc); }
return 0;
}
public override List<DataValue> GetRowDataValues(uint rowPos, int maxColumns = 0)
public override List<DataValue> GetRowValues(uint rowPos, int maxColumns = 0)
{
List<DataValue> values = new List<DataValue>();
return values;
@@ -523,7 +598,7 @@ namespace Sarand.Net.Standard.Proc.Excel
{
// ToDo OpenXML GetRowHeight.
}
catch (Exception exc) { HandleException("GetRowHeight", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return rowHeight;
}
public override bool SetRowHeight(uint rowPos, double rowHeight, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point)
@@ -533,7 +608,7 @@ namespace Sarand.Net.Standard.Proc.Excel
{
// ToDo OpenXML SetRowHeight.
}
catch (Exception exc) { HandleException("SetRowHeight", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return !HasErrorState();
}
@@ -549,52 +624,50 @@ namespace Sarand.Net.Standard.Proc.Excel
// ToDo OpenXML GetCellFormula.
return "";
}
public override DataValue GetCellDataValue(uint colPos, uint rowPos)
public override String GetCellText(uint colPos, uint rowPos, bool trimWhitespace = true)
{
// ToDo OpenXML GetCellText.
return "";
}
public override DataValue GetCellValue(uint colPos, uint rowPos)
{
// ToDo OpenXML GetCellDataValue.
return new DataValue();
}
public override bool SetCellFormula(uint colPos, uint rowPos, String value)
//-------------------------------------------------------------------
public override bool SetCellFormula(uint colPos, uint rowPos, String value, SaMsExcelStyleAttr attr)
{
// ToDo OpenXML SetCellFormula.
return true;
}
public override bool SetCellFormula(uint colPos, uint rowPos, String value, SaMsExcelItemAttr attr)
public override bool SetCellValue(uint colPos, uint rowPos, DataValue value, SaMsExcelStyleAttr attr)
{
// ToDo OpenXML SetCellFormula.
return true;
}
public override bool SetRowFormulas(uint colPosStart, uint rowPos, List<Object> values)
{
// ToDo OpenXML SetRowFormulas.
return true;
}
public override bool SetRangeFormulas(SaMsExcelRange range, List<Object> values)
{
// ToDo OpenXML SetRangeFormulas.
// ToDo OpenXML SetCellValue.
return true;
}
//-------------------------------------------------------------------
// Cell attributes.
public override bool SetRangeAttr(SaMsExcelRange range, SaMsExcelItemAttr attr)
public override bool SetRangeAttr(SaMsExcelRange range, SaMsExcelStyleAttr attr)
{
ClearErrorState();
try
{
// ToDo OpenXML SetRangeAttr.
}
catch (Exception exc) { HandleException("SetRangeAttr", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return !HasErrorState();
}
private bool SetRangeAttr(Range oRange, SaMsExcelItemAttr attr)
private bool SetRangeAttr(Range oRange, SaMsExcelStyleAttr attr)
{
ClearErrorState();
try
{
// ToDo OpenXML SetRangeAttr.
}
catch (Exception exc) { HandleException("SetRangeAttr", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return !HasErrorState();
}
@@ -610,7 +683,7 @@ namespace Sarand.Net.Standard.Proc.Excel
{
// ToDo OpenXML InsertImage.
}
catch (Exception exc) { HandleException("InsertImage", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return !HasErrorState();
}
@@ -622,19 +695,19 @@ namespace Sarand.Net.Standard.Proc.Excel
{
// ToDo OpenXML FreezePanes.
}
catch (Exception exc) { HandleException("FreezePanes", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return !HasErrorState();
}
//-------------------------------------------------------------------
public override bool AutoFit(SaMsExcelRange range)
public override bool AutoFitRows(SaMsExcelRange range)
{
ClearErrorState();
try
{
// ToDo OpenXML AutoFit.
}
catch (Exception exc) { HandleException("AutoFit", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return !HasErrorState();
}
@@ -646,7 +719,7 @@ namespace Sarand.Net.Standard.Proc.Excel
{
// ToDo OpenXML SelectRange.
}
catch (Exception exc) { HandleException("SelectRange", exc); }
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return !HasErrorState();
}
File diff suppressed because it is too large Load Diff
-16
View File
@@ -43,22 +43,6 @@ namespace Sarand.Net.Standard.Proc.Procs
// Utilities.
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// Run asynchronous.
//-------------------------------------------------------------------
public bool RunAsync()
{
// Run on separate thread.
Thread thread = new Thread(new ThreadStart(RunOnThread));
thread.IsBackground = true;
thread.Start();
return true;
}
private void RunOnThread()
{
RunProcess();
}
//-------------------------------------------------------------------
}
//=======================================================================
+2 -2
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Sarand.Net.Standard</RootNamespace>
</PropertyGroup>
@@ -11,7 +11,7 @@
<ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="2.12.3" />
<PackageReference Include="NPOI" Version="2.5.3" />
<PackageReference Include="NPOI" Version="2.6.2" />
</ItemGroup>
<ItemGroup>