@Override public DefaultSignificantCode addRange(TextRange range) { Preconditions.checkState(this.inputFile != null, "addRange() should be called after on()"); int line = range.start().line(); Preconditions.checkArgument(line == range.end().line(), "Ranges of significant code must be located in a single line"); Preconditions.checkState(!significantCodePerLine.containsKey(line), "Significant code was already reported for line '%s'. Can only report once per line.", line); significantCodePerLine.put(line, range); return this; }
private static boolean isOdd(FilterableIssue issue) { TextRange textRange = issue.textRange(); return textRange != null && textRange.start().line() % 2 == 1; } }
/** * Return list of symbol references ranges for the symbol at a given position in a file. * * @param componentKey Key of the file like 'myProjectKey:src/foo.php' * @param line Line you want to query * @param lineOffset Offset you want to query. * @return List of references for the symbol (potentially empty) or null if there is no symbol at this position. */ @CheckForNull public Collection<TextRange> referencesForSymbolAt(String componentKey, int line, int lineOffset) { DefaultSymbolTable symbolTable = sensorStorage.symbolsPerComponent.get(componentKey); if (symbolTable == null) { return null; } DefaultTextPointer location = new DefaultTextPointer(line, lineOffset); for (Map.Entry<TextRange, Set<TextRange>> symbol : symbolTable.getReferencesBySymbol().entrySet()) { if (symbol.getKey().start().compareTo(location) <= 0 && symbol.getKey().end().compareTo(location) > 0) { return symbol.getValue(); } } return null; }
@Override public DefaultCpdTokens addToken(TextRange range, String image) { requireNonNull(range, "Range should not be null"); requireNonNull(image, "Image should not be null"); checkInputFileNotNull(); if (isExcludedForDuplication()) { return this; } checkState(lastRange == null || lastRange.end().compareTo(range.start()) <= 0, "Tokens of file %s should be provided in order.\nPrevious token: %s\nLast token: %s", inputFile, lastRange, range); String value = image; int line = range.start().line(); if (line != startLine) { addNewTokensLine(result, startIndex, currentIndex, startLine, sb); startIndex = currentIndex + 1; startLine = line; } currentIndex++; sb.append(value); lastRange = range; return this; }
@Override public boolean overlap(TextRange another) { // [A,B] and [C,D] // B > C && D > A return this.end.compareTo(another.start()) > 0 && another.end().compareTo(this.start) > 0; }
private void checkOverlappingBoundaries() { if (syntaxHighlightingRules.size() > 1) { Iterator<SyntaxHighlightingRule> it = syntaxHighlightingRules.iterator(); SyntaxHighlightingRule previous = it.next(); while (it.hasNext()) { SyntaxHighlightingRule current = it.next(); if (previous.range().end().compareTo(current.range().start()) > 0 && (previous.range().end().compareTo(current.range().end()) < 0)) { String errorMsg = String.format("Cannot register highlighting rule for characters at %s as it " + "overlaps at least one existing rule", current.range()); throw new IllegalStateException(errorMsg); } previous = current; } } }
public void validate(TextRange range) { checkMetadata(); checkValid(range.start(), "start pointer"); checkValid(range.end(), "end pointer"); }
/** * Return list of syntax highlighting applied for a given position in a file. The result is a list because in theory you * can apply several styles to the same range. * * @param componentKey Key of the file like 'myProjectKey:src/foo.php' * @param line Line you want to query * @param lineOffset Offset you want to query. * @return List of styles applied to this position or empty list if there is no highlighting at this position. */ public List<TypeOfText> highlightingTypeAt(String componentKey, int line, int lineOffset) { DefaultHighlighting syntaxHighlightingData = sensorStorage.highlightingByComponent.get(componentKey); if (syntaxHighlightingData == null) { return Collections.emptyList(); } List<TypeOfText> result = new ArrayList<>(); DefaultTextPointer location = new DefaultTextPointer(line, lineOffset); for (SyntaxHighlightingRule sortedRule : syntaxHighlightingData.getSyntaxHighlightingRuleSet()) { if (sortedRule.range().start().compareTo(location) <= 0 && sortedRule.range().end().compareTo(location) > 0) { result.add(sortedRule.getTextType()); } } return result; }
@Override protected void doSave() { checkInputFileNotNull(); // Sort rules to avoid variation during consecutive runs Collections.sort(syntaxHighlightingRules, (left, right) -> { int result = left.range().start().compareTo(right.range().start()); if (result == 0) { result = right.range().end().compareTo(left.range().end()); } return result; }); checkOverlappingBoundaries(); storage.store(this); }
private static ScannerReport.TextRange toProtobufTextRange(ScannerReport.TextRange.Builder textRangeBuilder, TextRange primaryTextRange) { textRangeBuilder.clear(); textRangeBuilder.setStartLine(primaryTextRange.start().line()); textRangeBuilder.setStartOffset(primaryTextRange.start().lineOffset()); textRangeBuilder.setEndLine(primaryTextRange.end().line()); textRangeBuilder.setEndOffset(primaryTextRange.end().lineOffset()); return textRangeBuilder.build(); }
@Override public void store(DefaultSignificantCode significantCode) { ScannerReportWriter writer = reportPublisher.getWriter(); DefaultInputFile inputFile = (DefaultInputFile) significantCode.inputFile(); if (shouldSkipStorage(inputFile)) { return; } inputFile.setPublished(true); int componentRef = inputFile.scannerId(); if (writer.hasComponentData(FileStructure.Domain.SGNIFICANT_CODE, componentRef)) { throw new UnsupportedOperationException( "Trying to save significant code information twice for the same file is not supported: " + significantCode.inputFile()); } List<ScannerReport.LineSgnificantCode> protobuf = significantCode.significantCodePerLine().values().stream() .map(range -> ScannerReport.LineSgnificantCode.newBuilder() .setLine(range.start().line()) .setStartOffset(range.start().lineOffset()) .setEndOffset(range.end().lineOffset()) .build()) .collect(Collectors.toList()); writer.writeComponentSignificantCode(componentRef, protobuf); } }
@Test public void checkValidRangeUsingGlobalOffset() { Metadata metadata = new Metadata(2, 2, "", new int[] {0, 10}, new int[] {9, 15}, 16); DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), MODULE_RELATIVE_PATH, null), f -> f.setMetadata(metadata)); TextRange newRange = file.newRange(10, 13); assertThat(newRange.start().line()).isEqualTo(2); assertThat(newRange.start().lineOffset()).isEqualTo(0); assertThat(newRange.end().line()).isEqualTo(2); assertThat(newRange.end().lineOffset()).isEqualTo(3); }
/** * Get highlighting types at a given position in an inputfile * * @param lineOffset 0-based offset in file */ public List<TypeOfText> highlightingTypeFor(InputFile file, int line, int lineOffset) { int ref = ((DefaultInputComponent) file).scannerId(); if (!reader.hasSyntaxHighlighting(ref)) { return Collections.emptyList(); } TextPointer pointer = file.newPointer(line, lineOffset); List<TypeOfText> result = new ArrayList<>(); try (CloseableIterator<ScannerReport.SyntaxHighlightingRule> it = reader.readComponentSyntaxHighlighting(ref)) { while (it.hasNext()) { ScannerReport.SyntaxHighlightingRule rule = it.next(); TextRange ruleRange = toRange(file, rule.getRange()); if (ruleRange.start().compareTo(pointer) <= 0 && ruleRange.end().compareTo(pointer) > 0) { result.add(ScannerReportUtils.toBatchType(rule.getType())); } } } catch (Exception e) { throw new IllegalStateException("Can't read syntax highlighting for " + file, e); } return result; }
private static boolean noSonar(DefaultInputComponent inputComponent, Issue issue) { TextRange textRange = issue.primaryLocation().textRange(); return inputComponent.isFile() && textRange != null && ((DefaultInputFile) inputComponent).hasNoSonarAt(textRange.start().line()) && !StringUtils.containsIgnoreCase(issue.ruleKey().rule(), "nosonar"); }
@Test public void selectLine() { Metadata metadata = new FileMetadata().readMetadata(new StringReader("bla bla a\nabcde\n\nabc")); DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), MODULE_RELATIVE_PATH, null), f -> f.setMetadata(metadata)); assertThat(file.selectLine(1).start().line()).isEqualTo(1); assertThat(file.selectLine(1).start().lineOffset()).isEqualTo(0); assertThat(file.selectLine(1).end().line()).isEqualTo(1); assertThat(file.selectLine(1).end().lineOffset()).isEqualTo(9); // Don't fail when selecting empty line assertThat(file.selectLine(3).start().line()).isEqualTo(3); assertThat(file.selectLine(3).start().lineOffset()).isEqualTo(0); assertThat(file.selectLine(3).end().line()).isEqualTo(3); assertThat(file.selectLine(3).end().lineOffset()).isEqualTo(0); try { file.selectLine(5); fail(); } catch (Exception e) { assertThat(e).hasMessage("5 is not a valid line for pointer. File src/Foo.php has 4 line(s)"); } }
@Override public void store(DefaultHighlighting highlighting) { ScannerReportWriter writer = reportPublisher.getWriter(); DefaultInputFile inputFile = (DefaultInputFile) highlighting.inputFile(); if (shouldSkipStorage(inputFile)) { return; } inputFile.setPublished(true); int componentRef = inputFile.scannerId(); if (writer.hasComponentData(FileStructure.Domain.SYNTAX_HIGHLIGHTINGS, componentRef)) { throw new UnsupportedOperationException("Trying to save highlighting twice for the same file is not supported: " + inputFile); } final ScannerReport.SyntaxHighlightingRule.Builder builder = ScannerReport.SyntaxHighlightingRule.newBuilder(); final ScannerReport.TextRange.Builder rangeBuilder = ScannerReport.TextRange.newBuilder(); writer.writeComponentSyntaxHighlighting(componentRef, highlighting.getSyntaxHighlightingRuleSet().stream() .map(input -> { builder.setRange(rangeBuilder.setStartLine(input.range().start().line()) .setStartOffset(input.range().start().lineOffset()) .setEndLine(input.range().end().line()) .setEndOffset(input.range().end().lineOffset()) .build()); builder.setType(ScannerReportUtils.toProtocolType(input.getTextType())); return builder.build(); }).collect(toList())); }
@Test public void build_file_issue() { SensorStorage storage = mock(SensorStorage.class); DefaultExternalIssue issue = new DefaultExternalIssue(project, storage) .at(new DefaultIssueLocation() .on(inputFile) .at(inputFile.selectLine(1)) .message("Wrong way!")) .forRule(RuleKey.of("repo", "rule")) .remediationEffortMinutes(10l) .type(RuleType.BUG) .severity(Severity.BLOCKER); assertThat(issue.primaryLocation().inputComponent()).isEqualTo(inputFile); assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("external_repo", "rule")); assertThat(issue.engineId()).isEqualTo("repo"); assertThat(issue.ruleId()).isEqualTo("rule"); assertThat(issue.primaryLocation().textRange().start().line()).isEqualTo(1); assertThat(issue.remediationEffort()).isEqualTo(10l); assertThat(issue.type()).isEqualTo(RuleType.BUG); assertThat(issue.severity()).isEqualTo(Severity.BLOCKER); assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!"); issue.save(); verify(storage).store(issue); }
@Test public void build_file_issue() { SensorStorage storage = mock(SensorStorage.class); DefaultIssue issue = new DefaultIssue(project, storage) .at(new DefaultIssueLocation() .on(inputFile) .at(inputFile.selectLine(1)) .message("Wrong way!")) .forRule(RuleKey.of("repo", "rule")) .gap(10.0); assertThat(issue.primaryLocation().inputComponent()).isEqualTo(inputFile); assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule")); assertThat(issue.primaryLocation().textRange().start().line()).isEqualTo(1); assertThat(issue.gap()).isEqualTo(10.0); assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!"); issue.save(); verify(storage).store(issue); }
@Test public void checkValidRange() { Metadata metadata = new FileMetadata().readMetadata(new StringReader("bla bla a\nabcde")); DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), MODULE_RELATIVE_PATH, null), f -> f.setMetadata(metadata)); assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(2, 1)).start().line()).isEqualTo(1); // Don't fail file.newRange(file.newPointer(1, 0), file.newPointer(1, 1)); file.newRange(file.newPointer(1, 0), file.newPointer(1, 9)); file.newRange(file.newPointer(1, 0), file.newPointer(2, 0)); assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(2, 5))).isEqualTo(file.newRange(0, 15)); try { file.newRange(file.newPointer(1, 0), file.newPointer(1, 0)); fail(); } catch (Exception e) { assertThat(e).hasMessage("Start pointer [line=1, lineOffset=0] should be before end pointer [line=1, lineOffset=0]"); } try { file.newRange(file.newPointer(1, 0), file.newPointer(1, 10)); fail(); } catch (Exception e) { assertThat(e).hasMessage("10 is not a valid line offset for pointer. File src/Foo.php has 9 character(s) at line 1"); } }