Monorepo/GUIs.ClipboardWatcher/ProfileFilter.vb

613 lines
26 KiB
VB.net

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 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 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