Imports System.Text.RegularExpressions Imports System.Windows.Forms Imports DigitalData.Modules.Database Imports DigitalData.Modules.Language.Utils Imports DigitalData.Modules.Logging Imports DigitalData.Modules.Windows Imports DigitalData.Modules.ZooFlow.Params Imports DigitalData.Modules.ZooFlow.State Imports DigitalData.Modules.Patterns Public Class ProfileFilter Private _ProfileMatch As ProfileMatch Private _ProfileTable As DataTable Private _ProcessTable As DataTable Private _WindowTable As DataTable Private _ControlTable As DataTable Private _Profiles As List(Of ProfileData) Private _TreeView As TreeView Private _LogConfig As LogConfig Private _Logger As Logger Enum ImageIndex Root Profile Clipboard Process Window Control End Enum Public ReadOnly Property Profiles As List(Of ProfileData) Get Return _Profiles End Get End Property Public Sub New(LogConfig As LogConfig, ProfileDatatable As DataTable, ProcessTable As DataTable, WindowDatatable As DataTable, ControlDatatable As DataTable, TreeView As TreeView) Try _LogConfig = LogConfig _Logger = LogConfig.GetLogger() _Logger.Debug("Initializing Profile Filter") _Logger.Debug("Initializing Profile Data") _ProfileTable = ProfileDatatable _ProcessTable = ProcessTable _WindowTable = WindowDatatable _ControlTable = ControlDatatable _Logger.Debug("Initializing Profile Debugging") _TreeView = TreeView _ProfileMatch = New ProfileMatch(LogConfig) _Logger.Debug("Transforming Profiles") _Profiles = TransformProfiles() Catch ex As Exception _Logger.Error(ex) Throw ex End Try End Sub Public Function ToList() As List(Of ProfileData) Return _Profiles End Function Public Function LogRemainingProfiles(Profiles As List(Of ProfileData), StepDescription As String) As List(Of ProfileData) _Logger.Debug("Profiles remaining after Step {0}: {1}", StepDescription, Profiles.Count) Return Profiles End Function Public Function FilterProfilesByClipboardRegex(Profiles As List(Of ProfileData), ClipboardContents As String) As List(Of ProfileData) Dim oFilteredProfiles As New List(Of ProfileData) Dim oRootNode As TreeNode = New TreeNode() With { .Text = $"Suche nach '{ClipboardContents}'", .Tag = "ROOT", .ImageIndex = ImageIndex.Root } _TreeView.Nodes.Clear() _TreeView.Nodes.Add(oRootNode) For Each oProfile In Profiles _Logger.Debug("Current Profile: {0}", oProfile.Name) Dim oNode = _ProfileMatch.NewProfileNode(oProfile) oRootNode.Nodes.Add(oNode) Try Dim oRegex As New Regex(oProfile.Regex) Dim oMatch = oRegex.Match(ClipboardContents) If oMatch.Success Then _Logger.Debug("FilterProfilesByClipboardRegex: Clipboard Regex Matched: {0}", ClipboardContents) _Logger.Info("FilterProfilesByClipboardRegex: Profile {0} matched!", oProfile.Name) oFilteredProfiles.Add(oProfile) oProfile.IsMatched = True Dim oSubnode = _ProfileMatch.NewClipboardRegexNode(oProfile, oMatch.Success) oNode.Nodes.Add(oSubnode) End If Catch ex As Exception _Logger.Warn("Regex '{0}' could not be processed for input '{1}'", oProfile.Regex, ClipboardContents) _Logger.Error(ex) End Try Next Return oFilteredProfiles End Function Public Function FilterProfilesByProcess(Profiles As List(Of ProfileData), ProcessName As String) As List(Of ProfileData) Dim oFilteredProfiles As New List(Of ProfileData) Try For Each oProfile As ProfileData In Profiles Dim oGuid = oProfile.Guid Dim oProcesses As New List(Of ProcessData) For Each oProcess As ProcessData In oProfile.Processes If oProcess.PROFILE_ID <> oGuid Then Continue For End If Dim oIsMatch = oProcess.ProcessName.ToLower = ProcessName.ToLower Dim oParent = _ProfileMatch.FindNodeByTag(_TreeView.Nodes, oProfile.Name & "-REGEX") If oParent IsNot Nothing Then Dim oNode = _ProfileMatch.NewProcessNode(oProfile, oProcess, oIsMatch) oParent.Nodes.Add(oNode) End If _Logger.Debug($"FilterProfilesByProcess: Checking Profile: {oProfile.Name}") If oIsMatch Then _Logger.Debug($"Processname Matched: {oProcess.ProcessName}") oFilteredProfiles.Add(oProfile) _Logger.Info("FilterProfilesByProcess: Profile {0} matched!", oProfile.Name) oProfile.MatchedProcessID = oProcess.Guid oProcess.IsMatched = True oProcesses.Add(oProcess) oProfile.IsMatched = True oProfile.MatchedProcessID = oProcess.Guid End If Next If oFilteredProfiles.Count > 0 Then oProfile.Processes = oProcesses End If Next Return oFilteredProfiles Catch ex As Exception _Logger.Warn("Unexpected error in FilterProfilesByProcess...") _Logger.Error(ex) Return Profiles End Try End Function Public Function FilterWindowsByWindowTitleRegex(Profiles As List(Of ProfileData), WindowTitle As String) As List(Of ProfileData) Dim oProfiles As New List(Of ProfileData) For Each oProfile As ProfileData In Profiles _Logger.Debug("Checking WindowDefinition for profile: {0}...", oProfile.Name) Dim oFilteredWindows As New List(Of WindowData) ' Create list of all controls that match the current process and matched window For Each oWindow As WindowData In oProfile.Windows If oWindow.WindowProcessID = oProfile.MatchedProcessID Then oFilteredWindows.Add(oWindow) End If Next ' If Profile has no windows at all, it automatically matches If oFilteredWindows.Count = 0 Then _Logger.Debug("Profile has no Windows assigned, automatic MATCH") oProfile.IsMatched = True oProfiles.Add(oProfile) Continue For End If Dim oWindows As New List(Of WindowData) For Each oWindowDef As WindowData In oFilteredWindows If oWindowDef.WindowProcessID <> oProfile.MatchedProcessID Then Continue For Try If oWindowDef.Regex = String.Empty Then oProfile.MatchedWindowID = oWindowDef.Guid oWindowDef.IsMatched = True oWindows.Add(oWindowDef) Exit For End If Dim oRegex As New Regex(oWindowDef.Regex) Dim oMatch = oRegex.Match(WindowTitle) If oMatch.Success Then _Logger.Debug("MATCH on WindowTitle: {0}", WindowTitle) oProfile.MatchedWindowID = oWindowDef.Guid oWindowDef.IsMatched = True oWindows.Add(oWindowDef) Else _Logger.Debug("NO MATCH on WindowTitle: {0} - REGEX: {2}", WindowTitle, oWindowDef.Regex) End If Dim oParent = _ProfileMatch.FindNodeByTag(_TreeView.Nodes, oWindowDef.WindowProcessID & "-PROCESS") If oParent IsNot Nothing Then Dim oNode = _ProfileMatch.NewWindowNode(oProfile, oWindowDef, oMatch.Success) oParent.Nodes.Add(oNode) End If Catch ex As Exception _Logger.Warn("Regex '{0}' could not be processed for input '{1}'", oWindowDef.Regex, WindowTitle) _Logger.Error(ex) End Try Next ' If Profile has windows that matched, the profile matches If oWindows.Count > 0 Then oProfile.Windows = oWindows oProfile.IsMatched = True oProfiles.Add(oProfile) _Logger.Info("FilterWindowsByWindowTitleRegex: Profile {0} matched!", oProfile.Name) End If Next Return oProfiles End Function Public Function FilterProfilesByFocusedControl(Profiles As List(Of ProfileData), ClipboardContents As String, WindowHandle As IntPtr) As List(Of ProfileData) Dim oFilteredProfiles As New List(Of ProfileData) Dim oWindow As New Window(_LogConfig) Dim oFocusedWindow As Window.WindowInfo Try oFocusedWindow = oWindow.GetWindowInfo() If oFocusedWindow Is Nothing Then Throw New ApplicationException("Window Information is Empty") End If Catch ex As Exception _Logger.Error(ex) Return Profiles End Try For Each oProfile As ProfileData In Profiles Dim oFilteredControls As New List(Of ControlData) ' Create list of all controls that match the current process and matched window For Each oControl As ControlData In oProfile.Controls If oProfile.MatchedWindowID = oControl.WindowId And oFocusedWindow.ProcessName = oControl.ProcessName Then oFilteredControls.Add(oControl) End If Next ' If Profile has no controls at all, it automatically matches If oFilteredControls.Count = 0 Then _Logger.Debug("Profile has no Controls assigned, automatic MATCH") oFilteredProfiles.Add(oProfile) Continue For End If Dim oMatchingControls As New List(Of ControlData) For Each oControl In oFilteredControls Dim oFound As Boolean = False ' If control name is empty, use coordinates If oControl.ControlName Is Nothing OrElse oControl.ControlName = String.Empty Then Dim oControlBounds As Dictionary(Of String, Window.RectangleInfo) Try oControlBounds = oWindow.GetFocusedControlLocation(WindowHandle) For Each oItem As KeyValuePair(Of String, Window.RectangleInfo) In oControlBounds Select Case oItem.Key Case "TopLeft" If oControl.TopLeft.Top = oItem.Value.Top And oControl.TopLeft.Left = oItem.Value.Left Then oFound = True End If Case "TopRight" If oControl.TopRight.Top = oItem.Value.Top And oControl.TopLeft.Right = oItem.Value.Right Then oFound = True End If Case "BottomLeft" If oControl.BottomLeft.Bottom = oItem.Value.Bottom And oControl.TopLeft.Left = oItem.Value.Left Then oFound = True End If Case "BottomRight" If oControl.BottomRight.Bottom = oItem.Value.Bottom And oControl.TopLeft.Right = oItem.Value.Right Then oFound = True End If End Select Next Catch ex As Exception _Logger.Warn("Error while getting focused control location") _Logger.Error(ex) End Try Else Dim oControlName As String = oControl.ControlName Try Dim oFocusedControl = oWindow.GetFocusedControl(WindowHandle) If oFocusedControl.ControlName = oControlName Then oFound = True End If Catch ex As Exception _Logger.Warn("Error while getting focused control name") _Logger.Error(ex) End Try End If If oFound Then _Logger.Debug("Control {0} has MATCH", oControl.ControlName) oMatchingControls.Add(oControl) End If Dim oParent = _ProfileMatch.FindNodeByTag(_TreeView.Nodes, oControl.WindowId & "-WINDOW") If oParent IsNot Nothing Then Dim oNode = _ProfileMatch.NewControlNode(oProfile, oControl, oFound) oParent.Nodes.Add(oNode) End If Next ' If Profile has controls that matched, the profile matches If oMatchingControls.Count > 0 Then _Logger.Debug("Profile has {0} MATCHING Controls", oMatchingControls.Count) oProfile.Controls = oMatchingControls oFilteredProfiles.Add(oProfile) _Logger.Info("FilterProfilesByFocusedControl: Profile {0} matched!", oProfile.Name) End If Next Return oFilteredProfiles End Function Public Function FilterProfilesBySearchResults(Profiles As List(Of ProfileData), Database As MSSQLServer, User As UserState, ClipboardContents As String) As List(Of ProfileData) Dim oProfiles As New List(Of ProfileData) For Each oProfile In Profiles _Logger.Debug($"Checking SearchResults on ProfileID: {oProfile.Guid}") Dim oResultDocs As Integer = 0 Dim oResultData As Integer = 0 Dim oPatterns As New ClassPatterns(_LogConfig) Dim oDTDataSearches As DataTable Dim oDTDocSearches As DataTable Try oDTDataSearches = Database.GetDatatable($"SELECT COUNT_COMMAND, CONN_ID FROM TBCW_PROF_DATA_SEARCH WHERE ACTIVE = 1 AND PROFILE_ID = {oProfile.Guid}") Catch ex As Exception _Logger.Error(ex) Continue For End Try Try oDTDocSearches = Database.GetDatatable($"SELECT COUNT_COMMAND, CONN_ID FROM TBCW_PROF_DOC_SEARCH WHERE ACTIVE = 1 AND PROFILE_ID = {oProfile.Guid}") Catch ex As Exception _Logger.Error(ex) Continue For End Try For Each oRow As DataRow In oDTDataSearches.Rows Dim oCountCommand = String.Empty Dim oCountConnectionId = String.Empty Try oCountCommand = NotNull(oRow.Item("COUNT_COMMAND"), String.Empty) oCountConnectionId = NotNull(oRow.Item("CONN_ID"), 0) If oCountCommand = String.Empty Then _Logger.Debug("oCountCommand = String.Empty ==> Continue for") Continue For End If oCountCommand = oPatterns.ReplaceInternalValues(oCountCommand) oCountCommand = oPatterns.ReplaceUserValues(oCountCommand, User) oCountCommand = oPatterns.ReplaceClipboardContents(oCountCommand, ClipboardContents) _Logger.Debug($"Count-Command DATA before execute: [{oCountCommand}]") Dim oConnectionString = ProfileUtils.GetConnectionString(Database, oCountConnectionId) oResultData += NotNull(Of Integer)(Database.GetScalarValueWithConnection(oCountCommand, oConnectionString), 0) Catch ex As Exception _Logger.Warn("Invalid SQL Query for Counting Data in Profile {0}: {1}", oProfile.Guid, oCountCommand) End Try Next For Each oRow As DataRow In oDTDocSearches.Rows Dim oCountCommand = String.Empty Try oCountCommand = NotNull(oRow.Item("COUNT_COMMAND"), String.Empty) If oCountCommand = String.Empty Then Continue For End If _Logger.Debug($"Count-Command DOCS BEFORE Replace: {oCountCommand}") oCountCommand = oPatterns.ReplaceInternalValues(oCountCommand) ' _Logger.Debug($"Count-Command DOCS AFTER ReplaceInternalValues: {oCountCommand}") oCountCommand = oPatterns.ReplaceUserValues(oCountCommand, User) ' _Logger.Debug($"Count-Command DOCS AFTER ReplaceUserValues: {oCountCommand}") oCountCommand = oPatterns.ReplaceClipboardContents(oCountCommand, ClipboardContents) _Logger.Debug($"Count-Command DOCS BEFORE execute: {oCountCommand}") oResultDocs += NotNull(Of Integer)(Database.GetScalarValue(oCountCommand), 0) _Logger.Debug($"oResultDocs: {oResultDocs}") Catch ex As Exception _Logger.Warn("Invalid SQL Query for Counting Data in Profile {0}: {1}", oProfile.Guid, oCountCommand) End Try Next oProfile.CountData = oResultData oProfile.CountDocs = oResultDocs Next For Each oProfile In Profiles If oProfile.CountData > 0 Or oProfile.CountDocs > 0 Then oProfiles.Add(oProfile) _Logger.Info("Profile {0} matched in FilterProfilesBySearchResults!", oProfile.Name) End If Next Return oProfiles End Function Public Function FilterWindowsByWindowClipboardRegex(Profiles As List(Of ProfileData), ClipboardContents As String) As List(Of ProfileData) Dim oProfiles As New List(Of ProfileData) For Each oProfile As ProfileData In Profiles _Logger.Debug("Current Profile: {0}", oProfile.Name) Dim oWindows As New List(Of WindowData) For Each w As WindowData In oProfile.Windows Try If w.Regex = String.Empty Then oWindows.Add(w) End If Dim oRegex As New Regex(w.Regex) Dim oMatch = oRegex.Match(ClipboardContents) If oMatch.Success Then _Logger.Debug("Window Clipboard Regex Matched: {0}", ClipboardContents) oWindows.Add(w) End If Dim oResult As TreeNode = _ProfileMatch.FindNodeByTag(_TreeView.Nodes, oProfile.Name & "-REGEX") If Not IsNothing(oResult) Then Dim oNode As New TreeNode($"MATCH on WINDOW Clipboard Regex [{w.Regex}]: {oMatch.Success}") oNode.ImageIndex = 2 oNode.Tag = oProfile.Name & "-WINDOW_REGEX" oResult.Nodes.Add(oNode) End If Catch ex As Exception _Logger.Warn("Regex '{0}' could not be processed for input '{1}'", w.Regex, ClipboardContents) _Logger.Error(ex) End Try Next If oWindows.Count > 0 Then oProfile.Windows = oWindows oProfiles.Add(oProfile) _Logger.Info("FilterWindowsByWindowClipboardRegex: Profile {0} matched!", oProfile.Name) End If Next Return oProfiles End Function Public Function ClearNotMatchedProfiles(Profiles As List(Of ProfileData)) As List(Of ProfileData) Dim oFilteredProfiles As New List(Of ProfileData) For Each oProfile In Profiles If oProfile.IsMatched Then oFilteredProfiles.Add(oProfile) End If Next Return oFilteredProfiles End Function Public Function ClearDuplicateProfiles(Profiles As List(Of ProfileData)) As List(Of ProfileData) Return Profiles. GroupBy(Function(Profile) Profile.Guid). Select(Function(GroupedProfiles) GroupedProfiles.First). ToList() End Function Private Function TransformProfiles() As List(Of ProfileData) Try Dim oList As New List(Of ProfileData) For Each oRow As DataRow In _ProfileTable.Rows Dim oProfileId = oRow.Item("GUID") Dim oProcessList As List(Of ProcessData) = TransformProcesses(oProfileId, _ProcessTable) Dim oWindowList As List(Of WindowData) = TransformWindows(oProfileId, _WindowTable) Dim oControlList As List(Of ControlData) = TransformControls(oProfileId, _ControlTable) oList.Add(New ProfileData() With { .Guid = oRow.Item("GUID"), .Regex = oRow.Item("REGEX_EXPRESSION"), .Name = NotNull(oRow.Item("NAME"), String.Empty), .Comment = NotNull(oRow.Item("COMMENT"), String.Empty), .ProfileType = NotNull(oRow.Item("PROFILE_TYPE"), String.Empty), .Processes = oProcessList, .Windows = oWindowList, .Controls = oControlList }) Next oList = oList. Distinct(). ToList() Return oList Catch ex As Exception _Logger.Error(ex) Throw ex End Try End Function Private Function TransformControls(ProfileId As Integer, ControlDatatable As DataTable) As List(Of ControlData) Try Dim oControlList As New List(Of ControlData) For Each oRow As DataRow In ControlDatatable.Rows If oRow.Item("PROFILE_ID") = ProfileId Then oControlList.Add(New ControlData() With { .Guid = oRow.Item("GUID"), .ProcessName = NotNull(oRow.Item("PROCESS_NAME"), String.Empty), .ControlName = NotNull(oRow.Item("CONTROL_NAME"), String.Empty), .Description = NotNull(oRow.Item("DESCRIPTION"), String.Empty), .WindowId = oRow.Item("WINDOW_ID"), .TopLeft = New ControlBounds() With { .Left = oRow.Item("TOPLEFT_LEFT"), .Right = oRow.Item("TOPLEFT_RIGHT"), .Top = oRow.Item("TOPLEFT_TOP"), .Bottom = oRow.Item("TOPLEFT_BOTTOM") }, .TopRight = New ControlBounds() With { .Left = oRow.Item("TOPRIGHT_LEFT"), .Right = oRow.Item("TOPRIGHT_RIGHT"), .Top = oRow.Item("TOPRIGHT_TOP"), .Bottom = oRow.Item("TOPRIGHT_BOTTOM") }, .BottomLeft = New ControlBounds() With { .Left = oRow.Item("BOTTOMLEFT_LEFT"), .Right = oRow.Item("BOTTOMLEFT_RIGHT"), .Top = oRow.Item("BOTTOMLEFT_TOP"), .Bottom = oRow.Item("BOTTOMLEFT_BOTTOM") }, .BottomRight = New ControlBounds() With { .Left = oRow.Item("BOTTOMRIGHT_LEFT"), .Right = oRow.Item("BOTTOMRIGHT_RIGHT"), .Top = oRow.Item("BOTTOMRIGHT_TOP"), .Bottom = oRow.Item("BOTTOMRIGHT_BOTTOM") } }) End If Next Return oControlList Catch ex As Exception _Logger.Error(ex) Throw ex End Try End Function Private Function TransformProcesses(ProfileId As Integer, ProcessDatatable As DataTable) As List(Of ProcessData) Try Dim oProcessList As New List(Of ProcessData) For Each oRow As DataRow In ProcessDatatable.Rows If oRow.Item("PROFILE_ID") = ProfileId Then oProcessList.Add(New ProcessData() With { .Guid = oRow.Item("GUID"), .PROFILE_ID = oRow.Item("PROFILE_ID"), .ProcessName = NotNull(oRow.Item("PROC_NAME"), String.Empty) }) End If Next oProcessList = oProcessList. Distinct(). ToList() Return oProcessList Catch ex As Exception _Logger.Error(ex) Throw ex End Try End Function Private Function TransformWindows(ProfileId As Integer, WindowDatatable As DataTable) As List(Of WindowData) Try Dim oWindowList As New List(Of WindowData) For Each oRow As DataRow In WindowDatatable.Rows If oRow.Item("PROFILE_ID") = ProfileId Then oWindowList.Add(New WindowData() With { .Guid = oRow.Item("GUID"), .WindowProcessID = oRow.Item("PROCESS_ID"), .Title = NotNull(oRow.Item("DESCRIPTION"), String.Empty), .Regex = NotNull(oRow.Item("REGEX"), String.Empty), .Sequence = NotNull(oRow.Item("SEQUENCE"), 0) }) End If Next Return oWindowList Catch ex As Exception _Logger.Error(ex) Throw ex End Try End Function End Class