unit uCommonUtils; interface uses SysUtils, Classes, System.JSON, System.Generics.Collections, System.UITypes, FMX.Forms, FMX.Controls, FMX.StdCtrls; type TValidationResult = record IsValid: Boolean; ErrorMessage: string; ErrorField: string; procedure Clear; class function Success: TValidationResult; static; class function Failure(const AErrorField, AErrorMessage: string): TValidationResult; static; end; TValidator = class public class function IsEmpty(const AValue: string): Boolean; static; class function IsInteger(const AValue: string): Boolean; static; class function IsFloat(const AValue: string): Boolean; static; class function IsDateTime(const AValue: string): Boolean; static; class function IsEmail(const AValue: string): Boolean; static; class function IsPhone(const AValue: string): Boolean; static; class function IsIPAddress(const AValue: string): Boolean; static; class function MinLength(const AValue: string; AMinLen: Integer): Boolean; static; class function MaxLength(const AValue: string; AMaxLen: Integer): Boolean; static; class function InRange(const AValue: string; AMin, AMax: Double): Boolean; static; class function ValidateBarcode(const ABarcode: string): TValidationResult; static; class function ValidateLotNo(const ALotNo: string): TValidationResult; static; class function ValidateMaterialCode(const AMaterialCode: string): TValidationResult; static; end; TStringHelperEx = class helper for TStringHelper public function IsInteger: Boolean; function IsFloat: Boolean; function ToIntegerDef(ADefault: Integer): Integer; function ToFloatDef(ADefault: Double): Double; function TrimLeft: string; function TrimRight: string; function RemoveWhitespace: string; function Contains(const ASubStr: string): Boolean; function StartsWith(const AStr: string): Boolean; function EndsWith(const AStr: string): Boolean; function ReplaceAll(const AFrom, ATo: string): string; end; TDateTimeHelper = class public class function FormatDateTime(const AFormat: string; ADateTime: TDateTime): string; static; class function ParseDateTime(const AValue: string): TDateTime; static; class function GetDateOnly(ADateTime: TDateTime): TDate; static; class function GetTimeOnly(ADateTime: TDateTime): TTime; static; class function StartOfDay(ADateTime: TDateTime): TDateTime; static; class function EndOfDay(ADateTime: TDateTime): TDateTime; static; class function GetAge(ABirthDate: TDateTime): Integer; static; class function IsToday(ADateTime: TDateTime): Boolean; static; class function IsSameDay(ADate1, ADate2: TDateTime): Boolean; static; end; TFileHelper = class public class function ReadTextFile(const AFileName: string; AEncoding: TEncoding = nil): string; static; class procedure WriteTextFile(const AFileName: string; const AContent: string; AEncoding: TEncoding = nil); static; class function FileExists(const AFileName: string): Boolean; static; class function GetFileSize(const AFileName: string): Int64; static; class function GetFileExtension(const AFileName: string): string; static; class function GetFileNameWithoutExtension(const AFileName: string): string; static; class procedure EnsureDirectoryExists(const ADirPath: string); static; class function GetTempFileName(const AExtension: string = '.tmp'): string; static; class function GetAppDataPath: string; static; end; TJSONHelper = class public class function GetString(AJSON: TJSONObject; const AKey: string; const ADefault: string = ''): string; static; class function GetInteger(AJSON: TJSONObject; const AKey: string; const ADefault: Integer = 0): Integer; static; class function GetFloat(AJSON: TJSONObject; const AKey: string; const ADefault: Double = 0.0): Double; static; class function GetBoolean(AJSON: TJSONObject; const AKey: string; const ADefault: Boolean = False): Boolean; static; class function GetJSONObject(AJSON: TJSONObject; const AKey: string): TJSONObject; static; class function GetJSONArray(AJSON: TJSONObject; const AKey: string): TJSONArray; static; class function TryGetString(AJSON: TJSONObject; const AKey: string; out AValue: string): Boolean; static; class function TryGetInteger(AJSON: TJSONObject; const AKey: string; out AValue: Integer): Boolean; static; class function ObjectToString(AJSON: TJSONObject): string; static; class function StringToObject(const AJSONString: string): TJSONObject; static; end; TUIHelper = class public class procedure ShowMessage(const AMessage: string); static; class function ShowMessageOKCancel(const AMessage: string): Boolean; static; class procedure ShowError(const AMessage: string); static; class procedure ShowInfo(const AMessage: string); static; class procedure ShowWarning(const AMessage: string); static; class procedure SetControlEnabled(AControl: TControl; AEnabled: Boolean); static; class procedure SetControlsEnabled(AControls: array of TControl; AEnabled: Boolean); static; class procedure UpdateUI(AControl: TControl; AProc: TProc); static; class function GetScreenScale: Single; static; end; implementation { TValidationResult } procedure TValidationResult.Clear; begin IsValid := True; ErrorMessage := ''; ErrorField := ''; end; class function TValidationResult.Success: TValidationResult; begin Result.Clear; end; class function TValidationResult.Failure(const AErrorField, AErrorMessage: string): TValidationResult; begin Result.IsValid := False; Result.ErrorField := AErrorField; Result.ErrorMessage := AErrorMessage; end; { TValidator } class function TValidator.IsEmpty(const AValue: string): Boolean; begin Result := Trim(AValue) = ''; end; class function TValidator.IsInteger(const AValue: string): Boolean; var I: Integer; begin Result := TryStrToInt(AValue, I); end; class function TValidator.IsFloat(const AValue: string): Boolean; var F: Double; begin Result := TryStrToFloat(AValue, F); end; class function TValidator.IsDateTime(const AValue: string): Boolean; var DT: TDateTime; begin Result := TryStrToDateTime(AValue, DT); end; class function TValidator.IsEmail(const AValue: string): Boolean; var I, J: Integer; begin Result := False; if IsEmpty(AValue) then Exit; I := Pos('@', AValue); if I = 0 then Exit; if Pos('.', Copy(AValue, I, Length(AValue))) = 0 then Exit; J := Length(AValue); if (I > 1) and (J > I) then Result := True; end; class function TValidator.IsPhone(const AValue: string): Boolean; var S: string; I: Integer; begin Result := False; S := ''; for I := 1 to Length(AValue) do begin if AValue[I] in ['0'..'9', '+', '-', '(', ')'] then S := S + AValue[I]; end; if Length(S) >= 7 then Result := True; end; class function TValidator.IsIPAddress(const AValue: string): Boolean; var Parts: TArray; I, Val: Integer; begin Result := False; Parts := AValue.Split(['.']); if Length(Parts) <> 4 then Exit; for I := 0 to 3 do begin if not TryStrToInt(Parts[I], Val) then Exit; if (Val < 0) or (Val > 255) then Exit; end; Result := True; end; class function TValidator.MinLength(const AValue: string; AMinLen: Integer): Boolean; begin Result := Length(AValue) >= AMinLen; end; class function TValidator.MaxLength(const AValue: string; AMaxLen: Integer): Boolean; begin Result := Length(AValue) <= AMaxLen; end; class function TValidator.InRange(const AValue: string; AMin, AMax: Double): Boolean; var F: Double; begin Result := False; if not TryStrToFloat(AValue, F) then Exit; Result := (F >= AMin) and (F <= AMax); end; class function TValidator.ValidateBarcode(const ABarcode: string): TValidationResult; begin Result := TValidationResult.Success; if IsEmpty(ABarcode) then Exit(TValidationResult.Failure('Barcode', 'Barcode cannot be empty')); if Length(ABarcode) < 8 then Exit(TValidationResult.Failure('Barcode', 'Barcode must be at least 8 characters')); if Length(ABarcode) > 50 then Exit(TValidationResult.Failure('Barcode', 'Barcode must not exceed 50 characters')); end; class function TValidator.ValidateLotNo(const ALotNo: string): TValidationResult; begin Result := TValidationResult.Success; if IsEmpty(ALotNo) then Exit(TValidationResult.Failure('LotNo', 'Lot number cannot be empty')); if Length(ALotNo) < 6 then Exit(TValidationResult.Failure('LotNo', 'Lot number must be at least 6 characters')); end; class function TValidator.ValidateMaterialCode(const AMaterialCode: string): TValidationResult; begin Result := TValidationResult.Success; if IsEmpty(AMaterialCode) then Exit(TValidationResult.Failure('MaterialCode', 'Material code cannot be empty')); if Length(AMaterialCode) < 3 then Exit(TValidationResult.Failure('MaterialCode', 'Material code must be at least 3 characters')); end; { TStringHelperEx } function TStringHelperEx.IsInteger: Boolean; begin Result := TValidator.IsInteger(Self.ToString); end; function TStringHelperEx.IsFloat: Boolean; begin Result := TValidator.IsFloat(Self.ToString); end; function TStringHelperEx.ToIntegerDef(ADefault: Integer): Integer; begin if not TryStrToInt(Self.ToString, Result) then Result := ADefault; end; function TStringHelperEx.ToFloatDef(ADefault: Double): Double; begin if not TryStrToFloat(Self.ToString, Result) then Result := ADefault; end; function TStringHelperEx.TrimLeft: string; begin Result := System.StrUtils.TrimLeft(Self.ToString); end; function TStringHelperEx.TrimRight: string; begin Result := System.StrUtils.TrimRight(Self.ToString); end; function TStringHelperEx.RemoveWhitespace: string; var I: Integer; begin Result := ''; for I := 1 to Length(Self.ToString) do begin if not CharInSet(Self.ToString[I], [' ', #9, #10, #13]) then Result := Result + Self.ToString[I]; end; end; function TStringHelperEx.Contains(const ASubStr: string): Boolean; begin Result := Pos(ASubStr, Self.ToString) > 0; end; function TStringHelperEx.StartsWith(const AStr: string): Boolean; begin Result := Copy(Self.ToString, 1, Length(AStr)) = AStr; end; function TStringHelperEx.EndsWith(const AStr: string): Boolean; begin Result := Copy(Self.ToString, Length(Self.ToString) - Length(AStr) + 1, Length(AStr)) = AStr; end; function TStringHelperEx.ReplaceAll(const AFrom, ATo: string): string; begin Result := System.StrUtils.ReplaceStr(Self.ToString, AFrom, ATo); end; { TDateTimeHelper } class function TDateTimeHelper.FormatDateTime(const AFormat: string; ADateTime: TDateTime): string; begin Result := System.SysUtils.FormatDateTime(AFormat, ADateTime); end; class function TDateTimeHelper.ParseDateTime(const AValue: string): TDateTime; begin if not TryStrToDateTime(AValue, Result) then Result := 0; end; class function TDateTimeHelper.GetDateOnly(ADateTime: TDateTime): TDate; begin Result := Trunc(ADateTime); end; class function TDateTimeHelper.GetTimeOnly(ADateTime: TDateTime): TTime; begin Result := ADateTime - Trunc(ADateTime); end; class function TDateTimeHelper.StartOfDay(ADateTime: TDateTime): TDateTime; begin Result := Trunc(ADateTime); end; class function TDateTimeHelper.EndOfDay(ADateTime: TDateTime): TDateTime; begin Result := Trunc(ADateTime) + 1 - OneSecond; end; class function TDateTimeHelper.GetAge(ABirthDate: TDateTime): Integer; begin Result := YearOf(Now) - YearOf(ABirthDate); if (MonthOf(Now) < MonthOf(ABirthDate)) or ((MonthOf(Now) = MonthOf(ABirthDate)) and (DayOf(Now) < DayOf(ABirthDate))) then Dec(Result); end; class function TDateTimeHelper.IsToday(ADateTime: TDateTime): Boolean; begin Result := IsSameDay(ADateTime, Now); end; class function TDateTimeHelper.IsSameDay(ADate1, ADate2: TDateTime): Boolean; begin Result := (Trunc(ADate1) = Trunc(ADate2)); end; { TFileHelper } class function TFileHelper.ReadTextFile(const AFileName: string; AEncoding: TEncoding = nil): string; begin Result := ''; if not FileExists(AFileName) then Exit; if AEncoding = nil then AEncoding := TEncoding.UTF8; Result := TFile.ReadAllText(AFileName, AEncoding); end; class procedure TFileHelper.WriteTextFile(const AFileName: string; const AContent: string; AEncoding: TEncoding = nil); begin if AEncoding = nil then AEncoding := TEncoding.UTF8; EnsureDirectoryExists(ExtractFilePath(AFileName)); TFile.WriteAllText(AFileName, AContent, AEncoding); end; class function TFileHelper.FileExists(const AFileName: string): Boolean; begin Result := System.SysUtils.FileExists(AFileName); end; class function TFileHelper.GetFileSize(const AFileName: string): Int64; var F: TFileStream; begin Result := 0; if not FileExists(AFileName) then Exit; F := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone); try Result := F.Size; finally F.Free; end; end; class function TFileHelper.GetFileExtension(const AFileName: string): string; begin Result := System.SysUtils.ExtractFileExt(AFileName); end; class function TFileHelper.GetFileNameWithoutExtension(const AFileName: string): string; begin Result := System.SysUtils.ChangeFileExt(System.SysUtils.ExtractFileName(AFileName), ''); end; class procedure TFileHelper.EnsureDirectoryExists(const ADirPath: string); begin if not DirectoryExists(ADirPath) then ForceDirectories(ADirPath); end; class function TFileHelper.GetTempFileName(const AExtension: string = '.tmp'): string; var TempPath: string; begin TempPath := System.SysUtils.GetTempPath; Result := TempPath + ChangeFileExt(System.SysUtils.ExtractFileName(ParamStr(0)), '') + '_' + IntToStr(GetCurrentThreadID) + AExtension; end; class function TFileHelper.GetAppDataPath: string; begin Result := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0))) + 'Data\'; EnsureDirectoryExists(Result); end; { TJSONHelper } class function TJSONHelper.GetString(AJSON: TJSONObject; const AKey: string; const ADefault: string = ''): string; var Pair: TJSONPair; begin Result := ADefault; Pair := AJSON.GetValue(AKey); if Pair <> nil then Result := Pair.JsonValue.Value; end; class function TJSONHelper.GetInteger(AJSON: TJSONObject; const AKey: string; const ADefault: Integer = 0): Integer; var Pair: TJSONPair; begin Result := ADefault; Pair := AJSON.GetValue(AKey); if Pair <> nil then begin if Pair.JsonValue is TJSONNumber then Result := TJSONNumber(Pair.JsonValue).AsInt else TryStrToInt(Pair.JsonValue.Value, Result); end; end; class function TJSONHelper.GetFloat(AJSON: TJSONObject; const AKey: string; const ADefault: Double = 0.0): Double; var Pair: TJSONPair; begin Result := ADefault; Pair := AJSON.GetValue(AKey); if Pair <> nil then begin if Pair.JsonValue is TJSONNumber then Result := TJSONNumber(Pair.JsonValue).AsDouble else TryStrToFloat(Pair.JsonValue.Value, Result); end; end; class function TJSONHelper.GetBoolean(AJSON: TJSONObject; const AKey: string; const ADefault: Boolean = False): Boolean; var Pair: TJSONPair; begin Result := ADefault; Pair := AJSON.GetValue(AKey); if Pair <> nil then begin if Pair.JsonValue is TJSONTrue then Result := True else if Pair.JsonValue is TJSONFalse then Result := False; end; end; class function TJSONHelper.GetJSONObject(AJSON: TJSONObject; const AKey: string): TJSONObject; var Pair: TJSONPair; begin Result := nil; Pair := AJSON.GetValue(AKey); if Pair <> nil then if Pair.JsonValue is TJSONObject then Result := TJSONObject(Pair.JsonValue); end; class function TJSONHelper.GetJSONArray(AJSON: TJSONObject; const AKey: string): TJSONArray; var Pair: TJSONPair; begin Result := nil; Pair := AJSON.GetValue(AKey); if Pair <> nil then if Pair.JsonValue is TJSONArray then Result := TJSONArray(Pair.JsonValue); end; class function TJSONHelper.TryGetString(AJSON: TJSONObject; const AKey: string; out AValue: string): Boolean; var Pair: TJSONPair; begin Result := False; AValue := ''; Pair := AJSON.GetValue(AKey); if Pair <> nil then begin AValue := Pair.JsonValue.Value; Result := True; end; end; class function TJSONHelper.TryGetInteger(AJSON: TJSONObject; const AKey: string; out AValue: Integer): Boolean; var Pair: TJSONPair; begin Result := False; AValue := 0; Pair := AJSON.GetValue(AKey); if Pair <> nil then begin if Pair.JsonValue is TJSONNumber then begin AValue := TJSONNumber(Pair.JsonValue).AsInt; Result := True; end else if TryStrToInt(Pair.JsonValue.Value, AValue) then Result := True; end; end; class function TJSONHelper.ObjectToString(AJSON: TJSONObject): string; begin if AJSON <> nil then Result := AJSON.ToJSON else Result := ''; end; class function TJSONHelper.StringToObject(const AJSONString: string): TJSONObject; begin Result := nil; if AJSONString = '' then Exit; try Result := TJSONObject.ParseJSONValue(AJSONString) as TJSONObject; except Result := nil; end; end; { TUIHelper } class procedure TUIHelper.ShowMessage(const AMessage: string); begin if not Application.Active then Exit; Application.MessageBox(AMessage, 'Information', MB_OK + MB_ICONINFORMATION); end; class function TUIHelper.ShowMessageOKCancel(const AMessage: string): Boolean; begin Result := False; if not Application.Active then Exit; Result := Application.MessageBox(AMessage, 'Confirm', MB_OKCANCEL + MB_ICONQUESTION) = ID_OK; end; class procedure TUIHelper.ShowError(const AMessage: string); begin if not Application.Active then Exit; Application.MessageBox(AMessage, 'Error', MB_OK + MB_ICONERROR); end; class procedure TUIHelper.ShowInfo(const AMessage: string); begin ShowMessage(AMessage); end; class procedure TUIHelper.ShowWarning(const AMessage: string); begin if not Application.Active then Exit; Application.MessageBox(AMessage, 'Warning', MB_OK + MB_ICONWARNING); end; class procedure TUIHelper.SetControlEnabled(AControl: TControl; AEnabled: Boolean); begin if AControl = nil then Exit; AControl.Enabled := AEnabled; end; class procedure TUIHelper.SetControlsEnabled(AControls: array of TControl; AEnabled: Boolean); var Ctrl: TControl; begin for Ctrl in AControls do SetControlEnabled(Ctrl, AEnabled); end; class procedure TUIHelper.UpdateUI(AControl: TControl; AProc: TProc); begin if AControl = nil then Exit; AControl.BeginUpdate; try AProc; finally AControl.EndUpdate; end; end; class function TUIHelper.GetScreenScale: Single; begin if Application.MainForm <> nil then Result := Application.MainForm.CurrentPPI / 96.0 else Result := 1.0; end; end.