From 2a040d5b2b447e4e8bc50e023cda9b3ce1e677fc Mon Sep 17 00:00:00 2001 From: Oleg Kopysov Date: Fri, 5 Apr 2024 17:57:16 +0300 Subject: [PATCH] feat: Add conversion of the byte range in file to file lines Signed-off-by: Oleg Kopysov --- src/main/java/com/lpvs/entity/LPVSFile.java | 74 ++++++ .../lpvs/service/scan/LPVSDetectService.java | 8 +- src/main/resources/database_dump.sql | 19 +- .../java/com/lpvs/entity/LPVSFileTest.java | 52 +++- .../lpvs/service/LPVSGitHubServiceTest.java | 24 +- .../lpvs/service/LPVSLicenseServiceTest.java | 2 + .../lpvs/service/LPVSQueueServiceTest.java | 4 + .../service/scan/LPVSDetectServiceTest.java | 4 +- .../scanner/LPVSScanossDetectServiceTest.java | 16 +- src/test/resources/convert1.txt | 247 ++++++++++++++++++ 10 files changed, 408 insertions(+), 42 deletions(-) create mode 100644 src/test/resources/convert1.txt diff --git a/src/main/java/com/lpvs/entity/LPVSFile.java b/src/main/java/com/lpvs/entity/LPVSFile.java index edc56789..06852da8 100644 --- a/src/main/java/com/lpvs/entity/LPVSFile.java +++ b/src/main/java/com/lpvs/entity/LPVSFile.java @@ -11,7 +11,10 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import java.io.IOException; +import java.io.RandomAccessFile; import java.util.Set; /** @@ -21,6 +24,7 @@ @Setter @AllArgsConstructor @NoArgsConstructor +@Slf4j public class LPVSFile { /** @@ -33,6 +37,11 @@ public class LPVSFile { */ private String filePath; + /** + * The absolute path to the file on the server. + */ + private String absoluteFilePath; + /** * The type of snippet in the file. */ @@ -135,4 +144,69 @@ public String convertLicensesToString(LPVSVcs vcs) { licenseNames = licenseNames.substring(0, licenseNames.length() - 2); return licenseNames; } + + /** + * Converts byte ranges to line numbers in a file. + * + * @return A string representing the start and end line numbers corresponding to the byte ranges. + * Returns an empty string if the input does not start with "BYTES:" or if an error occurs. + */ + public String convertBytesToLinesNumbers() { + + if (matchedLines != null && !matchedLines.startsWith("BYTES:")) { + return matchedLines; + } + + StringBuilder result = new StringBuilder(); + + try (RandomAccessFile sourceFile = new RandomAccessFile(absoluteFilePath, "r")) { + // Skip "BYTES:" before splitting + String[] byteRangeTokens = matchedLines.substring(6).split(":"); + + for (String byteRangeToken : byteRangeTokens) { + String[] range = byteRangeToken.split("-"); + int startByte = Integer.parseInt(range[0]); + int endByte = Integer.parseInt(range[1]); + + // Read lines until reaching the end byte + long byteCounter = 0; + long currentLine = 1; + long startLine = 0; + long endLine = 0; + + // Reset file pointer to the beginning of the file + sourceFile.seek(0); + + while (byteCounter < endByte) { + String line = sourceFile.readLine(); + if (line == null) { + if (startLine > 0 && endLine == 0) endLine = currentLine; + break; + } + byteCounter += line.getBytes().length + 1; + if (byteCounter > startByte && startLine == 0) { + startLine = currentLine; + } + if (byteCounter >= endByte) { + endLine = currentLine; + break; + } + currentLine++; + } + + // Construct the string representing start and end line numbers in the range + if (result.length() > 0) { + result.append(","); + } + result.append(startLine); + result.append("-"); + result.append(endLine); + } + } catch (IOException e) { + log.error(e.getMessage()); + return ""; + } + + return result.toString(); + } } diff --git a/src/main/java/com/lpvs/service/scan/LPVSDetectService.java b/src/main/java/com/lpvs/service/scan/LPVSDetectService.java index a162f4b6..bcb8792d 100644 --- a/src/main/java/com/lpvs/service/scan/LPVSDetectService.java +++ b/src/main/java/com/lpvs/service/scan/LPVSDetectService.java @@ -6,6 +6,7 @@ */ package com.lpvs.service.scan; +import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -159,7 +160,12 @@ public void runOneScan() { public List runScan(LPVSQueue webhookConfig, String path) throws Exception { try { scanService.runScan(webhookConfig, path); - return scanService.checkLicenses(webhookConfig); + List files = scanService.checkLicenses(webhookConfig); + for (LPVSFile file : files) { + file.setAbsoluteFilePath(path + File.separator + file.getFilePath()); + file.setMatchedLines(file.convertBytesToLinesNumbers()); + } + return files; } catch (IllegalArgumentException | NullPointerException ex) { log.error(ex.getMessage()); return new ArrayList<>(); diff --git a/src/main/resources/database_dump.sql b/src/main/resources/database_dump.sql index b154c89c..f3f821a9 100644 --- a/src/main/resources/database_dump.sql +++ b/src/main/resources/database_dump.sql @@ -94,11 +94,14 @@ CREATE TABLE IF NOT EXISTS member ( UNIQUE (email,provider) ); -INSERT INTO license_list (license_name, license_spdx, license_usage) VALUES -('GNU General Public License v3.0 only','GPL-3.0-only','PROHIBITED'), -('OpenSSL License','OpenSSL','PERMITTED'), -('GNU Lesser General Public License v2.0 or later','LGPL-2.0-or-later','RESTRICTED'), -('MIT License', 'MIT', 'PERMITTED'), -('Apache License 2.0', 'Apache-2.0', 'PERMITTED'), -('GNU General Public License v2.0 only', 'GPL-2.0-only', 'RESTRICTED'), -('GNU Lesser General Public License v3.0 or later', 'LGPL-3.0-or-later', 'PROHIBITED'); \ No newline at end of file +INSERT INTO license_list (id, license_name, license_spdx, license_usage) VALUES +(1, 'GNU General Public License v3.0 only','GPL-3.0-only','PROHIBITED'), +(2, 'OpenSSL License','OpenSSL','PERMITTED'), +(3, 'GNU Lesser General Public License v2.0 or later','LGPL-2.0-or-later','RESTRICTED'), +(4, 'MIT License', 'MIT', 'PERMITTED'), +(5, 'Apache License 2.0', 'Apache-2.0', 'PERMITTED'), +(6, 'GNU General Public License v2.0 only', 'GPL-2.0-only', 'RESTRICTED'), +(7, 'GNU Lesser General Public License v3.0 or later', 'LGPL-3.0-or-later', 'PROHIBITED'); + +INSERT INTO license_conflicts (conflict_license_id, repository_license_id) VALUES +(1, 3), (1, 6), (2, 6), (2, 3), (3, 5), (3, 7), (5, 6), (6, 7); diff --git a/src/test/java/com/lpvs/entity/LPVSFileTest.java b/src/test/java/com/lpvs/entity/LPVSFileTest.java index fde5e74b..182796f7 100644 --- a/src/test/java/com/lpvs/entity/LPVSFileTest.java +++ b/src/test/java/com/lpvs/entity/LPVSFileTest.java @@ -6,6 +6,8 @@ */ package com.lpvs.entity; +import java.net.URISyntaxException; +import java.nio.file.Paths; import java.util.*; import com.lpvs.entity.enums.LPVSVcs; @@ -20,6 +22,7 @@ public class LPVSFileTest { LPVSFile lpvsFile; final long baseId = 1L; final String baseFilePath = "baseFilePath"; + final String baseAbsoluteFilePath = "baseAbsoluteFilePath"; final String baseSnippetType = "baseSnippetType"; final String baseSnippetMatch = "baseSnippetMatch"; final String baseMatchedLines = "baseMatchedLines"; @@ -51,6 +54,14 @@ public void getterSetterFilePathTest() { assertEquals(lpvsFile.getFilePath(), baseFilePath); } + @Test + public void getterSetterAbsoluteFilePathTest() { + assertEquals(lpvsFile.getAbsoluteFilePath(), null); + lpvsFile.setAbsoluteFilePath(baseAbsoluteFilePath); + assertNotEquals(lpvsFile.getAbsoluteFilePath(), null); + assertEquals(lpvsFile.getAbsoluteFilePath(), baseAbsoluteFilePath); + } + @Test public void getterSetterSnippetTypeTest() { assertEquals(lpvsFile.getSnippetType(), null); @@ -155,9 +166,6 @@ public void convertLicenseToStringCheckListUrlNullTest() { final String baseSpdxId = "spdxId"; final String baseAccess = "access"; final String baseAlternativeName = "licenseNameAlternative"; - final String baseChecklistUrl = "checklistUrl"; - List baseIncompatibleWith = - Arrays.asList("incompatibleWith1", "incompatibleWith2", "incompatibleWith3"); LPVSLicense lpvsLicense1 = new LPVSLicense( @@ -191,9 +199,6 @@ public void convertLicenseToStringCheckListUrlLicenseRef() { final String baseSpdxId = "spdxId"; final String baseAccess = "access"; final String baseAlternativeName = "licenseNameAlternative"; - final String baseChecklistUrl = "checklistUrl"; - List baseIncompatibleWith = - Arrays.asList("incompatibleWith1", "incompatibleWith2", "incompatibleWith3"); LPVSLicense lpvsLicense3 = new LPVSLicense( @@ -223,8 +228,6 @@ public void convertLicenseToStringCheckListUrlTwoTest() { final String baseAccess = "access"; final String baseAlternativeName = "licenseNameAlternative"; final String baseChecklistUrl = "checklistUrl"; - List baseIncompatibleWith = - Arrays.asList("incompatibleWith1", "incompatibleWith2", "incompatibleWith3"); LPVSLicense lpvsLicense1 = new LPVSLicense( @@ -250,4 +253,37 @@ public void convertLicenseToStringCheckListUrlTwoTest() { lpvsFile.convertLicensesToString(LPVSVcs.GITHUB), "spdxId (access), spdxId (access)"); } + + @Test + public void testConvertBytesToLinesNumbers() throws URISyntaxException { + // Test when matchedLines starts with "BYTES:" + LPVSFile validFile = new LPVSFile(); + + validFile.setAbsoluteFilePath( + Paths.get( + Objects.requireNonNull( + getClass() + .getClassLoader() + .getResource("convert1.txt")) + .toURI()) + .toString()); + validFile.setMatchedLines("BYTES:0-3709:6492-7819"); + String result = validFile.convertBytesToLinesNumbers(); + assertEquals("1-107,208-248", result); + + // Test when matchedLines does not start with "BYTES:" + validFile.setMatchedLines("5-10,15-20"); + result = validFile.convertBytesToLinesNumbers(); + assertEquals("5-10,15-20", result); + } + + @Test + public void testConvertBytesToLinesNumbers_N() throws URISyntaxException { + LPVSFile invalidFile = new LPVSFile(); + // File does not exist + invalidFile.setAbsoluteFilePath("convertX.txt"); + invalidFile.setMatchedLines("BYTES:0-3709:6492-7819"); + String result = invalidFile.convertBytesToLinesNumbers(); + assertEquals("", result); + } } diff --git a/src/test/java/com/lpvs/service/LPVSGitHubServiceTest.java b/src/test/java/com/lpvs/service/LPVSGitHubServiceTest.java index 967e98ec..0eb8da14 100644 --- a/src/test/java/com/lpvs/service/LPVSGitHubServiceTest.java +++ b/src/test/java/com/lpvs/service/LPVSGitHubServiceTest.java @@ -2134,6 +2134,8 @@ class TestCommentResults__ProhibitedPresentConflictsPresent { final String file_url_1 = "https://github.com/Samsung/LPVS/tree/main/src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; + + final String absolute_file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String snippet_type_1 = "snippet"; final String snippet_match_1 = "/**\n" @@ -2239,6 +2241,7 @@ void setUp() { new LPVSFile( 1L, file_path_1, + absolute_file_path_1, snippet_type_1, snippet_match_1, matched_lines_1, @@ -2463,6 +2466,7 @@ class TestCommentResults__EmptyPresentConflictsPresent { final String file_url_1 = "https://github.com/Samsung/LPVS/tree/main/src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; + final String absolute_file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String snippet_type_1 = "snippet"; final String snippet_match_1 = "/**\n" @@ -2566,6 +2570,7 @@ void setUp() { new LPVSFile( 1L, file_path_1, + absolute_file_path_1, snippet_type_1, snippet_match_1, matched_lines_1, @@ -2790,6 +2795,7 @@ class TestCommentResults__UnreviewedPresentConflictsPresent { final String file_url_1 = "https://github.com/Samsung/LPVS/tree/main/src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; + final String absolute_file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String snippet_type_1 = "snippet"; final String snippet_match_1 = "/**\n" @@ -2896,6 +2902,7 @@ void setUp() { new LPVSFile( 1L, file_path_1, + absolute_file_path_1, snippet_type_1, snippet_match_1, matched_lines_1, @@ -3120,6 +3127,7 @@ class TestCommentResults__RestrictedPresentConflictsPresent { final String file_url_1 = "https://github.com/Samsung/LPVS/tree/main/src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; + final String absolute_file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String snippet_type_1 = "snippet"; final String snippet_match_1 = "/**\n" @@ -3226,6 +3234,7 @@ void setUp() { new LPVSFile( 1L, file_path_1, + absolute_file_path_1, snippet_type_1, snippet_match_1, matched_lines_1, @@ -3442,6 +3451,7 @@ class TestCommentResults__ProhibitedAbsentConflictsAbsent { final String file_url_1 = "https://github.com/Samsung/LPVS/tree/main/src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; + final String absolute_file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String snippet_type_1 = "snippet"; final String snippet_match_1 = "/**\n" @@ -3534,6 +3544,7 @@ void setUp() { new LPVSFile( 1L, file_path_1, + absolute_file_path_1, snippet_type_1, snippet_match_1, matched_lines_1, @@ -4100,6 +4111,7 @@ class TestGetMatchedLinesAsLink_NotAll { // `lpvs_file_1` LPVSFile lpvs_file_1; final String file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; + final String absolute_file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String matched_lines_1 = "1-6"; final String expected_result = @@ -4116,6 +4128,7 @@ void setUp() { new LPVSFile( 1L, file_path_1, + absolute_file_path_1, "snippet", null, matched_lines_1, @@ -4149,6 +4162,7 @@ class TestGetMatchedLinesAsLink_All { // `lpvs_file_1` LPVSFile lpvs_file_1; final String file_path_1 = "LICENSE"; + final String absolute_file_path_1 = "LICENSE"; final String matched_lines_1 = "all"; final String expected_result = @@ -4165,6 +4179,7 @@ void setUp() { new LPVSFile( 1L, file_path_1, + absolute_file_path_1, "snippet", null, matched_lines_1, @@ -4254,6 +4269,7 @@ public void testCommentResults() throws Exception { } }); file.setFilePath(""); + file.setAbsoluteFilePath(""); file.setComponentFilePath(""); file.setComponentName(""); file.setComponentLines(""); @@ -4340,14 +4356,6 @@ public void testCheckEmpty() LPVSExitHandler exitHandler = mock(LPVSExitHandler.class); LPVSGitHubConnectionService lpvsGitHubConnectionService = new LPVSGitHubConnectionService("", "", "", exitHandler); - - final LPVSGitHubService gh_service = - new LPVSGitHubService( - mocked_pullRequestRepository, - mocked_lpvsDetectedLicenseRepository, - mocked_lpvsLicenseRepository, - mocked_lpvsLicenseConflictRepository, - lpvsGitHubConnectionService); Method method = lpvsGitHubConnectionService.getClass().getDeclaredMethod("checks"); method.setAccessible(true); method.invoke(lpvsGitHubConnectionService); diff --git a/src/test/java/com/lpvs/service/LPVSLicenseServiceTest.java b/src/test/java/com/lpvs/service/LPVSLicenseServiceTest.java index 10bcaa52..f2f83a12 100644 --- a/src/test/java/com/lpvs/service/LPVSLicenseServiceTest.java +++ b/src/test/java/com/lpvs/service/LPVSLicenseServiceTest.java @@ -433,6 +433,7 @@ void setUp() throws NoSuchFieldException, IllegalAccessException { null, null, null, + null, Set.of(lpvs_license_1), null, null, @@ -450,6 +451,7 @@ void setUp() throws NoSuchFieldException, IllegalAccessException { null, null, null, + null, Set.of(lpvs_license_2), null, null, diff --git a/src/test/java/com/lpvs/service/LPVSQueueServiceTest.java b/src/test/java/com/lpvs/service/LPVSQueueServiceTest.java index e71ea211..2a961a8e 100644 --- a/src/test/java/com/lpvs/service/LPVSQueueServiceTest.java +++ b/src/test/java/com/lpvs/service/LPVSQueueServiceTest.java @@ -207,6 +207,7 @@ public void testProcessWebHook__NoPRDownloaded() throws Exception { static final Long id_1 = 1L; static final String fileUrl_1 = "test_file_url_1"; static final String filePath_1 = "test_file_path_1"; + static final String absolute_filePath_1 = "test_file_path_1"; static final String snippetType_1 = "test_snippet_type_1"; static final String snippetMatch_1 = "test_snippet_match_1"; static final String matchedLines_1 = "test_matched_lines_1"; @@ -217,6 +218,7 @@ public void testProcessWebHook__NoPRDownloaded() throws Exception { new LPVSFile( id_1, filePath_1, + absolute_filePath_1, snippetType_1, snippetMatch_1, matchedLines_1, @@ -233,6 +235,7 @@ public void testProcessWebHook__NoPRDownloaded() throws Exception { static final Long id_2 = 2L; static final String fileUrl_2 = "test_file_url_2"; static final String filePath_2 = "test_file_path_2"; + static final String absoluteFilePath_2 = "test_file_path_2"; static final String snippetType_2 = "test_snippet_type_2"; static final String snippetMatch_2 = "test_snippet_match_2"; static final String matchedLines_2 = "test_matched_lines_2"; @@ -243,6 +246,7 @@ public void testProcessWebHook__NoPRDownloaded() throws Exception { new LPVSFile( id_2, filePath_2, + absoluteFilePath_2, snippetType_2, snippetMatch_2, matchedLines_2, diff --git a/src/test/java/com/lpvs/service/scan/LPVSDetectServiceTest.java b/src/test/java/com/lpvs/service/scan/LPVSDetectServiceTest.java index 092c7e92..19befd51 100644 --- a/src/test/java/com/lpvs/service/scan/LPVSDetectServiceTest.java +++ b/src/test/java/com/lpvs/service/scan/LPVSDetectServiceTest.java @@ -86,11 +86,11 @@ void setUp() throws IOException { lpvs_file_1 = new LPVSFile( 1L, null, null, null, null, null, null, null, null, null, null, null, - null); + null, null); lpvs_file_2 = new LPVSFile( 2L, null, null, null, null, null, null, null, null, null, null, null, - null); + null, null); when(scanoss_mock.checkLicenses(webhookConfig)) .thenReturn(List.of(lpvs_file_1, lpvs_file_2)); diff --git a/src/test/java/com/lpvs/service/scan/scanner/LPVSScanossDetectServiceTest.java b/src/test/java/com/lpvs/service/scan/scanner/LPVSScanossDetectServiceTest.java index ede7cf9e..7419c4c5 100644 --- a/src/test/java/com/lpvs/service/scan/scanner/LPVSScanossDetectServiceTest.java +++ b/src/test/java/com/lpvs/service/scan/scanner/LPVSScanossDetectServiceTest.java @@ -80,10 +80,6 @@ public void tearDown() { @Test public void testCheckLicense() { - LPVSLicenseService licenseService = Mockito.mock(LPVSLicenseService.class); - LPVSLicenseRepository lpvsLicenseRepository = Mockito.mock(LPVSLicenseRepository.class); - LPVSScanossDetectService scanossDetectService = - new LPVSScanossDetectService(false, licenseService, lpvsLicenseRepository); String licenseConflictsSource = "scanner"; Mockito.when(lpvsQueue.getHeadCommitSHA()).thenReturn("A_B"); Mockito.when(lpvsQueue.getRepositoryUrl()).thenReturn("https://github.com/Samsung/LPVS"); @@ -92,16 +88,11 @@ public void testCheckLicense() { .thenAnswer(i -> i.getArguments()[0]); ReflectionTestUtils.setField( licenseService, "licenseConflictsSource", licenseConflictsSource); - scanossDetectService.checkLicenses(lpvsQueue); Assertions.assertNotNull(scanossDetectService.checkLicenses(lpvsQueue)); } @Test public void testCheckLicenseHeadCommitSHA() { - LPVSLicenseService licenseService = Mockito.mock(LPVSLicenseService.class); - LPVSLicenseRepository lpvsLicenseRepository = Mockito.mock(LPVSLicenseRepository.class); - LPVSScanossDetectService scanossDetectService = - new LPVSScanossDetectService(false, licenseService, lpvsLicenseRepository); String licenseConflictsSource = "scanner"; LPVSQueue webhookConfig = Mockito.mock(LPVSQueue.class); Mockito.when(webhookConfig.getHeadCommitSHA()).thenReturn("A_B"); @@ -113,16 +104,11 @@ public void testCheckLicenseHeadCommitSHA() { ReflectionTestUtils.setField( licenseService, "licenseConflictsSource", licenseConflictsSource); webhookConfig.setHeadCommitSHA(""); - scanossDetectService.checkLicenses(webhookConfig); Assertions.assertNotNull(scanossDetectService.checkLicenses(webhookConfig)); } @Test public void testWithNullHeadCommitSHA() { - LPVSLicenseService licenseService = Mockito.mock(LPVSLicenseService.class); - LPVSLicenseRepository lpvsLicenseRepository = Mockito.mock(LPVSLicenseRepository.class); - LPVSScanossDetectService scanossDetectService = - new LPVSScanossDetectService(false, licenseService, lpvsLicenseRepository); String licenseConflictsSource = "scanner"; LPVSQueue webhookConfig = Mockito.mock(LPVSQueue.class); Mockito.when(webhookConfig.getHeadCommitSHA()).thenReturn(null); @@ -157,7 +143,7 @@ public void testRunScan_StatusEqualsOne() throws Exception { Process process = Mockito.mock(Process.class); InputStream errorStream = new ByteArrayInputStream( - "Scanoss scanner terminated with none-zero code. Terminating.".getBytes()); + "Scanoss scanner terminated with non-zero code. Terminating.".getBytes()); try (MockedConstruction mockedPb = Mockito.mockConstruction( diff --git a/src/test/resources/convert1.txt b/src/test/resources/convert1.txt new file mode 100644 index 00000000..1cffc000 --- /dev/null +++ b/src/test/resources/convert1.txt @@ -0,0 +1,247 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j; + +import java.io.Serializable; +import java.util.Iterator; + +/** + * Markers are named objects used to enrich log statements. Conforming logging + * system implementations of SLF4J should determine how information conveyed by + * any markers are used, if at all. Many conforming logging systems ignore marker + * data entirely. + * + *

Markers can contain references to nested markers, which in turn may + * contain references of their own. Note that the fluent API (new in 2.0) allows adding + * multiple markers to a logging statement. It is often preferable to use + * multiple markers instead of nested markers. + *

+ * + * @author Ceki Gülcü + */ +public interface Marker extends Serializable { + + /** + * This constant represents any marker, including a null marker. + */ + public final String ANY_MARKER = "*"; + + /** + * This constant represents any non-null marker. + */ + public final String ANY_NON_NULL_MARKER = "+"; + + /** + * Get the name of this Marker. + * + * @return name of marker + */ + public String getName(); + + /** + * Add a reference to another Marker. + * + *

Note that the fluent API allows adding multiple markers to a logging statement. + * It is often preferable to use multiple markers instead of nested markers. + *

+ * + * @param reference + * a reference to another marker + * @throws IllegalArgumentException + * if 'reference' is null + */ + public void add(Marker reference); + + /** + * Remove a marker reference. + * + * @param reference + * the marker reference to remove + * @return true if reference could be found and removed, false otherwise. + */ + public boolean remove(Marker reference); + + /** + * @deprecated Replaced by {@link #hasReferences()}. + */ + @Deprecated + public boolean hasChildren(); + + /** + * Does this marker have any references? + * + * @return true if this marker has one or more references, false otherwise. + */ + public boolean hasReferences(); + + /** + * Returns an Iterator which can be used to iterate over the references of this + * marker. An empty iterator is returned when this marker has no references. + * + * @return Iterator over the references of this marker + */ + public Iterator iterator(); + +private Node(ProgramPoint programPoint, @Nullable ProgramState programState, ExplodedGraph explodedGraph) { + Objects.requireNonNull(programPoint); + this.programPoint = programPoint; + this.programState = programState; + this.explodedGraph = explodedGraph; + hashcode = programPoint.hashCode() * 31 + (programState == null ? 0 : programState.hashCode()); + } + + public void addParent(@Nullable Node parent, @Nullable MethodYield methodYield) { + if (parent == null) { + return; + } + Edge edge = edges.computeIfAbsent(parent, p -> new Edge(this, p)); + if (methodYield != null) { + Preconditions.checkState(parent.programPoint.syntaxTree().is(Tree.Kind.METHOD_INVOCATION), "Yield on edge where parent is not MIT"); + edge.yields.add(methodYield); + } + } + + public Collection siblings() { + Collection collection = explodedGraph.nodesByProgramPoint.getOrDefault(programPoint, Collections.emptyList()); + collection.remove(this); + return collection; + } + + @Nullable + public Node parent() { + return parents().stream().findFirst().orElse(null); + } + + /** + * @return the ordered (by insertion) sets of parents + */ + public Set parents() { + return edges.keySet(); + } + + @Override + public int hashCode() { + return hashcode; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Node) { + Node other = (Node) obj; + return this.programPoint.equals(other.programPoint) + && Objects.equals(this.programState, other.programState); + } + return false; + } + + @Override + public String toString() { + return "B" + programPoint.block.id() + "." + programPoint.i + ": " + programState; + } + + public Collection edges() { + return edges.values(); + } + + public boolean isNew() { + return isNew; + } + } + + public static final class Edge { + final Node child; + final Node parent; + final int hashcode; + + private Set lc; + private Set la; + private final Set yields = new LinkedHashSet<>(); + + private Edge(Node child, Node parent) { + Preconditions.checkState(!child.equals(parent)); + this.child = child; + this.parent = parent; + hashcode = Objects.hash(child, parent); + } + + public Node child() { + return child; + } + + public Node parent() { + return parent; + } + + public Set learnedConstraints() { + if (lc == null) { + lc = child.programState.learnedConstraints(parent.programState); + } + return lc; + } + + + /** + * Does this marker contain a reference to the 'other' marker? Marker A is defined + * to contain marker B, if A == B or if B is referenced by A, or if B is referenced + * by any one of A's references (recursively). + * + * @param other + * The marker to test for inclusion. + * @throws IllegalArgumentException + * if 'other' is null + * @return Whether this marker contains the other marker. + */ + public boolean contains(Marker other); + + /** + * Does this marker contain the marker named 'name'? + * + * If 'name' is null the returned value is always false. + * + * @param name The marker name to test for inclusion. + * @return Whether this marker contains the other marker. + */ + public boolean contains(String name); + + /** + * Markers are considered equal if they have the same name. + * + * @param o + * @return true, if this.name equals o.name + * + * @since 1.5.1 + */ + public boolean equals(Object o); + + /** + * Compute the hash code based on the name of this marker. + * Note that markers are considered equal if they have the same name. + * + * @return the computed hashCode + * @since 1.5.1 + */ + public int hashCode(); + +}