diff --git a/docs/developer-documentation/components/Minimap.md b/docs/developer-documentation/components/Minimap.md index c01cacd..c4ad550 100644 --- a/docs/developer-documentation/components/Minimap.md +++ b/docs/developer-documentation/components/Minimap.md @@ -1,59 +1,86 @@ -# Component name +# Minimap View (Screenshot of component - Optional) --- -(Short description) +This component contains the colored map which visualizes the log contents according to the value in every cell. ## Relations to other components -- **Parent:** ParentComponent -- **Children:** - - Child1 - - Child2 +- **Parent:** App ## Props | Name | Type | Description | | ---- | ---- | ----------- | -| `prop1` | `type1` | short description | -| `prop2` | `type2` | short description | -| `prop3` | `type3` | short description | +| `logFile` | `LogFile` | it contains the input content of the log | +| `logViewState` | `LogViewState` | A state to keep several values for representing the log view | +| `onLogViewStateChanged` | `function` | A function that update the log view state to other components | +| `forwardRef` | `React.RefObject` | A ref object for update the scrolling between log view and minimap | +| `rowProperties` | `RowProperty[]` | A interface to keep all the row related properties | ## State | Name | Type | Initial Value | Description | | ---- | ---- | ------------- | ----------- | -| `stateObj1` | `type1` | `init value` | short description | -| `stateObj2` | `type1` | `init value` | short description | -| `stateObj3` | `type1` | `init value` | short description | +| `state` | `LogViewState` | `undefined` | A state to keep several values for representing the log view | +| `scale` | `number` | `1` | the scale of the Mini map. If it is 1, then minimap 1: 1 matches the log view. If it is 0, minimap matches all the log view. | +| `controlDown` | `boolean` | `false` | A state represents whether the ctrl button is pressed | ## Functions ### Component lifecycle functions + - ### `constructor(...)` - **Params:** - `props: Props` - - **Description:** Is invoked the first time the `StructureDialog` is opened. It constructs an array containing [StructureEntries](..\Types\StructureEntry.md) from the `logSelectedRows` props and updates the state accordingly. + - **Description:** initializes the minimap canvas and the states; bind handlers for wheel and click actions; - **Returns:** - +- ### `componentDidMount(...)` + - **Description:** add three events listener for window resize, the ctrl key down and up. -- ### `shouldComponentUpdate(...)` +- ### `componentDidUpdate(...)` - **Params:** - `nextProps: Readonly` - `nextState: Readonly` - `nextContext: any` - - **Description:** This function returns a `boolean` value that indicates whether or not rendering should be skipped. It returns `true` if ..., and ... otherwise. - - **Returns:** `boolean` + - **Description:** check whether the logViewState, scale or the logFile is changed. If so, call the draw() function. + - **Returns:** - - ### `render()` - - **Description:** - - **Returns:** Div of type `JSX.Element` containing.... + - **Description:** renders the minimap canvas + - **Returns:** div of type `JSX.Element` ### Functionality-related functions -- ### `exampleFunctionWithNoParams()` - - **Description:** short description of what happens in the function. + +- ### `draw()` + - **Description:** draw the minimap and the grey block. + - **Returns:** - + +- ### `handleClick(...)` + - **Params:** + - `e: React.MouseEvent` + - **Description:** A function to handle the click action on a random point on the canvas, the log view will jump to that point. + - **Returns:** - + +- ### `handleWheel(...)` + - **Params:** + - `e: React.WheelEvent` + - **Description:** A function to handle scroll action on the canvas. Depends on whether "ctrl" is pressed, it will either scroll the minimap or Zoom In/Out. + - **Returns:** - + +- ### `updateState(...)` + - **Params:** + - `scale: number` + - **Description:** A function to update the log view state when the scale is changed by zooming in/out the minimap. + - **Returns:** - + +- ### `controlDownListener(...)` + - **Params:** + - `e: any` + - **Description:** A function triggered by key down event of "ctrl", it will set the state controlDown to true. - **Returns:** - -- ### `exampleFunctionWithParams(...)` +- ### `controlUpListener(...)` - **Params:** - - `name: type` - - **Description:** short description of what happens in the function. + - `e: any` + - **Description:** A function triggered by key up event of "ctrl", it will set the state controlDown to false. - **Returns:** - diff --git a/docs/developer-documentation/dev-guidlines.md b/docs/developer-documentation/dev-guidlines.md index 44011df..9bfcfd6 100644 --- a/docs/developer-documentation/dev-guidlines.md +++ b/docs/developer-documentation/dev-guidlines.md @@ -14,23 +14,27 @@ Below you can find several actions that should be taken while working on Tracy, - Make sure that the merged branch is deleted. ### Guidelines +#### Best Practices +- **Creating and updating branches:** Create branches from main and fetch/pull regularly. Doing this will prevent huge merge conflicts on your branch in the future. +- **Format document:** Before you submit make sure to format the files you worked on. This makes sure your added code is formatted correctly. This also formats any code that was missed by previous contributors. Tracy utilises Prettier for code formating. +- **React and Typescript versions:** Use the latest versions of React and TypeScript, and keep them updated regularly. This will ensure that you can use the newest features and capabilities of both technologies, such as React hooks and TypeScript generics. +- **Code style:** Use a consistent code style and follow the naming conventions described below. This will help you maintain a clean and readable codebase, and avoid common errors and bugs. +- **Typescript practices:** + - Use type aliases or interfaces to declare the types of your component props and state, and provide descriptive names and comments for them. This will help you document your components and make them easier to use and reuse. + - Use type inference whenever possible, and avoid using any type or casting types with as. This will help you leverage TypeScript's type system and avoid losing type information or introducing type errors. +- **React practices:** + - Use functional components over class components where possible. This keeps the code up to date to the latest recommendation and best practises. + - Use Pure components when possible. This will help you write simpler and more concise components, and avoid unnecessary re-rendering and memory leaks. + - Use custom hooks to extract reusable logic from your components, and follow the naming convention of using the "use" prefix. This will help you organize your code and avoid duplication. + #### Naming convention +- Branch naming convention: In order to avoid confusion make sure you branch has a meaningful name that immediately informs the reader what the branch contains. In addition to this make sure to prefix your branch name with either of the following names: bug-fix/ or feature/ , followed by a meaningful name. - Use PascalCase for component names, type names, interface names, enum names, and type parameter names. For example: ComponentName, TypeName, InterfaceName, EnumName, T. - Use camelCase for variable names, function names, method names, property names, parameter names, and module alias names. For example: variableName, functionName, methodName, propertyName, parameterName, moduleAliasName. -- Use UPPER_CASE for constant values, including enum values and global constants. For example: CONSTANT_VALUE, ENUM_VALUE. +- Use UPPER_CASE for global constant values, including enum values and global constants. For example: CONSTANT_VALUE, ENUM_VALUE. Regular constans use camelCase. - Use descriptive and meaningful names for your identifiers, and avoid using abbreviations or acronyms that are not widely known or understood. For example: firstName, lastName, userProfile, not fn, ln, up. - Use consistent naming for component props types, such as Props. For example: ButtonProps, ModalProps. -#### Other -- Use the latest versions of React and TypeScript, and keep them updated regularly. This will ensure that you can use the newest features and capabilities of both technologies, such as React hooks and TypeScript generics. -- Use a consistent code style and formatting, and enforce it with tools such as ESLint, and EditorConfig??. This will help you maintain a clean and readable codebase, and avoid common errors and bugs. -- Use TypeScript's strict mode and enable all the strict compiler options, such as noImplicitAny, noImplicitThis, strictNullChecks, and strictFunctionTypes. This will help you catch potential type errors and enforce type safety throughout your codebase. ?? - discuss with Bob -- Use type aliases or interfaces to declare the types of your component props and state, and provide descriptive names and comments for them. This will help you document your components and make them easier to use and reuse. -- Use type inference whenever possible, and avoid using any type or casting types with as. This will help you leverage TypeScript's type system and avoid losing type information or introducing type errors. -- Use functional components instead of class components, and use React hooks instead of lifecycle methods. This will help you write simpler and more concise components, and avoid unnecessary re-rendering and memory leaks. -- Use Pure components when possible. This will help you write simpler and more concise components, and avoid unnecessary re-rendering and memory leaks. -- Use custom hooks to extract reusable logic from your components, and follow the naming convention of using the "use" prefix. This will help you organize your code and avoid duplication. - ### References - [Typescript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/docs/basic/setup) - [Google Typescript Style Guide](https://google.github.io/styleguide/tsguide.html) diff --git a/docs/developer-documentation/documentation/templates/Hook.md b/docs/developer-documentation/documentation/templates/Hook.md new file mode 100644 index 0000000..ed36db2 --- /dev/null +++ b/docs/developer-documentation/documentation/templates/Hook.md @@ -0,0 +1,12 @@ +# useCustomHookName + +This file contains custom hooks which are used for ... in the ... + + +## Dependencies +- `constants` +- `types` + +## Custom hooks +- `customHookWithoutParams()` +- `customHookWithParams(...)` diff --git a/docs/developer-documentation/hooks/useStructureEntryManager.md b/docs/developer-documentation/hooks/useStructureEntryManager.md index e69de29..4ff5431 100644 --- a/docs/developer-documentation/hooks/useStructureEntryManager.md +++ b/docs/developer-documentation/hooks/useStructureEntryManager.md @@ -0,0 +1,60 @@ +# useStructureEntryManager + +This file contains custom hooks which are used for the managment (i.e., addition/deletion/modification) of structure entries in the [StructureDialog](../components/StructureDialog.md). + + +## Dependencies +- `constants` +- `types` + +## Custom hooks +- `constructStructureEntriesArray(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `constructNewStructureEntry(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `appendNewStructureEntries(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `removeStructureEntryFromList(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `updateStructureEntriesAfterWildcardDeletion(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `toggleStructureLink(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `toggleCellSelection(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `removeLastStructureLink(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `addWildcardToStructureEntry(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `removeWildcardFromStructureEntry(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** diff --git a/docs/developer-documentation/hooks/useStructureRegularExpressionManager.md b/docs/developer-documentation/hooks/useStructureRegularExpressionManager.md index e69de29..a1723db 100644 --- a/docs/developer-documentation/hooks/useStructureRegularExpressionManager.md +++ b/docs/developer-documentation/hooks/useStructureRegularExpressionManager.md @@ -0,0 +1,51 @@ +# useStructureEntryManager + +This file contains custom hooks which contain all the Regular Expression logic related to the Structure Matching feature. The hooks in this file are used in the [`App`](../components/App.md) and [`StructureDialog`](../components/StructureDialog.md) React components. + + +## Dependencies +- `constants` +- `types` +- [`useWildcardManager`](useWildcardManager.md) + +## Custom hooks +- `getRegExpExactWhiteSpace(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `escapeSpecialChars(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getLineEndString(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getCellValue(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getRegExpForLogEntry(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `useStructureQueryConstructor(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `useJsonObjectToTextRangesMap(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `useStructureRegularExpressionSearch(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** \ No newline at end of file diff --git a/docs/developer-documentation/hooks/useStyleManager.md b/docs/developer-documentation/hooks/useStyleManager.md index e69de29..13d9902 100644 --- a/docs/developer-documentation/hooks/useStyleManager.md +++ b/docs/developer-documentation/hooks/useStyleManager.md @@ -0,0 +1,90 @@ +# useStructureEntryManager + +This file contains custom hooks which are used for styling `JSX.Element`(s) used in different React components. + + +## Dependencies +- `constants` +- `types` + +## Custom hooks +- `getLogViewRowStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `structureDialogBackdropStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `structureDialogDialogStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `wildcardStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getStructureTableHeaderStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getHeaderColumnStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getHeaderColumnInnerStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getStructureTableEntryIconStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getStructureTableRowStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getStructureTableLinkStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getStructureTableColumnStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getStructureTableCellSelectionStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getLogViewRowSelectionStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getLogViewStructureMatchStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getContextMenuStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getContextMenuItemStyle(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** \ No newline at end of file diff --git a/docs/developer-documentation/hooks/useWildcardManager.md b/docs/developer-documentation/hooks/useWildcardManager.md index e69de29..2d283ae 100644 --- a/docs/developer-documentation/hooks/useWildcardManager.md +++ b/docs/developer-documentation/hooks/useWildcardManager.md @@ -0,0 +1,70 @@ +# useStructureEntryManager + +This file contains custom hooks which are used for the managment(i.e., creation/insertion/deletion) of [`Wildcard`(s)](../types/Wildcard.md) in the structure definition displayed in the [StructureTable](../components/StructureTable.md) component. + + +## Dependencies +- `types` +- [`useStyleManager`](useStyleManager.md) + +## Custom hooks +- `createWildcard(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `updateCellContentsIndices(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getIndicesForWildcardFromDivId(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `insertWildcardIntoCellsContents(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getCellContentsFromTextValue(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `removeWildcardSubstitution(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `removeWildcardSubstitutionsForStructureEntry(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `removeWildcardFromCellContent(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `createCellContents(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getReactElementsFromCellContents(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `getWildcardIndex(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** +- `isSubstitutionFirstForWildcard(...)` + - **Params:** + - `props: Props` + - **Description:** + - **Returns:** diff --git a/docs/developer-documentation/types/StructureMatchId.md b/docs/developer-documentation/types/StructureMatchId.md new file mode 100644 index 0000000..8ec6473 --- /dev/null +++ b/docs/developer-documentation/types/StructureMatchId.md @@ -0,0 +1,7 @@ +# Name + +```TS +type StructureMatchId = number | null; +``` + +The Structure Matching feature can result in several matches.This type is used to type the indices of these matches, which are `null` before the matching and a `number` afterwards. \ No newline at end of file diff --git a/src/viewer/App.tsx b/src/viewer/App.tsx index 48aea66..83f26cc 100644 --- a/src/viewer/App.tsx +++ b/src/viewer/App.tsx @@ -49,7 +49,7 @@ interface State { // Structure related logFileAsString: string; - logEntryCharIndexMaps: LogEntryCharMaps; + logEntryCharIndexMaps: LogEntryCharMaps | null; selectedLogRows: string[][]; // selectedRowsTypes: RowType[]; rowProperties: RowProperty[]; @@ -105,7 +105,7 @@ export default class App extends React.Component { caseSearch: false, selectedLogRows: [], rowProperties: [], - logEntryCharIndexMaps: { firstCharIndexMap: null, lastCharIndexMap: null }, + logEntryCharIndexMaps: null, showStructureDialog: false, structureMatches: [], structureMatchesLogRows: [], @@ -327,7 +327,7 @@ export default class App extends React.Component { const structureMatches = useStructureRegularExpressionSearch( expression, logFileAsString, - logEntryCharIndexMaps, + logEntryCharIndexMaps!, ); let structureMatchesLogRows: number[] = []; @@ -386,12 +386,12 @@ export default class App extends React.Component { const entryMatches = getRegularExpressionMatches( entryExpression, logFileAsString, - logEntryCharIndexMaps, + logEntryCharIndexMaps!, ); const exitMatches = getRegularExpressionMatches( exitExpression, logFileAsString, - logEntryCharIndexMaps, + logEntryCharIndexMaps!, ); const stack: number[] = []; diff --git a/src/viewer/hooks/useLogSearchManager.ts b/src/viewer/hooks/useLogSearchManager.ts index 7affd39..198634f 100644 --- a/src/viewer/hooks/useLogSearchManager.ts +++ b/src/viewer/hooks/useLogSearchManager.ts @@ -1,160 +1,163 @@ -import { LogEntryCharRanges } from "../types"; +import { LogEntryCharMaps } from "../types"; export const escapeSpecialChars = (text: string): string => { - let safeText = ""; + let safeText = ""; - if (text !== undefined) { - safeText = text.replace(/[\\]/g, "\\\\\\$&"); //double escaped slashes - safeText = safeText.replace(/[\.\*\+\?\^\$\{\}\(\)\|\[\]\-]/g, "\\$&"); // replace special characters - safeText = safeText.replace(/[\"]/g, "\\\\$&"); //double quotes - } + if (text !== undefined) { + safeText = text.replace(/[\\]/g, "\\\\\\$&"); //double escaped slashes + safeText = safeText.replace(/[\.\*\+\?\^\$\{\}\(\)\|\[\]\-]/g, "\\$&"); // replace special characters + safeText = safeText.replace(/[\"]/g, "\\\\$&"); //double quotes + } - const regExpCarriageReturnAtEnd = /\r$/; + const regExpCarriageReturnAtEnd = /\r$/; - if (regExpCarriageReturnAtEnd.test(text)) { - safeText = safeText.replace(regExpCarriageReturnAtEnd, "\\\\r"); - } + if (regExpCarriageReturnAtEnd.test(text)) { + safeText = safeText.replace(regExpCarriageReturnAtEnd, "\\\\r"); + } - return safeText; + return safeText; }; export const useRegularExpressionSearch = ( - flags: string, - expression: string, - text: string, + flags: string, + expression: string, + text: string, ): boolean => { - const structureQuery = new RegExp(expression, flags); - const result = structureQuery.exec(escapeSpecialChars(text)); - if (result === null) return false; - else return true; + const structureQuery = new RegExp(expression, flags); + const result = structureQuery.exec(escapeSpecialChars(text)); + if (result === null) return false; + else return true; }; // Long function to reduce number of checks export const returnSearchIndices = ( - rows: string[][], - columnIndex: number, - searchText: string, - reSearchBool: boolean, - wholeSearchBool: boolean, - caseSearchBool: boolean, + rows: string[][], + columnIndex: number, + searchText: string, + reSearchBool: boolean, + wholeSearchBool: boolean, + caseSearchBool: boolean, ): number[] => { - let loglineText: string; - let searchTerms: string[]; - const indices: number[] = []; - if (!caseSearchBool && !reSearchBool) searchText = searchText.toLowerCase(); - if (searchText.charAt(0) === '"' && searchText.slice(-1) === '"') - searchTerms = [searchText.slice(1, -1)]; - else searchTerms = searchText.split(" "); - if (!reSearchBool && !wholeSearchBool) { - if (columnIndex === -1) { - if (!caseSearchBool) { - for (let i = 0; i < rows.length; i++) { - loglineText = rows[i].join(" ").toLowerCase(); - let found = true; - for (const term of searchTerms) { - if (loglineText.indexOf(term) == -1) { - found = false; - break; - } - } - if (found) indices.push(i); - } - } else { - for (let i = 0; i < rows.length; i++) { - loglineText = rows[i].join(" "); - let found = true; - for (const term of searchTerms) { - if (loglineText.indexOf(term) == -1) { - found = false; - break; - } - } - if (found) indices.push(i); - } - } - } else { - if (!caseSearchBool) { - for (let i = 0; i < rows.length; i++) { - loglineText = rows[i][columnIndex].toLowerCase(); - let found = true; - for (const term of searchTerms) { - if (loglineText.indexOf(term) == -1) { - found = false; - break; - } - } - if (found) indices.push(i); - } - } else { - for (let i = 0; i < rows.length; i++) { - loglineText = rows[i][columnIndex]; - let found = true; - for (const term of searchTerms) { - if (loglineText.indexOf(term) == -1) { - found = false; - break; - } - } - if (found) indices.push(i); - } - } - } - } else { - let flags: string; - if (!caseSearchBool) flags = "gsi"; - else flags = "gs"; - if (wholeSearchBool) - for (let i = 0; i < searchTerms.length; i++) searchTerms[i] = "\\b" + searchTerms[i] + "\\b"; - if (columnIndex === -1) { - for (let i = 0; i < rows.length; i++) { - loglineText = rows[i].join(" "); - let found = true; - for (const term of searchTerms) { - if (useRegularExpressionSearch(flags, term, loglineText) === false) { - found = false; - break; - } - } - if (found) indices.push(i); - } - } else { - for (let i = 0; i < rows.length; i++) { - loglineText = rows[i][columnIndex]; //Lowercase? - let found = true; - for (const term of searchTerms) { - if (useRegularExpressionSearch(flags, term, loglineText) === false) { - found = false; - break; - } - } - if (found) indices.push(i); - } - } - } - return indices; + let loglineText: string; + let searchTerms: string[]; + const indices: number[] = []; + if (!caseSearchBool && !reSearchBool) searchText = searchText.toLowerCase(); + if (searchText.charAt(0) === '"' && searchText.slice(-1) === '"') + searchTerms = [searchText.slice(1, -1)]; + else searchTerms = searchText.split(" "); + if (!reSearchBool && !wholeSearchBool) { + if (columnIndex === -1) { + if (!caseSearchBool) { + for (let i = 0; i < rows.length; i++) { + loglineText = rows[i].join(" ").toLowerCase(); + let found = true; + for (const term of searchTerms) { + if (loglineText.indexOf(term) == -1) { + found = false; + break; + } + } + if (found) indices.push(i); + } + } else { + for (let i = 0; i < rows.length; i++) { + loglineText = rows[i].join(" "); + let found = true; + for (const term of searchTerms) { + if (loglineText.indexOf(term) == -1) { + found = false; + break; + } + } + if (found) indices.push(i); + } + } + } else { + if (!caseSearchBool) { + for (let i = 0; i < rows.length; i++) { + loglineText = rows[i][columnIndex].toLowerCase(); + let found = true; + for (const term of searchTerms) { + if (loglineText.indexOf(term) == -1) { + found = false; + break; + } + } + if (found) indices.push(i); + } + } else { + for (let i = 0; i < rows.length; i++) { + loglineText = rows[i][columnIndex]; + let found = true; + for (const term of searchTerms) { + if (loglineText.indexOf(term) == -1) { + found = false; + break; + } + } + if (found) indices.push(i); + } + } + } + } else { + let flags: string; + if (!caseSearchBool) flags = "gsi"; + else flags = "gs"; + if (wholeSearchBool) + for (let i = 0; i < searchTerms.length; i++) searchTerms[i] = "\\b" + searchTerms[i] + "\\b"; + if (columnIndex === -1) { + for (let i = 0; i < rows.length; i++) { + loglineText = rows[i].join(" "); + let found = true; + for (const term of searchTerms) { + if (useRegularExpressionSearch(flags, term, loglineText) === false) { + found = false; + break; + } + } + if (found) indices.push(i); + } + } else { + for (let i = 0; i < rows.length; i++) { + loglineText = rows[i][columnIndex]; //Lowercase? + let found = true; + for (const term of searchTerms) { + if (useRegularExpressionSearch(flags, term, loglineText) === false) { + found = false; + break; + } + } + if (found) indices.push(i); + } + } + } + return indices; }; export const getRegularExpressionMatches = ( - expression: string, - logFileAsString: string, - logEntryCharRanges: LogEntryCharRanges, + expression: string, + logFileAsString: string, + logEntryCharRanges: LogEntryCharMaps, ): number[] => { - const searchIndices: number[] = []; - const resultingMatches: number[] = []; - const flags = "gs"; - const query = new RegExp(expression, flags); - let result; + const searchIndices: number[] = []; + const resultingMatches: number[] = []; + const flags = "gs"; + const query = new RegExp(expression, flags); + let result; - while ((result = query.exec(logFileAsString)) !== null) { - searchIndices.push(result.index); - } + while ((result = query.exec(logFileAsString)) !== null) { + searchIndices.push(result.index); + } - if (searchIndices.length > 0) { - searchIndices.forEach((searchIndex) => { - const indexOfMatchedEntry = logEntryCharRanges.firstCharIndexMap.get(searchIndex); - resultingMatches.push(indexOfMatchedEntry); - }); - } + if (searchIndices.length > 0) { + searchIndices.forEach((searchIndex) => { - return resultingMatches; + const indexOfMatchedEntry = logEntryCharRanges.firstCharIndexMap.get(searchIndex); + + if (indexOfMatchedEntry) + resultingMatches.push(indexOfMatchedEntry); + }); + } + + return resultingMatches; }; diff --git a/src/viewer/hooks/useStructureRegularExpressionManager.ts b/src/viewer/hooks/useStructureRegularExpressionManager.ts index 9bba0e2..f6c1049 100644 --- a/src/viewer/hooks/useStructureRegularExpressionManager.ts +++ b/src/viewer/hooks/useStructureRegularExpressionManager.ts @@ -11,239 +11,255 @@ const regExpjsonObject = "{.+?},?\\n"; const flags = "gs"; const getRegExpExactWhiteSpace = (amountOfWhitespace: number): string => - `\\s{${amountOfWhitespace}}`; + `\\s{${amountOfWhitespace}}`; const escapeSpecialChars = (text: string): string => { - let safeText = ""; + let safeText = ""; - if (text !== undefined) { - safeText = text.replace(/[\\]/g, "\\\\\\$&"); //double escaped slashes - safeText = safeText.replace(/[\.\*\+\?\^\$\{\}\(\)\|\[\]\-]/g, "\\$&"); // replace special characters - safeText = safeText.replace(/[\"]/g, "\\\\$&"); //double quotes - } + if (text !== undefined) { + safeText = text.replace(/[\\]/g, "\\\\\\$&"); //double escaped slashes + safeText = safeText.replace(/[\.\*\+\?\^\$\{\}\(\)\|\[\]\-]/g, "\\$&"); // replace special characters + safeText = safeText.replace(/[\"]/g, "\\\\$&"); //double quotes + } - const regExpCarriageReturnAtEnd = /\r$/; + const regExpCarriageReturnAtEnd = /\r$/; - if (regExpCarriageReturnAtEnd.test(text)) { - safeText = safeText.replace(regExpCarriageReturnAtEnd, "\\\\r"); - } + if (regExpCarriageReturnAtEnd.test(text)) { + safeText = safeText.replace(regExpCarriageReturnAtEnd, "\\\\r"); + } - return safeText; + return safeText; }; const getLineEndString = (amountOfWhiteSpace: number): string => { - return regExpLineFeed + getRegExpExactWhiteSpace(amountOfWhiteSpace); + return regExpLineFeed + getRegExpExactWhiteSpace(amountOfWhiteSpace); }; +function getHeaderValue(text: string) { + let headerValue = text; + const regExpCarriageReturnAtEnd = /\r$/; + + if (regExpCarriageReturnAtEnd.test(headerValue)) + headerValue = headerValue.replace(regExpCarriageReturnAtEnd, "\\\\r"); + + headerValue = `"${headerValue}"`; + + return headerValue; +} + const getCellValue = ( - content: CellContents[], - rowIndex: number, - cellIndex: number, - header: Header, - headerColumnType: StructureHeaderColumnType, - isSelected: boolean, - wildcards: Wildcard[], + content: CellContents[], + rowIndex: number, + cellIndex: number, + header: Header, + headerColumnType: StructureHeaderColumnType, + isSelected: boolean, + wildcards: Wildcard[], ): string => { - let value: string; - - if (isSelected && headerColumnType !== StructureHeaderColumnType.Custom) { - if (content[0].textValue == null) value = "null"; - else { - const valueParts: string[] = []; - - for (let i = 0; i < content.length; i++) { - if (content[i].wildcardIndex == null) { - valueParts.push(`${escapeSpecialChars(content[i].textValue)}`); - } else { - const isFirstSubstitution = isSubstitutionFirstForWildcard( - wildcards[content[i].wildcardIndex!], - rowIndex, - cellIndex, - i, - ); - - if (isFirstSubstitution) { - valueParts.push(`(?${regExpValuePattern})`); - } else { - valueParts.push(`\\k`); - } - } - } - - value = '"' + valueParts.join("") + '"'; - } - } else { - value = - header.name.toLowerCase() === "timestamp" - ? `"${regExpTimeStampPattern}"` - : `"${regExpValuePattern}"`; - } - - return value; + let value: string; + + if (isSelected && headerColumnType !== StructureHeaderColumnType.Custom) { + if (content[0].textValue == null) value = "null"; + else { + const valueParts: string[] = []; + + for (let i = 0; i < content.length; i++) { + if (content[i].wildcardIndex == null) { + valueParts.push(`${escapeSpecialChars(content[i].textValue)}`); + } else { + const isFirstSubstitution = isSubstitutionFirstForWildcard( + wildcards[content[i].wildcardIndex!], + rowIndex, + cellIndex, + i, + ); + + if (isFirstSubstitution) { + valueParts.push(`(?${regExpValuePattern})`); + } else { + valueParts.push(`\\k`); + } + } + } + + value = '"' + valueParts.join("") + '"'; + } + } else { + value = + header.name.toLowerCase() === "timestamp" + ? `"${regExpTimeStampPattern}"` + : `"${regExpValuePattern}"`; + } + + return value; }; const getRegExpForLogEntry = ( - logHeaders: Header[], - headerTypes: StructureHeaderColumnType[], - row: CellContents[][], - rowIndex: number, - cellSelection: boolean[], - wildcards: Wildcard[], + logHeaders: Header[], + headerTypes: StructureHeaderColumnType[], + row: CellContents[][], + rowIndex: number, + cellSelection: boolean[], + wildcards: Wildcard[], ): string => { - let objectString = "{" + getLineEndString(4); - let rowString = ""; - let hasProcessedLastUsableColumn = false; - - for (let c = row.length - 1; c >= 0; c--) { - const headerString = `"${logHeaders[c].name}"`; - const headerType = headerTypes[c]; - const isCellSelected = cellSelection[c]; - - if (headerType !== StructureHeaderColumnType.Custom && row[c] !== undefined) { - let valueString = getCellValue( - row[c], - rowIndex, - c, - logHeaders[c], - headerType, - isCellSelected, - wildcards, - ); - let headerAndCellString = ""; - - if (hasProcessedLastUsableColumn) { - valueString = valueString.concat(","); - headerAndCellString = headerAndCellString.concat( - headerString, - ": ", - valueString, - getLineEndString(4), - ); - } else { - headerAndCellString = headerAndCellString.concat( - headerString, - ": ", - valueString, - getLineEndString(2), - ); - } - - rowString = headerAndCellString.concat(rowString); - - hasProcessedLastUsableColumn = true; - } - } - - objectString = objectString.concat(rowString, "},?", regExpLineFeed); - - return objectString; + let objectString = "{" + getLineEndString(4); + let rowString = ""; + let hasProcessedLastUsableColumn = false; + + for (let c = row.length - 1; c >= 0; c--) { + + const headerString = getHeaderValue(logHeaders[c].name); + + const headerType = headerTypes[c]; + const isCellSelected = cellSelection[c]; + + if (headerType !== StructureHeaderColumnType.Custom && row[c] !== undefined) { + let valueString = getCellValue( + row[c], + rowIndex, + c, + logHeaders[c], + headerType, + isCellSelected, + wildcards, + ); + let headerAndCellString = ""; + + if (hasProcessedLastUsableColumn) { + valueString = valueString.concat(","); + headerAndCellString = headerAndCellString.concat( + headerString, + ": ", + valueString, + getLineEndString(4), + ); + } else { + headerAndCellString = headerAndCellString.concat( + headerString, + ": ", + valueString, + getLineEndString(2), + ); + } + + rowString = headerAndCellString.concat(rowString); + + hasProcessedLastUsableColumn = true; + } + } + + objectString = objectString.concat(rowString, "},?", regExpLineFeed); + + return objectString; }; export const useStructureQueryConstructor = ( - logHeaders: Header[], - headerColumnTypes: StructureHeaderColumnType[], - structureEntries: StructureEntry[], - wildcards: Wildcard[], + logHeaders: Header[], + headerColumnTypes: StructureHeaderColumnType[], + structureEntries: StructureEntry[], + wildcards: Wildcard[], ): string => { - let regularExp = ""; - - for (let r = 0; r < structureEntries.length; r++) { - const structureEntry = structureEntries[r]; - - const rowRegExp = getRegExpForLogEntry( - logHeaders, - headerColumnTypes, - structureEntry.row, - r, - structureEntry.cellSelection, - wildcards, - ); - regularExp = regularExp.concat(rowRegExp); - - if (structureEntry.structureLink !== undefined) { - let structureLinkRegExp = ""; - - switch (structureEntry.structureLink) { - case StructureLinkDistance.None: - structureLinkRegExp = getRegExpExactWhiteSpace(2); - break; - case StructureLinkDistance.Min: - structureLinkRegExp = regExpAnyCharMin; - break; - case StructureLinkDistance.Max: - structureLinkRegExp = regExpAnyCharMax; - break; - } - - regularExp = regularExp.concat(structureLinkRegExp); - } - } - - return regularExp; + let regularExp = ""; + + for (let r = 0; r < structureEntries.length; r++) { + const structureEntry = structureEntries[r]; + + const rowRegExp = getRegExpForLogEntry( + logHeaders, + headerColumnTypes, + structureEntry.row, + r, + structureEntry.cellSelection, + wildcards, + ); + regularExp = regularExp.concat(rowRegExp); + + if (structureEntry.structureLink !== undefined) { + let structureLinkRegExp = ""; + + switch (structureEntry.structureLink) { + case StructureLinkDistance.None: + structureLinkRegExp = getRegExpExactWhiteSpace(2); + break; + case StructureLinkDistance.Min: + structureLinkRegExp = regExpAnyCharMin; + break; + case StructureLinkDistance.Max: + structureLinkRegExp = regExpAnyCharMax; + break; + } + + regularExp = regularExp.concat(structureLinkRegExp); + } + } + + return regularExp; }; export const useGetCharIndicesForLogEntries = (logFileAsString: string): LogEntryCharMaps => { - const perfStart = performance.now(); - const jsonObjectsRegExp = new RegExp(regExpjsonObject, flags); - const firstCharIndexMap = new Map(); - const lastCharIndexMap = new Map(); - let logEntryIndex = 0; - - let result = jsonObjectsRegExp.exec(logFileAsString); - - if (result !== null) { - do { - firstCharIndexMap.set(result.index, logEntryIndex); - lastCharIndexMap.set(jsonObjectsRegExp.lastIndex, logEntryIndex); - logEntryIndex++; - } while ((result = jsonObjectsRegExp.exec(logFileAsString)) !== null); - } - - const perfEnd = performance.now(); - console.log(`Execution time (mapLogFileTextIndicesToObject()): ${perfEnd - perfStart} ms`); - - return { firstCharIndexMap: firstCharIndexMap, lastCharIndexMap: lastCharIndexMap }; + const perfStart = performance.now(); + const jsonObjectsRegExp = new RegExp(regExpjsonObject, flags); + const firstCharIndexMap = new Map(); + const lastCharIndexMap = new Map(); + let logEntryIndex = 0; + + let result = jsonObjectsRegExp.exec(logFileAsString); + + if (result !== null) { + do { + firstCharIndexMap.set(result.index, logEntryIndex); + lastCharIndexMap.set(jsonObjectsRegExp.lastIndex, logEntryIndex); + logEntryIndex++; + } while ((result = jsonObjectsRegExp.exec(logFileAsString)) !== null); + } + + const perfEnd = performance.now(); + console.log(`Execution time (mapLogFileTextIndicesToObject()): ${perfEnd - perfStart} ms`); + + return { firstCharIndexMap: firstCharIndexMap, lastCharIndexMap: lastCharIndexMap }; }; export const useStructureRegularExpressionSearch = ( - expression: string, - logFileAsString: string, - logEntryCharIndexMaps: LogEntryCharMaps, + expression: string, + logFileAsString: string, + logEntryCharIndexMaps: LogEntryCharMaps, ): number[][] => { - console.log("Starting Structure Matching"); - const perfStart = performance.now(); - const textRanges: number[][] = []; - const resultingMatches: number[][] = []; - const structureQuery = new RegExp(expression, flags); - let result; + console.log("Starting Structure Matching"); + const perfStart = performance.now(); + const textRanges: number[][] = []; + const resultingMatches: number[][] = []; + const structureQuery = new RegExp(expression, flags); + let result; + while ((result = structureQuery.exec(logFileAsString)) !== null) { + textRanges.push([result.index, structureQuery.lastIndex]); + } - while ((result = structureQuery.exec(logFileAsString)) !== null) { - textRanges.push([result.index, structureQuery.lastIndex]); - } + const perfEnd = performance.now(); + console.log(`Execution time (regular expression run): ${perfEnd - perfStart} ms`); - const perfEnd = performance.now(); - console.log(`Execution time (regular expression run): ${perfEnd - perfStart} ms`); + console.log(textRanges); - const transStart = performance.now(); + const transStart = performance.now(); - textRanges.forEach((matchRanges) => { - const indexesOfEntriesInMatch: number[] = []; + textRanges.forEach((matchRanges) => { + const indexesOfEntriesInMatch: number[] = []; - const indexOfFirstObjectInMatch = logEntryCharIndexMaps.firstCharIndexMap.get(matchRanges[0]); - const indexOfLastObjectInMatch = logEntryCharIndexMaps.lastCharIndexMap.get(matchRanges[1]); + const indexOfFirstObjectInMatch = logEntryCharIndexMaps.firstCharIndexMap.get(matchRanges[0]); + const indexOfLastObjectInMatch = logEntryCharIndexMaps.lastCharIndexMap.get(matchRanges[1]); - if(indexOfFirstObjectInMatch && indexOfLastObjectInMatch) { - for (let i = indexOfFirstObjectInMatch; i <= indexOfLastObjectInMatch; i++) { - indexesOfEntriesInMatch.push(i); - } - } + if (indexOfFirstObjectInMatch && indexOfLastObjectInMatch) { - resultingMatches.push(indexesOfEntriesInMatch); - }); + for (let i = indexOfFirstObjectInMatch; i <= indexOfLastObjectInMatch; i++) { + indexesOfEntriesInMatch.push(i); + } - const transEnd = performance.now(); - console.log(`Execution time (translation from char indices to logFile.rows indices): ${transEnd - transStart} ms`); + resultingMatches.push(indexesOfEntriesInMatch); + } + }); - return resultingMatches; -}; + const transEnd = performance.now(); + console.log(`Execution time (translation from char indices to logFile.rows indices): ${transEnd - transStart} ms`); + + return resultingMatches; +}; \ No newline at end of file