]> source.dussan.org Git - sonarqube.git/commitdiff
Extract implementation from plugin API - Scanner FS
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Tue, 4 Jun 2019 14:24:58 +0000 (09:24 -0500)
committerSonarTech <sonartech@sonarsource.com>
Fri, 12 Jul 2019 18:21:13 +0000 (20:21 +0200)
98 files changed:
sonar-application/build.gradle
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbsolutePathPredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbstractFilePredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AndPredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFilePredicates.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultIndexedFile.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FalsePredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileExtensionPredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FilenamePredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/LanguagePredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/NotPredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OperatorPredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OptimizedFilePredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OptimizedFilePredicateAdapter.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OrPredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/RelativePathPredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/StatusPredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TruePredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TypePredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/URIPredicate.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/CharHandler.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/FileHashComputer.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/IntArrayList.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineCounter.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineHashComputer.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineOffsetCounter.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/package-info.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRules.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/RulesBuilder.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/Sensor.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/InMemorySensorStorage.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/scanner/sensor/ProjectSensor.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/AbstractProjectOrModule.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultFileSystem.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultIndexedFile.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputComponent.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputDir.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputFile.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputModule.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputProject.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultTextPointer.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultTextRange.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/FileMetadata.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/InputModuleHierarchy.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/PathPattern.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/SensorStrategy.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/TestInputFileBuilder.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/CharHandler.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/FileHashComputer.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/IntArrayList.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/LineCounter.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/LineHashComputer.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/LineOffsetCounter.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/package-info.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/package-info.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/AbsolutePathPredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/AbstractFilePredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/AndPredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/DefaultFilePredicates.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/FalsePredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/FileExtensionPredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/FilenamePredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/LanguagePredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/NotPredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/OperatorPredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/OptimizedFilePredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/OptimizedFilePredicateAdapter.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/OrPredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/PathPatternPredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/RelativePathPredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/StatusPredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/TruePredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/TypePredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/URIPredicate.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/AbstractDefaultIssue.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssue.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssueLocation.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoader.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScanner.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultRules.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/RulesBuilder.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/AdditionalFilePredicates.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/DefaultModuleFileSystem.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/DefaultProjectFileSystem.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/MetadataGenerator.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStore.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/InMemorySensorStorage.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/SensorContextTester.java [new file with mode: 0644]

index 235301072f6d55c378f1f127fda9600463b5cc2d..b6ec4aae2d35e26b76fbf7a07fb22e5ead8f0bd5 100644 (file)
@@ -203,8 +203,8 @@ zip.doFirst {
 }
 // Check the size of the archive
 zip.doLast {
-  def minLength = 198000000
-  def maxLength = 208000000
+  def minLength = 185000000
+  def maxLength = 199000000
   def length = archiveFile.get().asFile.length()
   if (length < minLength)
     throw new GradleException("$archiveName size ($length) too small. Min is $minLength")
index 701a218234848e71c9e23d8646ac56c768743a80..d29918e3dc114d1d35a038148c34e64dcfc1fc02 100644 (file)
@@ -25,12 +25,11 @@ import java.io.InputStream;
 import java.nio.charset.Charset;
 import java.nio.file.Path;
 import javax.annotation.CheckForNull;
-import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
 import org.sonar.api.batch.sensor.SensorDescriptor;
 
 /**
  * This layer over {@link java.io.File} adds information for code analyzers.
- * For unit testing purpose, use {@link TestInputFileBuilder} and initialize
+ * For unit testing purpose, use TestInputFileBuilder and initialize
  * the needed fields:
  * 
  * <pre>
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbsolutePathPredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbsolutePathPredicate.java
deleted file mode 100644 (file)
index 89af60d..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import org.sonar.api.batch.fs.FileSystem.Index;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.scan.filesystem.PathResolver;
-import org.sonar.api.utils.PathUtils;
-
-import java.io.File;
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.Collections;
-
-/**
- * @since 4.2
- */
-class AbsolutePathPredicate extends AbstractFilePredicate {
-
-  private final String path;
-  private final Path baseDir;
-
-  AbsolutePathPredicate(String path, Path baseDir) {
-    this.baseDir = baseDir;
-    this.path = PathUtils.sanitize(path);
-  }
-
-  @Override
-  public boolean apply(InputFile f) {
-    return path.equals(f.absolutePath());
-  }
-
-  @Override
-  public Iterable<InputFile> get(Index index) {
-    String relative = PathUtils.sanitize(new PathResolver().relativePath(baseDir.toFile(), new File(path)));
-    if (relative == null) {
-      return Collections.emptyList();
-    }
-    InputFile f = index.inputFile(relative);
-    return f != null ? Arrays.asList(f) : Collections.<InputFile>emptyList();
-  }
-
-  @Override
-  public int priority() {
-    return USE_INDEX;
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbstractFilePredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbstractFilePredicate.java
deleted file mode 100644 (file)
index 62f8355..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import java.util.stream.StreamSupport;
-import org.sonar.api.batch.fs.FileSystem.Index;
-import org.sonar.api.batch.fs.InputFile;
-
-/**
- * Partial implementation of {@link OptimizedFilePredicate}.
- * @since 5.1
- */
-public abstract class AbstractFilePredicate implements OptimizedFilePredicate {
-
-  protected static final int DEFAULT_PRIORITY = 10;
-  protected static final int USE_INDEX = 20;
-
-  @Override
-  public Iterable<InputFile> filter(Iterable<InputFile> target) {
-    return () -> StreamSupport.stream(target.spliterator(), false)
-      .filter(this::apply)
-      .iterator();
-  }
-
-  @Override
-  public Iterable<InputFile> get(Index index) {
-    return filter(index.inputFiles());
-  }
-
-  @Override
-  public int priority() {
-    return DEFAULT_PRIORITY;
-  }
-
-  @Override
-  public final int compareTo(OptimizedFilePredicate o) {
-    return o.priority() - priority();
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AndPredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AndPredicate.java
deleted file mode 100644 (file)
index 932e2a2..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import org.sonar.api.batch.fs.FilePredicate;
-import org.sonar.api.batch.fs.FileSystem.Index;
-import org.sonar.api.batch.fs.InputFile;
-
-import static java.util.stream.Collectors.toList;
-
-/**
- * @since 4.2
- */
-class AndPredicate extends AbstractFilePredicate implements OperatorPredicate {
-
-  private final List<OptimizedFilePredicate> predicates = new ArrayList<>();
-
-  private AndPredicate() {
-  }
-
-  public static FilePredicate create(Collection<FilePredicate> predicates) {
-    if (predicates.isEmpty()) {
-      return TruePredicate.TRUE;
-    }
-    AndPredicate result = new AndPredicate();
-    for (FilePredicate filePredicate : predicates) {
-      if (filePredicate == TruePredicate.TRUE) {
-        continue;
-      } else if (filePredicate == FalsePredicate.FALSE) {
-        return FalsePredicate.FALSE;
-      } else if (filePredicate instanceof AndPredicate) {
-        result.predicates.addAll(((AndPredicate) filePredicate).predicates);
-      } else {
-        result.predicates.add(OptimizedFilePredicateAdapter.create(filePredicate));
-      }
-    }
-    Collections.sort(result.predicates);
-    return result;
-  }
-
-  @Override
-  public boolean apply(InputFile f) {
-    for (OptimizedFilePredicate predicate : predicates) {
-      if (!predicate.apply(f)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  @Override
-  public Iterable<InputFile> filter(Iterable<InputFile> target) {
-    Iterable<InputFile> result = target;
-    for (OptimizedFilePredicate predicate : predicates) {
-      result = predicate.filter(result);
-    }
-    return result;
-  }
-
-  @Override
-  public Iterable<InputFile> get(Index index) {
-    if (predicates.isEmpty()) {
-      return index.inputFiles();
-    }
-    // Optimization, use get on first predicate then filter with next predicates
-    Iterable<InputFile> result = predicates.get(0).get(index);
-    for (int i = 1; i < predicates.size(); i++) {
-      result = predicates.get(i).filter(result);
-    }
-    return result;
-  }
-
-  Collection<OptimizedFilePredicate> predicates() {
-    return predicates;
-  }
-
-  @Override
-  public List<FilePredicate> operands() {
-    return predicates.stream().map(p -> (FilePredicate) p).collect(toList());
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFilePredicates.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFilePredicates.java
deleted file mode 100644 (file)
index 4d36ce6..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import java.io.File;
-import java.net.URI;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import org.sonar.api.batch.fs.FilePredicate;
-import org.sonar.api.batch.fs.FilePredicates;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.InputFile.Status;
-
-/**
- * Factory of {@link org.sonar.api.batch.fs.FilePredicate}
- *
- * @since 4.2
- */
-public class DefaultFilePredicates implements FilePredicates {
-
-  private final Path baseDir;
-
-  /**
-   * Client code should use {@link org.sonar.api.batch.fs.FileSystem#predicates()} to get an instance
-   */
-  public DefaultFilePredicates(Path baseDir) {
-    this.baseDir = baseDir;
-  }
-
-  /**
-   * Returns a predicate that always evaluates to true
-   */
-  @Override
-  public FilePredicate all() {
-    return TruePredicate.TRUE;
-  }
-
-  /**
-   * Returns a predicate that always evaluates to false
-   */
-  @Override
-  public FilePredicate none() {
-    return FalsePredicate.FALSE;
-  }
-
-  @Override
-  public FilePredicate hasAbsolutePath(String s) {
-    return new AbsolutePathPredicate(s, baseDir);
-  }
-
-  /**
-   * non-normalized path and Windows-style path are supported
-   */
-  @Override
-  public FilePredicate hasRelativePath(String s) {
-    return new RelativePathPredicate(s);
-  }
-
-  @Override
-  public FilePredicate hasFilename(String s) {
-    return new FilenamePredicate(s);
-  }
-
-  @Override
-  public FilePredicate hasExtension(String s) {
-    return new FileExtensionPredicate(s);
-  }
-
-  @Override
-  public FilePredicate hasURI(URI uri) {
-    return new URIPredicate(uri, baseDir);
-  }
-
-  @Override
-  public FilePredicate matchesPathPattern(String inclusionPattern) {
-    return new PathPatternPredicate(PathPattern.create(inclusionPattern));
-  }
-
-  @Override
-  public FilePredicate matchesPathPatterns(String[] inclusionPatterns) {
-    if (inclusionPatterns.length == 0) {
-      return TruePredicate.TRUE;
-    }
-    FilePredicate[] predicates = new FilePredicate[inclusionPatterns.length];
-    for (int i = 0; i < inclusionPatterns.length; i++) {
-      predicates[i] = new PathPatternPredicate(PathPattern.create(inclusionPatterns[i]));
-    }
-    return or(predicates);
-  }
-
-  @Override
-  public FilePredicate doesNotMatchPathPattern(String exclusionPattern) {
-    return not(matchesPathPattern(exclusionPattern));
-  }
-
-  @Override
-  public FilePredicate doesNotMatchPathPatterns(String[] exclusionPatterns) {
-    if (exclusionPatterns.length == 0) {
-      return TruePredicate.TRUE;
-    }
-    return not(matchesPathPatterns(exclusionPatterns));
-  }
-
-  @Override
-  public FilePredicate hasPath(String s) {
-    File file = new File(s);
-    if (file.isAbsolute()) {
-      return hasAbsolutePath(s);
-    }
-    return hasRelativePath(s);
-  }
-
-  @Override
-  public FilePredicate is(File ioFile) {
-    if (ioFile.isAbsolute()) {
-      return hasAbsolutePath(ioFile.getAbsolutePath());
-    }
-    return hasRelativePath(ioFile.getPath());
-  }
-
-  @Override
-  public FilePredicate hasLanguage(String language) {
-    return new LanguagePredicate(language);
-  }
-
-  @Override
-  public FilePredicate hasLanguages(Collection<String> languages) {
-    List<FilePredicate> list = new ArrayList<>();
-    for (String language : languages) {
-      list.add(hasLanguage(language));
-    }
-    return or(list);
-  }
-
-  @Override
-  public FilePredicate hasLanguages(String... languages) {
-    List<FilePredicate> list = new ArrayList<>();
-    for (String language : languages) {
-      list.add(hasLanguage(language));
-    }
-    return or(list);
-  }
-
-  @Override
-  public FilePredicate hasType(InputFile.Type type) {
-    return new TypePredicate(type);
-  }
-
-  @Override
-  public FilePredicate not(FilePredicate p) {
-    return new NotPredicate(p);
-  }
-
-  @Override
-  public FilePredicate or(Collection<FilePredicate> or) {
-    return OrPredicate.create(or);
-  }
-
-  @Override
-  public FilePredicate or(FilePredicate... or) {
-    return OrPredicate.create(Arrays.asList(or));
-  }
-
-  @Override
-  public FilePredicate or(FilePredicate first, FilePredicate second) {
-    return OrPredicate.create(Arrays.asList(first, second));
-  }
-
-  @Override
-  public FilePredicate and(Collection<FilePredicate> and) {
-    return AndPredicate.create(and);
-  }
-
-  @Override
-  public FilePredicate and(FilePredicate... and) {
-    return AndPredicate.create(Arrays.asList(and));
-  }
-
-  @Override
-  public FilePredicate and(FilePredicate first, FilePredicate second) {
-    return AndPredicate.create(Arrays.asList(first, second));
-  }
-
-  @Override
-  public FilePredicate hasStatus(Status status) {
-    return new StatusPredicate(status);
-  }
-
-  @Override
-  public FilePredicate hasAnyStatus() {
-    return new StatusPredicate(null);
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java
deleted file mode 100644 (file)
index 3b9b7ac..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.stream.StreamSupport;
-import org.sonar.api.batch.fs.FilePredicate;
-import org.sonar.api.batch.fs.FilePredicates;
-import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.batch.fs.InputDir;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.scan.filesystem.PathResolver;
-import org.sonar.api.utils.PathUtils;
-
-/**
- * @since 4.2
- */
-public class DefaultFileSystem implements FileSystem {
-
-  private final Cache cache;
-  private final Path baseDir;
-  private Path workDir;
-  private Charset encoding;
-  protected final FilePredicates predicates;
-
-  /**
-   * Only for testing
-   */
-  public DefaultFileSystem(Path baseDir) {
-    this(baseDir, new MapCache());
-  }
-
-  /**
-   * Only for testing
-   */
-  public DefaultFileSystem(File baseDir) {
-    this(baseDir.toPath(), new MapCache());
-  }
-
-  protected DefaultFileSystem(Path baseDir, Cache cache) {
-    this.baseDir = baseDir;
-    this.cache = cache;
-    this.predicates = new DefaultFilePredicates(this.baseDir);
-  }
-
-  public Path baseDirPath() {
-    return baseDir;
-  }
-
-  @Override
-  public File baseDir() {
-    return baseDir.toFile();
-  }
-
-  public DefaultFileSystem setEncoding(Charset e) {
-    this.encoding = e;
-    return this;
-  }
-
-  @Override
-  public Charset encoding() {
-    return encoding;
-  }
-
-  public DefaultFileSystem setWorkDir(Path d) {
-    this.workDir = d;
-    return this;
-  }
-
-  @Override
-  public File workDir() {
-    return workDir.toFile();
-  }
-
-  @Override
-  public InputFile inputFile(FilePredicate predicate) {
-    Iterable<InputFile> files = inputFiles(predicate);
-    Iterator<InputFile> iterator = files.iterator();
-    if (!iterator.hasNext()) {
-      return null;
-    }
-    InputFile first = iterator.next();
-    if (!iterator.hasNext()) {
-      return first;
-    }
-
-    StringBuilder sb = new StringBuilder();
-    sb.append("expected one element but was: <" + first);
-    for (int i = 0; i < 4 && iterator.hasNext(); i++) {
-      sb.append(", " + iterator.next());
-    }
-    if (iterator.hasNext()) {
-      sb.append(", ...");
-    }
-    sb.append('>');
-
-    throw new IllegalArgumentException(sb.toString());
-
-  }
-
-  public Iterable<InputFile> inputFiles() {
-    return inputFiles(predicates.all());
-  }
-
-  @Override
-  public Iterable<InputFile> inputFiles(FilePredicate predicate) {
-    return OptimizedFilePredicateAdapter.create(predicate).get(cache);
-  }
-
-  @Override
-  public boolean hasFiles(FilePredicate predicate) {
-    return inputFiles(predicate).iterator().hasNext();
-  }
-
-  @Override
-  public Iterable<File> files(FilePredicate predicate) {
-    return () -> StreamSupport.stream(inputFiles(predicate).spliterator(), false)
-      .map(InputFile::file)
-      .iterator();
-  }
-
-  @Override
-  public InputDir inputDir(File dir) {
-    String relativePath = PathUtils.sanitize(new PathResolver().relativePath(baseDir.toFile(), dir));
-    if (relativePath == null) {
-      return null;
-    }
-    // Issues on InputDir are moved to the project, so we just return a fake InputDir for backward compatibility
-    return new DefaultInputDir("unused", relativePath).setModuleBaseDir(baseDir);
-  }
-
-  public DefaultFileSystem add(InputFile inputFile) {
-    cache.add(inputFile);
-    return this;
-  }
-
-  @Override
-  public SortedSet<String> languages() {
-    return cache.languages();
-  }
-
-  @Override
-  public FilePredicates predicates() {
-    return predicates;
-  }
-
-  public abstract static class Cache implements Index {
-
-    protected abstract void doAdd(InputFile inputFile);
-
-    final void add(InputFile inputFile) {
-      doAdd(inputFile);
-    }
-
-    protected abstract SortedSet<String> languages();
-  }
-
-  /**
-   * Used only for testing
-   */
-  private static class MapCache extends Cache {
-    private final Map<String, InputFile> fileMap = new HashMap<>();
-    private final Map<String, Set<InputFile>> filesByNameCache = new HashMap<>();
-    private final Map<String, Set<InputFile>> filesByExtensionCache = new HashMap<>();
-    private SortedSet<String> languages = new TreeSet<>();
-
-    @Override
-    public Iterable<InputFile> inputFiles() {
-      return new ArrayList<>(fileMap.values());
-    }
-
-    @Override
-    public InputFile inputFile(String relativePath) {
-      return fileMap.get(relativePath);
-    }
-
-    @Override
-    public Iterable<InputFile> getFilesByName(String filename) {
-      return filesByNameCache.get(filename);
-    }
-
-    @Override
-    public Iterable<InputFile> getFilesByExtension(String extension) {
-      return filesByExtensionCache.get(extension);
-    }
-
-    @Override
-    protected void doAdd(InputFile inputFile) {
-      if (inputFile.language() != null) {
-        languages.add(inputFile.language());
-      }
-      fileMap.put(inputFile.relativePath(), inputFile);
-      filesByNameCache.computeIfAbsent(inputFile.filename(), x -> new HashSet<>()).add(inputFile);
-      filesByExtensionCache.computeIfAbsent(FileExtensionPredicate.getExtension(inputFile), x -> new HashSet<>()).add(inputFile);
-    }
-
-    @Override
-    protected SortedSet<String> languages() {
-      return languages;
-    }
-  }
-
-  @Override
-  public File resolvePath(String path) {
-    File file = new File(path);
-    if (!file.isAbsolute()) {
-      try {
-        file = new File(baseDir(), path).getCanonicalFile();
-      } catch (IOException e) {
-        throw new IllegalArgumentException("Unable to resolve path '" + path + "'", e);
-      }
-    }
-    return file;
-  }
-}
index 1488121129c79463cfc08261ea47a6deefa22aa3..ba7cb94cff3cb071a17d6d3627a1a2d7a34f3c8e 100644 (file)
@@ -45,14 +45,6 @@ public class DefaultIndexedFile extends DefaultInputComponent implements Indexed
   private final Path absolutePath;
   private final SensorStrategy sensorStrategy;
 
-  /**
-   * Testing purposes only!
-   */
-  public DefaultIndexedFile(String projectKey, Path baseDir, String relativePath, @Nullable String language) {
-    this(baseDir.resolve(relativePath), projectKey, relativePath, relativePath, Type.MAIN, language, TestInputFileBuilder.nextBatchId(),
-      new SensorStrategy());
-  }
-
   public DefaultIndexedFile(Path absolutePath, String projectKey, String projectRelativePath, String moduleRelativePath, Type type, @Nullable String language, int batchId,
     SensorStrategy sensorStrategy) {
     super(batchId);
index 5dbcf99c541b1a91b279bdb9c27ee5aace1e3194..19381e4c7ea706b631a0b4bbb39f5ca91c4a0637 100644 (file)
@@ -49,7 +49,7 @@ import static org.sonar.api.utils.Preconditions.checkState;
 
 /**
  * @since 4.2
- * To create {@link InputFile} in tests, use {@link TestInputFileBuilder}.
+ * To create {@link InputFile} in tests, use TestInputFileBuilder.
  */
 public class DefaultInputFile extends DefaultInputComponent implements InputFile {
 
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FalsePredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FalsePredicate.java
deleted file mode 100644 (file)
index 8bbbea9..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import org.sonar.api.batch.fs.FilePredicate;
-import org.sonar.api.batch.fs.FileSystem.Index;
-import org.sonar.api.batch.fs.InputFile;
-
-import java.util.Collections;
-
-class FalsePredicate extends AbstractFilePredicate {
-
-  static final FilePredicate FALSE = new FalsePredicate();
-
-  @Override
-  public boolean apply(InputFile inputFile) {
-    return false;
-  }
-
-  @Override
-  public Iterable<InputFile> filter(Iterable<InputFile> target) {
-    return Collections.emptyList();
-  }
-
-  @Override
-  public Iterable<InputFile> get(Index index) {
-    return Collections.emptyList();
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileExtensionPredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileExtensionPredicate.java
deleted file mode 100644 (file)
index e9fa863..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import java.util.Locale;
-import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.batch.fs.InputFile;
-
-/**
- * @since 6.3
- */
-public class FileExtensionPredicate extends AbstractFilePredicate {
-
-  private final String extension;
-
-  public FileExtensionPredicate(String extension) {
-    this.extension = lowercase(extension);
-  }
-
-  @Override
-  public boolean apply(InputFile inputFile) {
-    return extension.equals(getExtension(inputFile));
-  }
-
-  @Override
-  public Iterable<InputFile> get(FileSystem.Index index) {
-    return index.getFilesByExtension(extension);
-  }
-
-  public static String getExtension(InputFile inputFile) {
-    return getExtension(inputFile.filename());
-  }
-
-  static String getExtension(String name) {
-    int index = name.lastIndexOf('.');
-    if (index < 0) {
-      return "";
-    }
-    return lowercase(name.substring(index + 1));
-  }
-
-  private static String lowercase(String extension) {
-    return extension.toLowerCase(Locale.ENGLISH);
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java
deleted file mode 100644 (file)
index bda14d8..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.internal.charhandler.CharHandler;
-import org.sonar.api.batch.fs.internal.charhandler.FileHashComputer;
-import org.sonar.api.batch.fs.internal.charhandler.LineCounter;
-import org.sonar.api.batch.fs.internal.charhandler.LineHashComputer;
-import org.sonar.api.batch.fs.internal.charhandler.LineOffsetCounter;
-
-/**
- * Computes hash of files. Ends of Lines are ignored, so files with
- * same content but different EOL encoding have the same hash.
- */
-@Immutable
-public class FileMetadata {
-  private static final char LINE_FEED = '\n';
-  private static final char CARRIAGE_RETURN = '\r';
-
-  /**
-   * Compute hash of a file ignoring line ends differences.
-   * Maximum performance is needed.
-   */
-  public Metadata readMetadata(InputStream stream, Charset encoding, String filePath, @Nullable CharHandler otherHandler) {
-    LineCounter lineCounter = new LineCounter(filePath, encoding);
-    FileHashComputer fileHashComputer = new FileHashComputer(filePath);
-    LineOffsetCounter lineOffsetCounter = new LineOffsetCounter();
-
-    if (otherHandler != null) {
-      CharHandler[] handlers = {lineCounter, fileHashComputer, lineOffsetCounter, otherHandler};
-      readFile(stream, encoding, filePath, handlers);
-    } else {
-      CharHandler[] handlers = {lineCounter, fileHashComputer, lineOffsetCounter};
-      readFile(stream, encoding, filePath, handlers);
-    }
-    return new Metadata(lineCounter.lines(), lineCounter.nonBlankLines(), fileHashComputer.getHash(), lineOffsetCounter.getOriginalLineStartOffsets(),
-      lineOffsetCounter.getOriginalLineEndOffsets(),
-      lineOffsetCounter.getLastValidOffset());
-  }
-
-  public Metadata readMetadata(InputStream stream, Charset encoding, String filePath) {
-    return readMetadata(stream, encoding, filePath, null);
-  }
-
-  /**
-   * For testing purpose
-   */
-  public Metadata readMetadata(Reader reader) {
-    LineCounter lineCounter = new LineCounter("fromString", StandardCharsets.UTF_16);
-    FileHashComputer fileHashComputer = new FileHashComputer("fromString");
-    LineOffsetCounter lineOffsetCounter = new LineOffsetCounter();
-    CharHandler[] handlers = {lineCounter, fileHashComputer, lineOffsetCounter};
-
-    try {
-      read(reader, handlers);
-    } catch (IOException e) {
-      throw new IllegalStateException("Should never occur", e);
-    }
-    return new Metadata(lineCounter.lines(), lineCounter.nonBlankLines(), fileHashComputer.getHash(), lineOffsetCounter.getOriginalLineStartOffsets(),
-      lineOffsetCounter.getOriginalLineEndOffsets(),
-      lineOffsetCounter.getLastValidOffset());
-  }
-
-  public static void readFile(InputStream stream, Charset encoding, String filePath, CharHandler[] handlers) {
-    try (Reader reader = new BufferedReader(new InputStreamReader(stream, encoding))) {
-      read(reader, handlers);
-    } catch (IOException e) {
-      throw new IllegalStateException(String.format("Fail to read file '%s' with encoding '%s'", filePath, encoding), e);
-    }
-  }
-
-  private static void read(Reader reader, CharHandler[] handlers) throws IOException {
-    char c;
-    int i = reader.read();
-    boolean afterCR = false;
-    while (i != -1) {
-      c = (char) i;
-      if (afterCR) {
-        for (CharHandler handler : handlers) {
-          if (c == CARRIAGE_RETURN) {
-            handler.newLine();
-            handler.handleAll(c);
-          } else if (c == LINE_FEED) {
-            handler.handleAll(c);
-            handler.newLine();
-          } else {
-            handler.newLine();
-            handler.handleIgnoreEoL(c);
-            handler.handleAll(c);
-          }
-        }
-        afterCR = c == CARRIAGE_RETURN;
-      } else if (c == LINE_FEED) {
-        for (CharHandler handler : handlers) {
-          handler.handleAll(c);
-          handler.newLine();
-        }
-      } else if (c == CARRIAGE_RETURN) {
-        afterCR = true;
-        for (CharHandler handler : handlers) {
-          handler.handleAll(c);
-        }
-      } else {
-        for (CharHandler handler : handlers) {
-          handler.handleIgnoreEoL(c);
-          handler.handleAll(c);
-        }
-      }
-      i = reader.read();
-    }
-    for (CharHandler handler : handlers) {
-      if (afterCR) {
-        handler.newLine();
-      }
-      handler.eof();
-    }
-  }
-
-  @FunctionalInterface
-  public interface LineHashConsumer {
-    void consume(int lineIdx, @Nullable byte[] hash);
-  }
-
-  /**
-   * Compute a MD5 hash of each line of the file after removing of all blank chars
-   */
-  public static void computeLineHashesForIssueTracking(InputFile f, LineHashConsumer consumer) {
-    try {
-      readFile(f.inputStream(), f.charset(), f.absolutePath(), new CharHandler[] {new LineHashComputer(consumer, f.file())});
-    } catch (IOException e) {
-      throw new IllegalStateException("Failed to compute line hashes for " + f.absolutePath(), e);
-    }
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FilenamePredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FilenamePredicate.java
deleted file mode 100644 (file)
index 303ff57..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.batch.fs.InputFile;
-
-/**
- * @since 6.3
- */
-public class FilenamePredicate extends AbstractFilePredicate {
-  private final String filename;
-
-  public FilenamePredicate(String filename) {
-    this.filename = filename;
-  }
-
-  @Override
-  public boolean apply(InputFile inputFile) {
-    return filename.equals(inputFile.filename());
-  }
-
-  @Override
-  public Iterable<InputFile> get(FileSystem.Index index) {
-    return index.getFilesByName(filename);
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/LanguagePredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/LanguagePredicate.java
deleted file mode 100644 (file)
index 433bbc3..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import org.sonar.api.batch.fs.InputFile;
-
-/**
- * @since 4.2
- */
-class LanguagePredicate extends AbstractFilePredicate {
-  private final String language;
-
-  LanguagePredicate(String language) {
-    this.language = language;
-  }
-
-  @Override
-  public boolean apply(InputFile f) {
-    return language.equals(f.language());
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/NotPredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/NotPredicate.java
deleted file mode 100644 (file)
index bd82a7a..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import java.util.Arrays;
-import java.util.List;
-import org.sonar.api.batch.fs.FilePredicate;
-import org.sonar.api.batch.fs.InputFile;
-
-/**
- * @since 4.2
- */
-class NotPredicate extends AbstractFilePredicate implements OperatorPredicate {
-
-  private final FilePredicate predicate;
-
-  NotPredicate(FilePredicate predicate) {
-    this.predicate = predicate;
-  }
-
-  @Override
-  public boolean apply(InputFile f) {
-    return !predicate.apply(f);
-  }
-
-  @Override
-  public List<FilePredicate> operands() {
-    return Arrays.asList(predicate);
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OperatorPredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OperatorPredicate.java
deleted file mode 100644 (file)
index 2c2b90f..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import java.util.List;
-import org.sonar.api.batch.fs.FilePredicate;
-
-/**
- * A predicate that associate other predicates
- */
-public interface OperatorPredicate extends FilePredicate {
-
-  List<FilePredicate> operands();
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OptimizedFilePredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OptimizedFilePredicate.java
deleted file mode 100644 (file)
index c78696d..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import org.sonar.api.batch.fs.FilePredicate;
-import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.batch.fs.InputFile;
-
-/**
- * Optimized version of FilePredicate allowing to speed up query by looking at InputFile by index.
- */
-public interface OptimizedFilePredicate extends FilePredicate, Comparable<OptimizedFilePredicate> {
-
-  /**
-   * Filter provided files to keep only the ones that are valid for this predicate
-   */
-  Iterable<InputFile> filter(Iterable<InputFile> inputFiles);
-
-  /**
-   * Get all files that are valid for this predicate.
-   */
-  Iterable<InputFile> get(FileSystem.Index index);
-
-  /**
-   * For optimization. FilePredicates will be applied in priority order. For example when doing
-   * p.and(p1, p2, p3) then p1, p2 and p3 will be applied according to their priority value. Higher priority value
-   * are applied first.
-   * Assign a high priority when the predicate will likely highly reduce the set of InputFiles to filter. Also
-   * {@link RelativePathPredicate} and AbsolutePathPredicate have a high priority since they are using cache index.
-   */
-  int priority();
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OptimizedFilePredicateAdapter.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OptimizedFilePredicateAdapter.java
deleted file mode 100644 (file)
index 15a7197..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import org.sonar.api.batch.fs.FilePredicate;
-import org.sonar.api.batch.fs.InputFile;
-
-class OptimizedFilePredicateAdapter extends AbstractFilePredicate {
-
-  private FilePredicate unoptimizedPredicate;
-
-  private OptimizedFilePredicateAdapter(FilePredicate unoptimizedPredicate) {
-    this.unoptimizedPredicate = unoptimizedPredicate;
-  }
-
-  @Override
-  public boolean apply(InputFile inputFile) {
-    return unoptimizedPredicate.apply(inputFile);
-  }
-
-  public static OptimizedFilePredicate create(FilePredicate predicate) {
-    if (predicate instanceof OptimizedFilePredicate) {
-      return (OptimizedFilePredicate) predicate;
-    } else {
-      return new OptimizedFilePredicateAdapter(predicate);
-    }
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OrPredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OrPredicate.java
deleted file mode 100644 (file)
index 58c81e4..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import org.sonar.api.batch.fs.FilePredicate;
-import org.sonar.api.batch.fs.InputFile;
-
-/**
- * @since 4.2
- */
-class OrPredicate extends AbstractFilePredicate implements OperatorPredicate {
-
-  private final List<FilePredicate> predicates = new ArrayList<>();
-
-  private OrPredicate() {
-  }
-
-  public static FilePredicate create(Collection<FilePredicate> predicates) {
-    if (predicates.isEmpty()) {
-      return TruePredicate.TRUE;
-    }
-    OrPredicate result = new OrPredicate();
-    for (FilePredicate filePredicate : predicates) {
-      if (filePredicate == TruePredicate.TRUE) {
-        return TruePredicate.TRUE;
-      } else if (filePredicate == FalsePredicate.FALSE) {
-        continue;
-      } else if (filePredicate instanceof OrPredicate) {
-        result.predicates.addAll(((OrPredicate) filePredicate).predicates);
-      } else {
-        result.predicates.add(filePredicate);
-      }
-    }
-    return result;
-  }
-
-  @Override
-  public boolean apply(InputFile f) {
-    for (FilePredicate predicate : predicates) {
-      if (predicate.apply(f)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  Collection<FilePredicate> predicates() {
-    return predicates;
-  }
-
-  @Override
-  public List<FilePredicate> operands() {
-    return predicates;
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java
deleted file mode 100644 (file)
index ea9bea1..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import java.nio.file.Paths;
-import org.sonar.api.batch.fs.InputFile;
-
-/**
- * @since 4.2
- */
-class PathPatternPredicate extends AbstractFilePredicate {
-
-  private final PathPattern pattern;
-
-  PathPatternPredicate(PathPattern pattern) {
-    this.pattern = pattern;
-  }
-
-  @Override
-  public boolean apply(InputFile f) {
-    return pattern.match(f.path(), Paths.get(f.relativePath()));
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/RelativePathPredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/RelativePathPredicate.java
deleted file mode 100644 (file)
index 27c2958..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import java.util.Collections;
-import javax.annotation.Nullable;
-import org.sonar.api.batch.fs.FileSystem.Index;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.utils.PathUtils;
-
-/**
- * @since 4.2
- */
-public class RelativePathPredicate extends AbstractFilePredicate {
-
-  @Nullable
-  private final String path;
-
-  RelativePathPredicate(String path) {
-    this.path = PathUtils.sanitize(path);
-  }
-
-  public String path() {
-    return path;
-  }
-
-  @Override
-  public boolean apply(InputFile f) {
-    if (path == null) {
-      return false;
-    }
-
-    return path.equals(f.relativePath());
-  }
-
-  @Override
-  public Iterable<InputFile> get(Index index) {
-    if (path != null) {
-      InputFile f = index.inputFile(this.path);
-      if (f != null) {
-        return Collections.singletonList(f);
-      }
-    }
-    return Collections.emptyList();
-  }
-
-  @Override
-  public int priority() {
-    return USE_INDEX;
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/StatusPredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/StatusPredicate.java
deleted file mode 100644 (file)
index 7aaaf4c..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import javax.annotation.Nullable;
-import org.sonar.api.batch.fs.InputFile;
-
-/**
- * @deprecated since 7.8
- */
-@Deprecated
-public class StatusPredicate extends AbstractFilePredicate {
-
-  private final InputFile.Status status;
-
-  StatusPredicate(@Nullable InputFile.Status status) {
-    this.status = status;
-  }
-
-  @Override
-  public boolean apply(InputFile f) {
-    return status == null || status == f.status();
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java
deleted file mode 100644 (file)
index 00f9319..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.StringReader;
-import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.utils.PathUtils;
-
-/**
- * Intended to be used in unit tests that need to create {@link InputFile}s.
- * An InputFile is unambiguously identified by a <b>module key</b> and a <b>relative path</b>, so these parameters are mandatory.
- * <p>
- * A module base directory is only needed to construct absolute paths.
- * <p>
- * Examples of usage of the constructors:
- *
- * <pre>
- * InputFile file1 = TestInputFileBuilder.create("module1", "myfile.java").build();
- * InputFile file2 = TestInputFileBuilder.create("", fs.baseDir(), myfile).build();
- * </pre>
- * <p>
- * file1 will have the "module1" as both module key and module base directory.
- * file2 has an empty string as module key, and a relative path which is the path from the filesystem base directory to myfile.
- *
- * @since 6.3
- */
-public class TestInputFileBuilder {
-  private static int batchId = 1;
-
-  private final int id;
-  private final String relativePath;
-  private final String projectKey;
-  @CheckForNull
-  private Path projectBaseDir;
-  private Path moduleBaseDir;
-  private String language;
-  private InputFile.Type type = InputFile.Type.MAIN;
-  private InputFile.Status status;
-  private int lines = -1;
-  private Charset charset;
-  private String hash;
-  private int nonBlankLines;
-  private int[] originalLineStartOffsets = new int[0];
-  private int[] originalLineEndOffsets = new int[0];
-  private int lastValidOffset = -1;
-  private boolean publish = true;
-  private String contents;
-
-  /**
-   * Create a InputFile identified by the given project key and relative path.
-   */
-  public TestInputFileBuilder(String projectKey, String relativePath) {
-    this(projectKey, relativePath, batchId++);
-  }
-
-  /**
-   * Create a InputFile with a given module key and module base directory.
-   * The relative path is generated comparing the file path to the module base directory.
-   * filePath must point to a file that is within the module base directory.
-   */
-  public TestInputFileBuilder(String projectKey, File moduleBaseDir, File filePath) {
-    String relativePath = moduleBaseDir.toPath().relativize(filePath.toPath()).toString();
-    this.projectKey = projectKey;
-    setModuleBaseDir(moduleBaseDir.toPath());
-    this.relativePath = PathUtils.sanitize(relativePath);
-    this.id = batchId++;
-  }
-
-  public TestInputFileBuilder(String projectKey, String relativePath, int id) {
-    this.projectKey = projectKey;
-    setModuleBaseDir(Paths.get(projectKey));
-    this.relativePath = PathUtils.sanitize(relativePath);
-    this.id = id;
-  }
-
-  public static TestInputFileBuilder create(String moduleKey, File moduleBaseDir, File filePath) {
-    return new TestInputFileBuilder(moduleKey, moduleBaseDir, filePath);
-  }
-
-  public static TestInputFileBuilder create(String moduleKey, String relativePath) {
-    return new TestInputFileBuilder(moduleKey, relativePath);
-  }
-
-  public static int nextBatchId() {
-    return batchId++;
-  }
-
-  public TestInputFileBuilder setProjectBaseDir(Path projectBaseDir) {
-    this.projectBaseDir = normalize(projectBaseDir);
-    return this;
-  }
-
-  public TestInputFileBuilder setModuleBaseDir(Path moduleBaseDir) {
-    this.moduleBaseDir = normalize(moduleBaseDir);
-    return this;
-  }
-
-  private static Path normalize(Path path) {
-    try {
-      return path.normalize().toRealPath(LinkOption.NOFOLLOW_LINKS);
-    } catch (IOException e) {
-      return path.normalize();
-    }
-  }
-
-  public TestInputFileBuilder setLanguage(@Nullable String language) {
-    this.language = language;
-    return this;
-  }
-
-  public TestInputFileBuilder setType(InputFile.Type type) {
-    this.type = type;
-    return this;
-  }
-
-  public TestInputFileBuilder setStatus(InputFile.Status status) {
-    this.status = status;
-    return this;
-  }
-
-  public TestInputFileBuilder setLines(int lines) {
-    this.lines = lines;
-    return this;
-  }
-
-  public TestInputFileBuilder setCharset(Charset charset) {
-    this.charset = charset;
-    return this;
-  }
-
-  public TestInputFileBuilder setHash(String hash) {
-    this.hash = hash;
-    return this;
-  }
-
-  /**
-   * Set contents of the file and calculates metadata from it.
-   * The contents will be returned by {@link InputFile#contents()} and {@link InputFile#inputStream()} and can be
-   * inconsistent with the actual physical file pointed by {@link InputFile#path()}, {@link InputFile#absolutePath()}, etc.
-   */
-  public TestInputFileBuilder setContents(String content) {
-    this.contents = content;
-    initMetadata(content);
-    return this;
-  }
-
-  public TestInputFileBuilder setNonBlankLines(int nonBlankLines) {
-    this.nonBlankLines = nonBlankLines;
-    return this;
-  }
-
-  public TestInputFileBuilder setLastValidOffset(int lastValidOffset) {
-    this.lastValidOffset = lastValidOffset;
-    return this;
-  }
-
-  public TestInputFileBuilder setOriginalLineStartOffsets(int[] originalLineStartOffsets) {
-    this.originalLineStartOffsets = originalLineStartOffsets;
-    return this;
-  }
-
-  public TestInputFileBuilder setOriginalLineEndOffsets(int[] originalLineEndOffsets) {
-    this.originalLineEndOffsets = originalLineEndOffsets;
-    return this;
-  }
-
-  public TestInputFileBuilder setPublish(boolean publish) {
-    this.publish = publish;
-    return this;
-  }
-
-  public TestInputFileBuilder setMetadata(Metadata metadata) {
-    this.setLines(metadata.lines());
-    this.setLastValidOffset(metadata.lastValidOffset());
-    this.setNonBlankLines(metadata.nonBlankLines());
-    this.setHash(metadata.hash());
-    this.setOriginalLineStartOffsets(metadata.originalLineStartOffsets());
-    this.setOriginalLineEndOffsets(metadata.originalLineEndOffsets());
-    return this;
-  }
-
-  public TestInputFileBuilder initMetadata(String content) {
-    return setMetadata(new FileMetadata().readMetadata(new StringReader(content)));
-  }
-
-  public DefaultInputFile build() {
-    Path absolutePath = moduleBaseDir.resolve(relativePath);
-    if (projectBaseDir == null) {
-      projectBaseDir = moduleBaseDir;
-    }
-    String projectRelativePath = projectBaseDir.relativize(absolutePath).toString();
-    DefaultIndexedFile indexedFile = new DefaultIndexedFile(absolutePath, projectKey, projectRelativePath, relativePath, type, language, id, new SensorStrategy());
-    DefaultInputFile inputFile = new DefaultInputFile(indexedFile,
-      f -> f.setMetadata(new Metadata(lines, nonBlankLines, hash, originalLineStartOffsets, originalLineEndOffsets, lastValidOffset)),
-      contents);
-    inputFile.setStatus(status);
-    inputFile.setCharset(charset);
-    inputFile.setPublished(publish);
-    return inputFile;
-  }
-
-  public static DefaultInputModule newDefaultInputModule(String moduleKey, File baseDir) {
-    ProjectDefinition definition = ProjectDefinition.create()
-      .setKey(moduleKey)
-      .setBaseDir(baseDir)
-      .setWorkDir(new File(baseDir, ".sonar"));
-    return newDefaultInputModule(definition);
-  }
-
-  public static DefaultInputModule newDefaultInputModule(ProjectDefinition projectDefinition) {
-    return new DefaultInputModule(projectDefinition, TestInputFileBuilder.nextBatchId());
-  }
-
-  public static DefaultInputModule newDefaultInputModule(AbstractProjectOrModule parent, String key) throws IOException {
-    Path basedir = parent.getBaseDir().resolve(key);
-    Files.createDirectory(basedir);
-    return newDefaultInputModule(key, basedir.toFile());
-  }
-
-  public static DefaultInputProject newDefaultInputProject(String projectKey, File baseDir) {
-    ProjectDefinition definition = ProjectDefinition.create()
-      .setKey(projectKey)
-      .setBaseDir(baseDir)
-      .setWorkDir(new File(baseDir, ".sonar"));
-    return newDefaultInputProject(definition);
-  }
-
-  public static DefaultInputProject newDefaultInputProject(ProjectDefinition projectDefinition) {
-    return new DefaultInputProject(projectDefinition, TestInputFileBuilder.nextBatchId());
-  }
-
-  public static DefaultInputProject newDefaultInputProject(String key, Path baseDir) throws IOException {
-    Files.createDirectory(baseDir);
-    return newDefaultInputProject(key, baseDir.toFile());
-  }
-
-  public static DefaultInputDir newDefaultInputDir(AbstractProjectOrModule module, String relativePath) throws IOException {
-    Path basedir = module.getBaseDir().resolve(relativePath);
-    Files.createDirectory(basedir);
-    return new DefaultInputDir(module.key(), relativePath)
-      .setModuleBaseDir(module.getBaseDir());
-  }
-
-  public static DefaultInputFile newDefaultInputFile(Path projectBaseDir, AbstractProjectOrModule module, String relativePath) {
-    return new TestInputFileBuilder(module.key(), relativePath)
-      .setStatus(InputFile.Status.SAME)
-      .setProjectBaseDir(projectBaseDir)
-      .setModuleBaseDir(module.getBaseDir())
-      .build();
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TruePredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TruePredicate.java
deleted file mode 100644 (file)
index 6f4d9f6..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import org.sonar.api.batch.fs.FilePredicate;
-import org.sonar.api.batch.fs.FileSystem.Index;
-import org.sonar.api.batch.fs.InputFile;
-
-class TruePredicate extends AbstractFilePredicate {
-
-  static final FilePredicate TRUE = new TruePredicate();
-
-  @Override
-  public boolean apply(InputFile inputFile) {
-    return true;
-  }
-
-  @Override
-  public Iterable<InputFile> get(Index index) {
-    return index.inputFiles();
-  }
-
-  @Override
-  public Iterable<InputFile> filter(Iterable<InputFile> target) {
-    return target;
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TypePredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TypePredicate.java
deleted file mode 100644 (file)
index 55f364b..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import org.sonar.api.batch.fs.InputFile;
-
-/**
- * @since 4.2
- */
-class TypePredicate extends AbstractFilePredicate {
-
-  private final InputFile.Type type;
-
-  TypePredicate(InputFile.Type type) {
-    this.type = type;
-  }
-
-  @Override
-  public boolean apply(InputFile f) {
-    return type == f.type();
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/URIPredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/URIPredicate.java
deleted file mode 100644 (file)
index 2c710f1..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal;
-
-import java.net.URI;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Optional;
-import org.sonar.api.batch.fs.FileSystem.Index;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.scan.filesystem.PathResolver;
-
-/**
- * @since 6.6
- */
-class URIPredicate extends AbstractFilePredicate {
-
-  private final URI uri;
-  private final Path baseDir;
-
-  URIPredicate(URI uri, Path baseDir) {
-    this.baseDir = baseDir;
-    this.uri = uri;
-  }
-
-  @Override
-  public boolean apply(InputFile f) {
-    return uri.equals(f.uri());
-  }
-
-  @Override
-  public Iterable<InputFile> get(Index index) {
-    Path path = Paths.get(uri);
-    Optional<String> relative = PathResolver.relativize(baseDir, path);
-    if (!relative.isPresent()) {
-      return Collections.emptyList();
-    }
-    InputFile f = index.inputFile(relative.get());
-    return f != null ? Arrays.asList(f) : Collections.<InputFile>emptyList();
-  }
-
-  @Override
-  public int priority() {
-    return USE_INDEX;
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/CharHandler.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/CharHandler.java
deleted file mode 100644 (file)
index 8932b7d..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal.charhandler;
-
-public abstract class CharHandler {
-
-  public void handleAll(char c) {
-  }
-
-  public void handleIgnoreEoL(char c) {
-  }
-
-  public void newLine() {
-  }
-
-  public void eof() {
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/FileHashComputer.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/FileHashComputer.java
deleted file mode 100644 (file)
index 42a8c7a..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal.charhandler;
-
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.CharacterCodingException;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CodingErrorAction;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-
-import org.apache.commons.codec.binary.Hex;
-import org.apache.commons.codec.digest.DigestUtils;
-
-public class FileHashComputer extends CharHandler {
-  private static final char LINE_FEED = '\n';
-
-  
-  private MessageDigest globalMd5Digest = DigestUtils.getMd5Digest();
-  private StringBuilder sb = new StringBuilder();
-  private final CharsetEncoder encoder;
-  private final String filePath;
-
-  public FileHashComputer(String filePath) {
-    encoder = StandardCharsets.UTF_8.newEncoder()
-      .onMalformedInput(CodingErrorAction.REPLACE)
-      .onUnmappableCharacter(CodingErrorAction.REPLACE);
-    this.filePath = filePath;
-  }
-
-  @Override
-  public void handleIgnoreEoL(char c) {
-    sb.append(c);
-  }
-
-  @Override
-  public void newLine() {
-    sb.append(LINE_FEED);
-    processBuffer();
-    sb.setLength(0);
-  }
-
-  @Override
-  public void eof() {
-    if (sb.length() > 0) {
-      processBuffer();
-    }
-  }
-
-  private void processBuffer() {
-    try {
-      if (sb.length() > 0) {
-        ByteBuffer encoded = encoder.encode(CharBuffer.wrap(sb));
-        globalMd5Digest.update(encoded.array(), 0, encoded.limit());
-      }
-    } catch (CharacterCodingException e) {
-      throw new IllegalStateException("Error encoding line hash in file: " + filePath, e);
-    }
-  }
-
-  public String getHash() {
-    return Hex.encodeHexString(globalMd5Digest.digest());
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/IntArrayList.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/IntArrayList.java
deleted file mode 100644 (file)
index 0795098..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal.charhandler;
-
-import java.util.Arrays;
-import java.util.Collection;
-
-/**
- * Specialization of {@link java.util.ArrayList} to create a list of int (only append elements) and then produce an int[].
- */
-class IntArrayList {
-
-  /**
-   * Default initial capacity.
-   */
-  private static final int DEFAULT_CAPACITY = 10;
-
-  /**
-   * Shared empty array instance used for default sized empty instances. We
-   * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
-   * first element is added.
-   */
-  private static final int[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
-
-  /**
-   * The array buffer into which the elements of the ArrayList are stored.
-   * The capacity of the IntArrayList is the length of this array buffer. Any
-   * empty IntArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
-   * will be expanded to DEFAULT_CAPACITY when the first element is added.
-   */
-  private int[] elementData;
-
-  /**
-   * The size of the IntArrayList (the number of elements it contains).
-   */
-  private int size;
-
-  /**
-   * Constructs an empty list with an initial capacity of ten.
-   */
-  public IntArrayList() {
-    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
-  }
-
-  /**
-   * Trims the capacity of this <tt>IntArrayList</tt> instance to be the
-   * list's current size and return the internal array. An application can use this operation to minimize
-   * the storage of an <tt>IntArrayList</tt> instance.
-   */
-  public int[] trimAndGet() {
-    if (size < elementData.length) {
-      elementData = Arrays.copyOf(elementData, size);
-    }
-    return elementData;
-  }
-
-  private void ensureCapacityInternal(int minCapacity) {
-    int capacity = minCapacity;
-    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
-      capacity = Math.max(DEFAULT_CAPACITY, minCapacity);
-    }
-
-    ensureExplicitCapacity(capacity);
-  }
-
-  private void ensureExplicitCapacity(int minCapacity) {
-    if (minCapacity - elementData.length > 0) {
-      grow(minCapacity);
-    }
-  }
-
-  /**
-   * Increases the capacity to ensure that it can hold at least the
-   * number of elements specified by the minimum capacity argument.
-   *
-   * @param minCapacity the desired minimum capacity
-   */
-  private void grow(int minCapacity) {
-    int oldCapacity = elementData.length;
-    int newCapacity = oldCapacity + (oldCapacity >> 1);
-    if (newCapacity - minCapacity < 0) {
-      newCapacity = minCapacity;
-    }
-    elementData = Arrays.copyOf(elementData, newCapacity);
-  }
-
-  /**
-   * Appends the specified element to the end of this list.
-   *
-   * @param e element to be appended to this list
-   * @return <tt>true</tt> (as specified by {@link Collection#add})
-   */
-  public boolean add(int e) {
-    ensureCapacityInternal(size + 1);
-    elementData[size] = e;
-    size++;
-    return true;
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineCounter.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineCounter.java
deleted file mode 100644 (file)
index 9925e16..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal.charhandler;
-
-import java.nio.charset.Charset;
-
-import org.sonar.api.CoreProperties;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-
-public class LineCounter extends CharHandler {
-  private static final Logger LOG = Loggers.get(LineCounter.class);
-    
-  private int lines = 1;
-  private int nonBlankLines = 0;
-  private boolean blankLine = true;
-  boolean alreadyLoggedInvalidCharacter = false;
-  private final String filePath;
-  private final Charset encoding;
-
-  public LineCounter(String filePath, Charset encoding) {
-    this.filePath = filePath;
-    this.encoding = encoding;
-  }
-
-  @Override
-  public void handleAll(char c) {
-    if (!alreadyLoggedInvalidCharacter && c == '\ufffd') {
-      LOG.warn("Invalid character encountered in file {} at line {} for encoding {}. Please fix file content or configure the encoding to be used using property '{}'.", filePath,
-        lines, encoding, CoreProperties.ENCODING_PROPERTY);
-      alreadyLoggedInvalidCharacter = true;
-    }
-  }
-
-  @Override
-  public void newLine() {
-    lines++;
-    if (!blankLine) {
-      nonBlankLines++;
-    }
-    blankLine = true;
-  }
-
-  @Override
-  public void handleIgnoreEoL(char c) {
-    if (!Character.isWhitespace(c)) {
-      blankLine = false;
-    }
-  }
-
-  @Override
-  public void eof() {
-    if (!blankLine) {
-      nonBlankLines++;
-    }
-  }
-
-  public int lines() {
-    return lines;
-  }
-
-  public int nonBlankLines() {
-    return nonBlankLines;
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineHashComputer.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineHashComputer.java
deleted file mode 100644 (file)
index ce0c62b..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal.charhandler;
-
-import java.io.File;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.CharacterCodingException;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CodingErrorAction;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-
-import org.apache.commons.codec.digest.DigestUtils;
-import org.sonar.api.batch.fs.internal.FileMetadata.LineHashConsumer;
-
-public class LineHashComputer extends CharHandler {
-  private final MessageDigest lineMd5Digest = DigestUtils.getMd5Digest();
-  private final CharsetEncoder encoder;
-  private final StringBuilder sb = new StringBuilder();
-  private final LineHashConsumer consumer;
-  private final File file;
-  private int line = 1;
-
-  public LineHashComputer(LineHashConsumer consumer, File f) {
-    this.consumer = consumer;
-    this.file = f;
-    this.encoder = StandardCharsets.UTF_8.newEncoder()
-      .onMalformedInput(CodingErrorAction.REPLACE)
-      .onUnmappableCharacter(CodingErrorAction.REPLACE);
-  }
-
-  @Override
-  public void handleIgnoreEoL(char c) {
-    if (!Character.isWhitespace(c)) {
-      sb.append(c);
-    }
-  }
-
-  @Override
-  public void newLine() {
-    processBuffer();
-    sb.setLength(0);
-    line++;
-  }
-
-  @Override
-  public void eof() {
-    if (this.line > 0) {
-      processBuffer();
-    }
-  }
-
-  private void processBuffer() {
-    try {
-      if (sb.length() > 0) {
-        ByteBuffer encoded = encoder.encode(CharBuffer.wrap(sb));
-        lineMd5Digest.update(encoded.array(), 0, encoded.limit());
-        consumer.consume(line, lineMd5Digest.digest());
-      }
-    } catch (CharacterCodingException e) {
-      throw new IllegalStateException("Error encoding line hash in file: " + file.getAbsolutePath(), e);
-    }
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineOffsetCounter.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineOffsetCounter.java
deleted file mode 100644 (file)
index 22292be..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.fs.internal.charhandler;
-
-public class LineOffsetCounter extends CharHandler {
-  private long currentOriginalLineStartOffset = 0;
-  private long currentOriginalLineEndOffset = 0;
-  private final IntArrayList originalLineStartOffsets = new IntArrayList();
-  private final IntArrayList originalLineEndOffsets = new IntArrayList();
-  private long lastValidOffset = 0;
-
-  public LineOffsetCounter() {
-    originalLineStartOffsets.add(0);
-  }
-
-  @Override
-  public void handleAll(char c) {
-    currentOriginalLineStartOffset++;
-  }
-
-  @Override
-  public void handleIgnoreEoL(char c) {
-    currentOriginalLineEndOffset++;
-  }
-
-  @Override
-  public void newLine() {
-    if (currentOriginalLineStartOffset > Integer.MAX_VALUE) {
-      throw new IllegalStateException("File is too big: " + currentOriginalLineStartOffset);
-    }
-    originalLineStartOffsets.add((int) currentOriginalLineStartOffset);
-    originalLineEndOffsets.add((int) currentOriginalLineEndOffset);
-    currentOriginalLineEndOffset = currentOriginalLineStartOffset;
-  }
-
-  @Override
-  public void eof() {
-    originalLineEndOffsets.add((int) currentOriginalLineEndOffset);
-    lastValidOffset = currentOriginalLineStartOffset;
-  }
-
-  public int[] getOriginalLineStartOffsets() {
-    return originalLineStartOffsets.trimAndGet();
-  }
-
-  public int[] getOriginalLineEndOffsets() {
-    return originalLineEndOffsets.trimAndGet();
-  }
-
-  public int getLastValidOffset() {
-    if (lastValidOffset > Integer.MAX_VALUE) {
-      throw new IllegalStateException("File is too big: " + lastValidOffset);
-    }
-    return (int) lastValidOffset;
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/package-info.java
deleted file mode 100644 (file)
index 953ee25..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.api.batch.fs.internal.charhandler;
-
-import javax.annotation.ParametersAreNonnullByDefault;
-
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRules.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRules.java
deleted file mode 100644 (file)
index def0012..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.rule.internal;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-import javax.annotation.concurrent.Immutable;
-import org.sonar.api.batch.rule.Rule;
-import org.sonar.api.batch.rule.Rules;
-import org.sonar.api.rule.RuleKey;
-
-@Immutable
-class DefaultRules implements Rules {
-  private final Map<String, List<Rule>> rulesByRepository;
-  private final Map<String, Map<String, List<Rule>>> rulesByRepositoryAndInternalKey;
-  private final Map<RuleKey, Rule> rulesByRuleKey;
-
-  DefaultRules(Collection<NewRule> newRules) {
-    Map<String, List<Rule>> rulesByRepositoryBuilder = new HashMap<>();
-    Map<String, Map<String, List<Rule>>> rulesByRepositoryAndInternalKeyBuilder = new HashMap<>();
-    Map<RuleKey, Rule> rulesByRuleKeyBuilder = new HashMap<>();
-
-    for (NewRule newRule : newRules) {
-      DefaultRule r = new DefaultRule(newRule);
-      rulesByRuleKeyBuilder.put(r.key(), r);
-      rulesByRepositoryBuilder.computeIfAbsent(r.key().repository(), x -> new ArrayList<>()).add(r);
-      addToTable(rulesByRepositoryAndInternalKeyBuilder, r);
-    }
-
-    rulesByRuleKey = Collections.unmodifiableMap(rulesByRuleKeyBuilder);
-    rulesByRepository = Collections.unmodifiableMap(rulesByRepositoryBuilder);
-    rulesByRepositoryAndInternalKey = Collections.unmodifiableMap(rulesByRepositoryAndInternalKeyBuilder);
-  }
-
-  private static void addToTable(Map<String, Map<String, List<Rule>>> rulesByRepositoryAndInternalKeyBuilder, DefaultRule r) {
-    if (r.internalKey() == null) {
-      return;
-    }
-
-    rulesByRepositoryAndInternalKeyBuilder
-      .computeIfAbsent(r.key().repository(), x -> new HashMap<>())
-      .computeIfAbsent(r.internalKey(), x -> new ArrayList<>())
-      .add(r);
-  }
-
-  @Override
-  public Rule find(RuleKey ruleKey) {
-    return rulesByRuleKey.get(ruleKey);
-  }
-
-  @Override
-  public Collection<Rule> findAll() {
-    return rulesByRepository.values().stream().flatMap(List::stream).collect(Collectors.toList());
-  }
-
-  @Override
-  public Collection<Rule> findByRepository(String repository) {
-    return rulesByRepository.getOrDefault(repository, Collections.emptyList());
-  }
-
-  @Override
-  public Collection<Rule> findByInternalKey(String repository, String internalKey) {
-    return rulesByRepositoryAndInternalKey
-      .getOrDefault(repository, Collections.emptyMap())
-      .getOrDefault(internalKey, Collections.emptyList());
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/RulesBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/RulesBuilder.java
deleted file mode 100644 (file)
index f7ebe36..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.rule.internal;
-
-import org.sonar.api.batch.rule.Rules;
-import org.sonar.api.rule.RuleKey;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * For unit testing and internal use only.
- *
- * @since 4.2
- */
-
-public class RulesBuilder {
-
-  private final Map<RuleKey, NewRule> map = new HashMap<>();
-
-  public NewRule add(RuleKey key) {
-    if (map.containsKey(key)) {
-      throw new IllegalStateException(String.format("Rule '%s' already exists", key));
-    }
-    NewRule newRule = new NewRule(key);
-    map.put(key, newRule);
-    return newRule;
-  }
-
-  public Rules build() {
-    return new DefaultRules(map.values());
-  }
-}
index 8e0a70824e9e1deeb1af0af16672d69b2204d355..7d4536c68ce5ceb6e091362c3597eee85f32027c 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.api.batch.sensor;
 import org.sonar.api.ExtensionPoint;
 import org.sonar.api.batch.InstantiationStrategy;
 import org.sonar.api.batch.ScannerSide;
-import org.sonar.api.batch.sensor.internal.SensorContextTester;
 import org.sonar.api.scanner.sensor.ProjectSensor;
 import org.sonarsource.api.sonarlint.SonarLintSide;
 
@@ -33,7 +32,7 @@ import org.sonarsource.api.sonarlint.SonarLintSide;
  * <p>
  * For example the Cobertura Sensor parses Cobertura report and saves the first-level of measures on files.
  * 
- * For testing purpose you can use {@link SensorContextTester}
+ * For testing purpose you can use SensorContextTester
  * @since 5.1
  * @since 7.6 use {@link ProjectSensor} instead to make your Sensor run only once per analysis, and no more once per module
  */
index 8f7cb5fdaf65c7fb5960c8fb2be06caac4a67034..cc2e443bbca1b1208769300b54bf5627eba47545 100644 (file)
@@ -30,7 +30,6 @@ import org.sonar.api.batch.sensor.coverage.NewCoverage;
 import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
 import org.sonar.api.batch.sensor.error.NewAnalysisError;
 import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
-import org.sonar.api.batch.sensor.internal.SensorContextTester;
 import org.sonar.api.batch.sensor.issue.ExternalIssue;
 import org.sonar.api.batch.sensor.issue.Issue;
 import org.sonar.api.batch.sensor.issue.NewExternalIssue;
@@ -48,7 +47,7 @@ import org.sonar.api.utils.Version;
 
 /**
  * See {@link Sensor#execute(SensorContext)}
- * In order to write unit tests you can use {@link SensorContextTester}
+ * In order to write unit tests you can use SensorContextTester
  * @since 5.1
  */
 public interface SensorContext {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/InMemorySensorStorage.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/InMemorySensorStorage.java
deleted file mode 100644 (file)
index 74b238c..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.sensor.internal;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.sonar.api.batch.sensor.code.internal.DefaultSignificantCode;
-import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
-import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
-import org.sonar.api.batch.sensor.error.AnalysisError;
-import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
-import org.sonar.api.batch.sensor.issue.ExternalIssue;
-import org.sonar.api.batch.sensor.issue.Issue;
-import org.sonar.api.batch.sensor.issue.internal.DefaultExternalIssue;
-import org.sonar.api.batch.sensor.measure.Measure;
-import org.sonar.api.batch.sensor.rule.AdHocRule;
-import org.sonar.api.batch.sensor.rule.internal.DefaultAdHocRule;
-import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable;
-
-import static org.sonar.api.utils.Preconditions.checkArgument;
-
-class InMemorySensorStorage implements SensorStorage {
-
-  Map<String, Map<String, Measure>> measuresByComponentAndMetric = new HashMap<>();
-
-  Collection<Issue> allIssues = new ArrayList<>();
-  Collection<ExternalIssue> allExternalIssues = new ArrayList<>();
-  Collection<AdHocRule> allAdHocRules = new ArrayList<>();
-  Collection<AnalysisError> allAnalysisErrors = new ArrayList<>();
-
-  Map<String, DefaultHighlighting> highlightingByComponent = new HashMap<>();
-  Map<String, DefaultCpdTokens> cpdTokensByComponent = new HashMap<>();
-  Map<String, List<DefaultCoverage>> coverageByComponent = new HashMap<>();
-  Map<String, DefaultSymbolTable> symbolsPerComponent = new HashMap<>();
-  Map<String, String> contextProperties = new HashMap<>();
-  Map<String, DefaultSignificantCode> significantCodePerComponent = new HashMap<>();
-
-  @Override
-  public void store(Measure measure) {
-    // Emulate duplicate measure check
-    String componentKey = measure.inputComponent().key();
-    String metricKey = measure.metric().key();
-    if (measuresByComponentAndMetric.getOrDefault(componentKey, Collections.emptyMap()).containsKey(metricKey)) {
-      throw new IllegalStateException("Can not add the same measure twice");
-    }
-    measuresByComponentAndMetric.computeIfAbsent(componentKey, x -> new HashMap<>()).put(metricKey, measure);
-  }
-
-  @Override
-  public void store(Issue issue) {
-    allIssues.add(issue);
-  }
-
-  @Override
-  public void store(DefaultAdHocRule adHocRule) {
-    allAdHocRules.add(adHocRule);
-  }
-
-  @Override
-  public void store(DefaultHighlighting highlighting) {
-    String fileKey = highlighting.inputFile().key();
-    // Emulate duplicate storage check
-    if (highlightingByComponent.containsKey(fileKey)) {
-      throw new UnsupportedOperationException("Trying to save highlighting twice for the same file is not supported: " + highlighting.inputFile());
-    }
-    highlightingByComponent.put(fileKey, highlighting);
-  }
-
-  @Override
-  public void store(DefaultCoverage defaultCoverage) {
-    String fileKey = defaultCoverage.inputFile().key();
-    coverageByComponent.computeIfAbsent(fileKey, x -> new ArrayList<>()).add(defaultCoverage);
-  }
-
-  @Override
-  public void store(DefaultCpdTokens defaultCpdTokens) {
-    String fileKey = defaultCpdTokens.inputFile().key();
-    // Emulate duplicate storage check
-    if (cpdTokensByComponent.containsKey(fileKey)) {
-      throw new UnsupportedOperationException("Trying to save CPD tokens twice for the same file is not supported: " + defaultCpdTokens.inputFile());
-    }
-    cpdTokensByComponent.put(fileKey, defaultCpdTokens);
-  }
-
-  @Override
-  public void store(DefaultSymbolTable symbolTable) {
-    String fileKey = symbolTable.inputFile().key();
-    // Emulate duplicate storage check
-    if (symbolsPerComponent.containsKey(fileKey)) {
-      throw new UnsupportedOperationException("Trying to save symbol table twice for the same file is not supported: " + symbolTable.inputFile());
-    }
-    symbolsPerComponent.put(fileKey, symbolTable);
-  }
-
-  @Override
-  public void store(AnalysisError analysisError) {
-    allAnalysisErrors.add(analysisError);
-  }
-
-  @Override
-  public void storeProperty(String key, String value) {
-    checkArgument(key != null, "Key of context property must not be null");
-    checkArgument(value != null, "Value of context property must not be null");
-    contextProperties.put(key, value);
-  }
-
-  @Override
-  public void store(DefaultExternalIssue issue) {
-    allExternalIssues.add(issue);
-  }
-
-  @Override
-  public void store(DefaultSignificantCode significantCode) {
-    String fileKey = significantCode.inputFile().key();
-    // Emulate duplicate storage check
-    if (significantCodePerComponent.containsKey(fileKey)) {
-      throw new UnsupportedOperationException("Trying to save significant code information twice for the same file is not supported: " + significantCode.inputFile());
-    }
-    significantCodePerComponent.put(fileKey, significantCode);
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
deleted file mode 100644 (file)
index 70f5d50..0000000
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.api.batch.sensor.internal;
-
-import java.io.File;
-import java.io.Serializable;
-import java.nio.charset.Charset;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Stream;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import org.sonar.api.SonarQubeSide;
-import org.sonar.api.SonarRuntime;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.InputModule;
-import org.sonar.api.batch.fs.TextRange;
-import org.sonar.api.batch.fs.internal.DefaultFileSystem;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.fs.internal.DefaultInputModule;
-import org.sonar.api.batch.fs.internal.DefaultInputProject;
-import org.sonar.api.batch.fs.internal.DefaultTextPointer;
-import org.sonar.api.batch.rule.ActiveRules;
-import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
-import org.sonar.api.batch.sensor.Sensor;
-import org.sonar.api.batch.sensor.SensorContext;
-import org.sonar.api.batch.sensor.code.NewSignificantCode;
-import org.sonar.api.batch.sensor.code.internal.DefaultSignificantCode;
-import org.sonar.api.batch.sensor.coverage.NewCoverage;
-import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
-import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
-import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
-import org.sonar.api.batch.sensor.cpd.internal.TokensLine;
-import org.sonar.api.batch.sensor.error.AnalysisError;
-import org.sonar.api.batch.sensor.error.NewAnalysisError;
-import org.sonar.api.batch.sensor.error.internal.DefaultAnalysisError;
-import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
-import org.sonar.api.batch.sensor.highlighting.TypeOfText;
-import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
-import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule;
-import org.sonar.api.batch.sensor.issue.ExternalIssue;
-import org.sonar.api.batch.sensor.issue.Issue;
-import org.sonar.api.batch.sensor.issue.NewExternalIssue;
-import org.sonar.api.batch.sensor.issue.NewIssue;
-import org.sonar.api.batch.sensor.issue.internal.DefaultExternalIssue;
-import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
-import org.sonar.api.batch.sensor.measure.Measure;
-import org.sonar.api.batch.sensor.measure.NewMeasure;
-import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
-import org.sonar.api.batch.sensor.rule.AdHocRule;
-import org.sonar.api.batch.sensor.rule.NewAdHocRule;
-import org.sonar.api.batch.sensor.rule.internal.DefaultAdHocRule;
-import org.sonar.api.batch.sensor.symbol.NewSymbolTable;
-import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.config.Settings;
-import org.sonar.api.config.internal.ConfigurationBridge;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.api.internal.MetadataLoader;
-import org.sonar.api.internal.SonarRuntimeImpl;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.scanner.fs.InputProject;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.Version;
-
-import static java.util.Collections.unmodifiableMap;
-
-/**
- * Utility class to help testing {@link Sensor}. This is not an API and method signature may evolve.
- * <p>
- * Usage: call {@link #create(File)} to create an "in memory" implementation of {@link SensorContext} with a filesystem initialized with provided baseDir.
- * <p>
- * You have to manually register inputFiles using:
- * <pre>
- *   sensorContextTester.fileSystem().add(new DefaultInputFile("myProjectKey", "src/Foo.java")
- * .setLanguage("java")
- * .initMetadata("public class Foo {\n}"));
- * </pre>
- * <p>
- * Then pass it to your {@link Sensor}. You can then query elements provided by your sensor using methods {@link #allIssues()}, ...
- */
-public class SensorContextTester implements SensorContext {
-
-  private Settings settings;
-  private DefaultFileSystem fs;
-  private ActiveRules activeRules;
-  private InMemorySensorStorage sensorStorage;
-  private DefaultInputProject project;
-  private DefaultInputModule module;
-  private SonarRuntime runtime;
-  private boolean cancelled;
-
-  private SensorContextTester(Path moduleBaseDir) {
-    this.settings = new MapSettings();
-    this.fs = new DefaultFileSystem(moduleBaseDir).setEncoding(Charset.defaultCharset());
-    this.activeRules = new ActiveRulesBuilder().build();
-    this.sensorStorage = new InMemorySensorStorage();
-    this.project = new DefaultInputProject(ProjectDefinition.create().setKey("projectKey").setBaseDir(moduleBaseDir.toFile()).setWorkDir(moduleBaseDir.resolve(".sonar").toFile()));
-    this.module = new DefaultInputModule(ProjectDefinition.create().setKey("projectKey").setBaseDir(moduleBaseDir.toFile()).setWorkDir(moduleBaseDir.resolve(".sonar").toFile()));
-    this.runtime = SonarRuntimeImpl.forSonarQube(MetadataLoader.loadVersion(System2.INSTANCE), SonarQubeSide.SCANNER, MetadataLoader.loadEdition(System2.INSTANCE));
-  }
-
-  public static SensorContextTester create(File moduleBaseDir) {
-    return new SensorContextTester(moduleBaseDir.toPath());
-  }
-
-  public static SensorContextTester create(Path moduleBaseDir) {
-    return new SensorContextTester(moduleBaseDir);
-  }
-
-  @Override
-  public Settings settings() {
-    return settings;
-  }
-
-  @Override
-  public Configuration config() {
-    return new ConfigurationBridge(settings);
-  }
-
-  public SensorContextTester setSettings(Settings settings) {
-    this.settings = settings;
-    return this;
-  }
-
-  @Override
-  public DefaultFileSystem fileSystem() {
-    return fs;
-  }
-
-  public SensorContextTester setFileSystem(DefaultFileSystem fs) {
-    this.fs = fs;
-    return this;
-  }
-
-  @Override
-  public ActiveRules activeRules() {
-    return activeRules;
-  }
-
-  public SensorContextTester setActiveRules(ActiveRules activeRules) {
-    this.activeRules = activeRules;
-    return this;
-  }
-
-  /**
-   * Default value is the version of this API at compilation time. You can override it
-   * using {@link #setRuntime(SonarRuntime)} to test your Sensor behaviour.
-   */
-  @Override
-  public Version getSonarQubeVersion() {
-    return runtime().getApiVersion();
-  }
-
-  /**
-   * @see #setRuntime(SonarRuntime) to override defaults (SonarQube scanner with version
-   * of this API as used at compilation time).
-   */
-  @Override
-  public SonarRuntime runtime() {
-    return runtime;
-  }
-
-  public SensorContextTester setRuntime(SonarRuntime runtime) {
-    this.runtime = runtime;
-    return this;
-  }
-
-  @Override
-  public boolean isCancelled() {
-    return cancelled;
-  }
-
-  public void setCancelled(boolean cancelled) {
-    this.cancelled = cancelled;
-  }
-
-  @Override
-  public InputModule module() {
-    return module;
-  }
-
-  @Override
-  public InputProject project() {
-    return project;
-  }
-
-  @Override
-  public <G extends Serializable> NewMeasure<G> newMeasure() {
-    return new DefaultMeasure<>(sensorStorage);
-  }
-
-  public Collection<Measure> measures(String componentKey) {
-    return sensorStorage.measuresByComponentAndMetric.getOrDefault(componentKey, Collections.emptyMap()).values();
-  }
-
-  public <G extends Serializable> Measure<G> measure(String componentKey, Metric<G> metric) {
-    return measure(componentKey, metric.key());
-  }
-
-  public <G extends Serializable> Measure<G> measure(String componentKey, String metricKey) {
-    return sensorStorage.measuresByComponentAndMetric.getOrDefault(componentKey, Collections.emptyMap()).get(metricKey);
-  }
-
-  @Override
-  public NewIssue newIssue() {
-    return new DefaultIssue(project, sensorStorage);
-  }
-
-  public Collection<Issue> allIssues() {
-    return sensorStorage.allIssues;
-  }
-
-  @Override
-  public NewExternalIssue newExternalIssue() {
-    return new DefaultExternalIssue(project, sensorStorage);
-  }
-
-  @Override
-  public NewAdHocRule newAdHocRule() {
-    return new DefaultAdHocRule(sensorStorage);
-  }
-
-  public Collection<ExternalIssue> allExternalIssues() {
-    return sensorStorage.allExternalIssues;
-  }
-
-  public Collection<AdHocRule> allAdHocRules() {
-    return sensorStorage.allAdHocRules;
-  }
-
-  public Collection<AnalysisError> allAnalysisErrors() {
-    return sensorStorage.allAnalysisErrors;
-  }
-
-  @CheckForNull
-  public Integer lineHits(String fileKey, int line) {
-    return sensorStorage.coverageByComponent.getOrDefault(fileKey, Collections.emptyList()).stream()
-      .map(c -> c.hitsByLine().get(line))
-      .flatMap(Stream::of)
-      .filter(Objects::nonNull)
-      .reduce(null, SensorContextTester::sumOrNull);
-  }
-
-  @CheckForNull
-  public static Integer sumOrNull(@Nullable Integer o1, @Nullable Integer o2) {
-    return o1 == null ? o2 : (o1 + o2);
-  }
-
-  @CheckForNull
-  public Integer conditions(String fileKey, int line) {
-    return sensorStorage.coverageByComponent.getOrDefault(fileKey, Collections.emptyList()).stream()
-      .map(c -> c.conditionsByLine().get(line))
-      .flatMap(Stream::of)
-      .filter(Objects::nonNull)
-      .reduce(null, SensorContextTester::maxOrNull);
-  }
-
-  @CheckForNull
-  public Integer coveredConditions(String fileKey, int line) {
-    return sensorStorage.coverageByComponent.getOrDefault(fileKey, Collections.emptyList()).stream()
-      .map(c -> c.coveredConditionsByLine().get(line))
-      .flatMap(Stream::of)
-      .filter(Objects::nonNull)
-      .reduce(null, SensorContextTester::maxOrNull);
-  }
-
-  @CheckForNull
-  public TextRange significantCodeTextRange(String fileKey, int line) {
-    if (sensorStorage.significantCodePerComponent.containsKey(fileKey)) {
-      return sensorStorage.significantCodePerComponent.get(fileKey)
-        .significantCodePerLine()
-        .get(line);
-    }
-    return null;
-
-  }
-
-  @CheckForNull
-  public static Integer maxOrNull(@Nullable Integer o1, @Nullable Integer o2) {
-    return o1 == null ? o2 : Math.max(o1, o2);
-  }
-
-  @CheckForNull
-  public List<TokensLine> cpdTokens(String componentKey) {
-    DefaultCpdTokens defaultCpdTokens = sensorStorage.cpdTokensByComponent.get(componentKey);
-    return defaultCpdTokens != null ? defaultCpdTokens.getTokenLines() : null;
-  }
-
-  @Override
-  public NewHighlighting newHighlighting() {
-    return new DefaultHighlighting(sensorStorage);
-  }
-
-  @Override
-  public NewCoverage newCoverage() {
-    return new DefaultCoverage(sensorStorage);
-  }
-
-  @Override
-  public NewCpdTokens newCpdTokens() {
-    return new DefaultCpdTokens(sensorStorage);
-  }
-
-  @Override
-  public NewSymbolTable newSymbolTable() {
-    return new DefaultSymbolTable(sensorStorage);
-  }
-
-  @Override
-  public NewAnalysisError newAnalysisError() {
-    return new DefaultAnalysisError(sensorStorage);
-  }
-
-  /**
-   * 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;
-  }
-
-  /**
-   * 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 void addContextProperty(String key, String value) {
-    sensorStorage.storeProperty(key, value);
-  }
-
-  /**
-   * @return an immutable map of the context properties defined with {@link SensorContext#addContextProperty(String, String)}.
-   * @since 6.1
-   */
-  public Map<String, String> getContextProperties() {
-    return unmodifiableMap(sensorStorage.contextProperties);
-  }
-
-  @Override
-  public void markForPublishing(InputFile inputFile) {
-    DefaultInputFile file = (DefaultInputFile) inputFile;
-    file.setPublished(true);
-  }
-
-  @Override
-  public NewSignificantCode newSignificantCode() {
-    return new DefaultSignificantCode(sensorStorage);
-  }
-}
index 3d81fa9385b90f7f88b548184171cf550f7e2230..9ea27e49f785cabab3dabc93faef70fa7f65760e 100644 (file)
@@ -23,7 +23,6 @@ import org.sonar.api.ExtensionPoint;
 import org.sonar.api.scanner.ScannerSide;
 import org.sonar.api.batch.sensor.SensorContext;
 import org.sonar.api.batch.sensor.SensorDescriptor;
-import org.sonar.api.batch.sensor.internal.SensorContextTester;
 import org.sonarsource.api.sonarlint.SonarLintSide;
 
 /**
@@ -32,7 +31,7 @@ import org.sonarsource.api.sonarlint.SonarLintSide;
  * <p>
  * For example the Cobertura Sensor parses Cobertura report and saves the first-level of measures on files.
  * 
- * For testing purpose you can use {@link SensorContextTester}
+ * For testing purpose you can use  SensorContextTester
  * @since 7.6
  */
 @ScannerSide
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/AbstractProjectOrModule.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/AbstractProjectOrModule.java
new file mode 100644 (file)
index 0000000..9fc2d13
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.SystemUtils;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+
+@Immutable
+public abstract class AbstractProjectOrModule extends DefaultInputComponent {
+  private static final Logger LOGGER = Loggers.get(AbstractProjectOrModule.class);
+  private final Path baseDir;
+  private final Path workDir;
+  private final String name;
+  private final String originalName;
+  private final String description;
+  private final String keyWithBranch;
+  private final String branch;
+  private final Map<String, String> properties;
+
+  private final String key;
+  private final ProjectDefinition definition;
+  private final Charset encoding;
+
+  public AbstractProjectOrModule(ProjectDefinition definition, int scannerComponentId) {
+    super(scannerComponentId);
+    this.baseDir = initBaseDir(definition);
+    this.workDir = initWorkingDir(definition);
+    this.name = definition.getName();
+    this.originalName = definition.getOriginalName();
+    this.description = definition.getDescription();
+    this.keyWithBranch = definition.getKeyWithBranch();
+    this.branch = definition.getBranch();
+    this.properties = Collections.unmodifiableMap(new HashMap<>(definition.properties()));
+
+    this.definition = definition;
+    this.key = definition.getKey();
+    this.encoding = initEncoding(definition);
+  }
+
+  private static Charset initEncoding(ProjectDefinition module) {
+    String encodingStr = module.properties().get(CoreProperties.ENCODING_PROPERTY);
+    Charset result;
+    if (StringUtils.isNotEmpty(encodingStr)) {
+      result = Charset.forName(StringUtils.trim(encodingStr));
+    } else {
+      result = Charset.defaultCharset();
+    }
+    return result;
+  }
+
+  private static Path initBaseDir(ProjectDefinition module) {
+    Path result;
+    try {
+      result = module.getBaseDir().toPath().toRealPath(LinkOption.NOFOLLOW_LINKS);
+    } catch (IOException e) {
+      throw new IllegalStateException("Unable to resolve module baseDir", e);
+    }
+    return result;
+  }
+
+  private static Path initWorkingDir(ProjectDefinition module) {
+    File workingDirAsFile = module.getWorkDir();
+    Path workingDir = workingDirAsFile.getAbsoluteFile().toPath().normalize();
+    if (SystemUtils.IS_OS_WINDOWS) {
+      try {
+        Files.createDirectories(workingDir);
+        Files.setAttribute(workingDir, "dos:hidden", true, LinkOption.NOFOLLOW_LINKS);
+      } catch (IOException e) {
+        LOGGER.warn("Failed to set working directory hidden: {}", e.getMessage());
+      }
+    }
+    return workingDir;
+  }
+
+  /**
+   * Module key without branch
+   */
+  @Override
+  public String key() {
+    return key;
+  }
+
+  @Override
+  public boolean isFile() {
+    return false;
+  }
+
+  public ProjectDefinition definition() {
+    return definition;
+  }
+
+  public Path getBaseDir() {
+    return baseDir;
+  }
+
+  public Path getWorkDir() {
+    return workDir;
+  }
+
+  public String getKeyWithBranch() {
+    return keyWithBranch;
+  }
+
+  @CheckForNull
+  public String getBranch() {
+    return branch;
+  }
+
+  public Map<String, String> properties() {
+    return properties;
+  }
+
+  @CheckForNull
+  public String getOriginalName() {
+    return originalName;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public Charset getEncoding() {
+    return encoding;
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultFileSystem.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultFileSystem.java
new file mode 100644 (file)
index 0000000..1c17296
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.stream.StreamSupport;
+import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.FilePredicates;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputDir;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.scan.filesystem.PathResolver;
+import org.sonar.api.utils.PathUtils;
+import org.sonar.scanner.fs.predicates.DefaultFilePredicates;
+import org.sonar.scanner.fs.predicates.FileExtensionPredicate;
+import org.sonar.scanner.fs.predicates.OptimizedFilePredicateAdapter;
+
+/**
+ * @since 4.2
+ */
+public class DefaultFileSystem implements FileSystem {
+
+  private final Cache cache;
+  private final Path baseDir;
+  private Path workDir;
+  private Charset encoding;
+  protected final FilePredicates predicates;
+
+  /**
+   * Only for testing
+   */
+  public DefaultFileSystem(Path baseDir) {
+    this(baseDir, new MapCache());
+  }
+
+  /**
+   * Only for testing
+   */
+  public DefaultFileSystem(File baseDir) {
+    this(baseDir.toPath(), new MapCache());
+  }
+
+  protected DefaultFileSystem(Path baseDir, Cache cache) {
+    this.baseDir = baseDir;
+    this.cache = cache;
+    this.predicates = new DefaultFilePredicates(this.baseDir);
+  }
+
+  public Path baseDirPath() {
+    return baseDir;
+  }
+
+  @Override
+  public File baseDir() {
+    return baseDir.toFile();
+  }
+
+  public DefaultFileSystem setEncoding(Charset e) {
+    this.encoding = e;
+    return this;
+  }
+
+  @Override
+  public Charset encoding() {
+    return encoding;
+  }
+
+  public DefaultFileSystem setWorkDir(Path d) {
+    this.workDir = d;
+    return this;
+  }
+
+  @Override
+  public File workDir() {
+    return workDir.toFile();
+  }
+
+  @Override
+  public InputFile inputFile(FilePredicate predicate) {
+    Iterable<InputFile> files = inputFiles(predicate);
+    Iterator<InputFile> iterator = files.iterator();
+    if (!iterator.hasNext()) {
+      return null;
+    }
+    InputFile first = iterator.next();
+    if (!iterator.hasNext()) {
+      return first;
+    }
+
+    StringBuilder sb = new StringBuilder();
+    sb.append("expected one element but was: <" + first);
+    for (int i = 0; i < 4 && iterator.hasNext(); i++) {
+      sb.append(", " + iterator.next());
+    }
+    if (iterator.hasNext()) {
+      sb.append(", ...");
+    }
+    sb.append('>');
+
+    throw new IllegalArgumentException(sb.toString());
+
+  }
+
+  public Iterable<InputFile> inputFiles() {
+    return inputFiles(predicates.all());
+  }
+
+  @Override
+  public Iterable<InputFile> inputFiles(FilePredicate predicate) {
+    return OptimizedFilePredicateAdapter.create(predicate).get(cache);
+  }
+
+  @Override
+  public boolean hasFiles(FilePredicate predicate) {
+    return inputFiles(predicate).iterator().hasNext();
+  }
+
+  @Override
+  public Iterable<File> files(FilePredicate predicate) {
+    return () -> StreamSupport.stream(inputFiles(predicate).spliterator(), false)
+      .map(InputFile::file)
+      .iterator();
+  }
+
+  @Override
+  public InputDir inputDir(File dir) {
+    String relativePath = PathUtils.sanitize(new PathResolver().relativePath(baseDir.toFile(), dir));
+    if (relativePath == null) {
+      return null;
+    }
+    // Issues on InputDir are moved to the project, so we just return a fake InputDir for backward compatibility
+    return new DefaultInputDir("unused", relativePath).setModuleBaseDir(baseDir);
+  }
+
+  public DefaultFileSystem add(InputFile inputFile) {
+    cache.add(inputFile);
+    return this;
+  }
+
+  @Override
+  public SortedSet<String> languages() {
+    return cache.languages();
+  }
+
+  @Override
+  public FilePredicates predicates() {
+    return predicates;
+  }
+
+  public abstract static class Cache implements Index {
+
+    protected abstract void doAdd(InputFile inputFile);
+
+    final void add(InputFile inputFile) {
+      doAdd(inputFile);
+    }
+
+    protected abstract SortedSet<String> languages();
+  }
+
+  /**
+   * Used only for testing
+   */
+  private static class MapCache extends Cache {
+    private final Map<String, InputFile> fileMap = new HashMap<>();
+    private final Map<String, Set<InputFile>> filesByNameCache = new HashMap<>();
+    private final Map<String, Set<InputFile>> filesByExtensionCache = new HashMap<>();
+    private SortedSet<String> languages = new TreeSet<>();
+
+    @Override
+    public Iterable<InputFile> inputFiles() {
+      return new ArrayList<>(fileMap.values());
+    }
+
+    @Override
+    public InputFile inputFile(String relativePath) {
+      return fileMap.get(relativePath);
+    }
+
+    @Override
+    public Iterable<InputFile> getFilesByName(String filename) {
+      return filesByNameCache.get(filename);
+    }
+
+    @Override
+    public Iterable<InputFile> getFilesByExtension(String extension) {
+      return filesByExtensionCache.get(extension);
+    }
+
+    @Override
+    protected void doAdd(InputFile inputFile) {
+      if (inputFile.language() != null) {
+        languages.add(inputFile.language());
+      }
+      fileMap.put(inputFile.relativePath(), inputFile);
+      filesByNameCache.computeIfAbsent(inputFile.filename(), x -> new HashSet<>()).add(inputFile);
+      filesByExtensionCache.computeIfAbsent(FileExtensionPredicate.getExtension(inputFile), x -> new HashSet<>()).add(inputFile);
+    }
+
+    @Override
+    protected SortedSet<String> languages() {
+      return languages;
+    }
+  }
+
+  @Override
+  public File resolvePath(String path) {
+    File file = new File(path);
+    if (!file.isAbsolute()) {
+      try {
+        file = new File(baseDir(), path).getCanonicalFile();
+      } catch (IOException e) {
+        throw new IllegalArgumentException("Unable to resolve path '" + path + "'", e);
+      }
+    }
+    return file;
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultIndexedFile.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultIndexedFile.java
new file mode 100644 (file)
index 0000000..a3d0602
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.batch.fs.IndexedFile;
+import org.sonar.api.batch.fs.InputFile.Type;
+import org.sonar.api.utils.PathUtils;
+
+/**
+ * @since 6.3
+ */
+@Immutable
+public class DefaultIndexedFile extends DefaultInputComponent implements IndexedFile {
+  private final String projectRelativePath;
+  private final String moduleRelativePath;
+  private final String projectKey;
+  private final String language;
+  private final Type type;
+  private final Path absolutePath;
+  private final SensorStrategy sensorStrategy;
+
+  /**
+   * Testing purposes only!
+   */
+  public DefaultIndexedFile(String projectKey, Path baseDir, String relativePath, @Nullable String language) {
+    this(baseDir.resolve(relativePath), projectKey, relativePath, relativePath, Type.MAIN, language, TestInputFileBuilder.nextBatchId(),
+      new SensorStrategy());
+  }
+
+  public DefaultIndexedFile(Path absolutePath, String projectKey, String projectRelativePath, String moduleRelativePath, Type type, @Nullable String language, int batchId,
+    SensorStrategy sensorStrategy) {
+    super(batchId);
+    this.projectKey = projectKey;
+    this.projectRelativePath = PathUtils.sanitize(projectRelativePath);
+    this.moduleRelativePath = PathUtils.sanitize(moduleRelativePath);
+    this.type = type;
+    this.language = language;
+    this.sensorStrategy = sensorStrategy;
+    this.absolutePath = absolutePath;
+  }
+
+  @Override
+  public String relativePath() {
+    return sensorStrategy.isGlobal() ? projectRelativePath : moduleRelativePath;
+  }
+
+  public String getModuleRelativePath() {
+    return moduleRelativePath;
+  }
+
+  public String getProjectRelativePath() {
+    return projectRelativePath;
+  }
+
+  @Override
+  public String absolutePath() {
+    return PathUtils.sanitize(path().toString());
+  }
+
+  @Override
+  public File file() {
+    return path().toFile();
+  }
+
+  @Override
+  public Path path() {
+    return absolutePath;
+  }
+
+  @Override
+  public InputStream inputStream() throws IOException {
+    return Files.newInputStream(path());
+  }
+
+  @CheckForNull
+  @Override
+  public String language() {
+    return language;
+  }
+
+  @Override
+  public Type type() {
+    return type;
+  }
+
+  /**
+   * Component key (without branch).
+   */
+  @Override
+  public String key() {
+    return new StringBuilder().append(projectKey).append(":").append(projectRelativePath).toString();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+
+    if (!(o instanceof DefaultIndexedFile)) {
+      return false;
+    }
+
+    DefaultIndexedFile that = (DefaultIndexedFile) o;
+    return projectRelativePath.equals(that.projectRelativePath);
+  }
+
+  @Override
+  public int hashCode() {
+    return projectRelativePath.hashCode();
+  }
+
+  @Override
+  public String toString() {
+    return projectRelativePath;
+  }
+
+  @Override
+  public boolean isFile() {
+    return true;
+  }
+
+  @Override
+  public String filename() {
+    return path().getFileName().toString();
+  }
+
+  @Override
+  public URI uri() {
+    return path().toUri();
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputComponent.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputComponent.java
new file mode 100644 (file)
index 0000000..11601e0
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.sonar.api.batch.fs.InputComponent;
+import org.sonar.api.batch.measure.Metric;
+
+/**
+ * @since 5.2
+ */
+public abstract class DefaultInputComponent implements InputComponent {
+  private int id;
+  private Set<String> storedMetricKeys = new HashSet<>();
+
+  public DefaultInputComponent(int scannerId) {
+    this.id = scannerId;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || this.getClass() != o.getClass()) {
+      return false;
+    }
+
+    DefaultInputComponent that = (DefaultInputComponent) o;
+    return key().equals(that.key());
+  }
+
+  public int scannerId() {
+    return id;
+  }
+
+  @Override
+  public int hashCode() {
+    return key().hashCode();
+  }
+
+  @Override
+  public String toString() {
+    return "[key=" + key() + "]";
+  }
+
+  public void setHasMeasureFor(Metric metric) {
+    storedMetricKeys.add(metric.key());
+  }
+
+  public boolean hasMeasureFor(Metric metric) {
+    return storedMetricKeys.contains(metric.key());
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputDir.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputDir.java
new file mode 100644 (file)
index 0000000..13eb77b
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import java.io.File;
+import java.net.URI;
+import java.nio.file.Path;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.batch.fs.InputDir;
+import org.sonar.api.utils.PathUtils;
+
+/**
+ * @since 4.5
+ */
+public class DefaultInputDir extends DefaultInputComponent implements InputDir {
+
+  private final String relativePath;
+  private final String moduleKey;
+  private Path moduleBaseDir;
+
+  public DefaultInputDir(String moduleKey, String relativePath) {
+    super(-1);
+    this.moduleKey = moduleKey;
+    this.relativePath = PathUtils.sanitize(relativePath);
+  }
+
+  @Override
+  public String relativePath() {
+    return relativePath;
+  }
+
+  @Override
+  public String absolutePath() {
+    return PathUtils.sanitize(path().toString());
+  }
+
+  @Override
+  public File file() {
+    return path().toFile();
+  }
+
+  @Override
+  public Path path() {
+    if (moduleBaseDir == null) {
+      throw new IllegalStateException("Can not return the java.nio.file.Path because module baseDir is not set (see method setModuleBaseDir(java.io.File))");
+    }
+    return moduleBaseDir.resolve(relativePath);
+  }
+
+  public String moduleKey() {
+    return moduleKey;
+  }
+
+  @Override
+  public String key() {
+    StringBuilder sb = new StringBuilder().append(moduleKey).append(":");
+    if (StringUtils.isEmpty(relativePath)) {
+      sb.append("/");
+    } else {
+      sb.append(relativePath);
+    }
+    return sb.toString();
+  }
+
+  /**
+   * For testing purpose. Will be automatically set when dir is added to {@link DefaultFileSystem}
+   */
+  public DefaultInputDir setModuleBaseDir(Path moduleBaseDir) {
+    this.moduleBaseDir = moduleBaseDir.normalize();
+    return this;
+  }
+
+  @Override
+  public boolean isFile() {
+    return false;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || this.getClass() != o.getClass()) {
+      return false;
+    }
+
+    DefaultInputDir that = (DefaultInputDir) o;
+    return moduleKey.equals(that.moduleKey) && relativePath.equals(that.relativePath);
+  }
+
+  @Override
+  public int hashCode() {
+    return moduleKey.hashCode() + relativePath.hashCode() * 13;
+  }
+
+  @Override
+  public String toString() {
+    return "[moduleKey=" + moduleKey + ", relative=" + relativePath + ", basedir=" + moduleBaseDir + "]";
+  }
+
+  @Override
+  public URI uri() {
+    return path().toUri();
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputFile.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputFile.java
new file mode 100644 (file)
index 0000000..f29f11d
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.apache.commons.io.ByteOrderMark;
+import org.apache.commons.io.input.BOMInputStream;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextPointer;
+import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.fs.internal.Metadata;
+
+import static org.sonar.api.utils.Preconditions.checkArgument;
+import static org.sonar.api.utils.Preconditions.checkState;
+
+/**
+ * @since 4.2
+ * To create {@link InputFile} in tests, use {@link TestInputFileBuilder}.
+ */
+public class DefaultInputFile extends DefaultInputComponent implements InputFile {
+
+  private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+
+  private final DefaultIndexedFile indexedFile;
+  private final String contents;
+  private final Consumer<DefaultInputFile> metadataGenerator;
+
+  private boolean published;
+  private boolean excludedForCoverage;
+  private boolean excludedForDuplication;
+  private boolean ignoreAllIssues;
+  // Lazy init to save memory
+  private BitSet noSonarLines;
+  private Status status;
+  private Charset charset;
+  private Metadata metadata;
+  private Collection<int[]> ignoreIssuesOnlineRanges;
+  private BitSet executableLines;
+
+  public DefaultInputFile(DefaultIndexedFile indexedFile, Consumer<DefaultInputFile> metadataGenerator) {
+    this(indexedFile, metadataGenerator, null);
+  }
+
+  // For testing
+  public DefaultInputFile(DefaultIndexedFile indexedFile, Consumer<DefaultInputFile> metadataGenerator, @Nullable String contents) {
+    super(indexedFile.scannerId());
+    this.indexedFile = indexedFile;
+    this.metadataGenerator = metadataGenerator;
+    this.metadata = null;
+    this.published = false;
+    this.excludedForCoverage = false;
+    this.contents = contents;
+  }
+
+  public void checkMetadata() {
+    if (metadata == null) {
+      metadataGenerator.accept(this);
+    }
+  }
+
+  @Override
+  public InputStream inputStream() throws IOException {
+    return contents != null ? new ByteArrayInputStream(contents.getBytes(charset()))
+      : new BOMInputStream(Files.newInputStream(path()),
+      ByteOrderMark.UTF_8, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE);
+  }
+
+  @Override
+  public String contents() throws IOException {
+    if (contents != null) {
+      return contents;
+    } else {
+      ByteArrayOutputStream result = new ByteArrayOutputStream();
+      try (InputStream inputStream = inputStream()) {
+        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+        int length;
+        while ((length = inputStream.read(buffer)) != -1) {
+          result.write(buffer, 0, length);
+        }
+      }
+      return result.toString(charset().name());
+    }
+  }
+
+  public DefaultInputFile setPublished(boolean published) {
+    this.published = published;
+    return this;
+  }
+
+  public boolean isPublished() {
+    return published;
+  }
+
+  public DefaultInputFile setExcludedForCoverage(boolean excludedForCoverage) {
+    this.excludedForCoverage = excludedForCoverage;
+    return this;
+  }
+
+  public boolean isExcludedForCoverage() {
+    return excludedForCoverage;
+  }
+
+  public DefaultInputFile setExcludedForDuplication(boolean excludedForDuplication) {
+    this.excludedForDuplication = excludedForDuplication;
+    return this;
+  }
+
+  public boolean isExcludedForDuplication() {
+    return excludedForDuplication;
+  }
+
+  /**
+   * @deprecated since 6.6
+   */
+  @Deprecated
+  @Override
+  public String relativePath() {
+    return indexedFile.relativePath();
+  }
+
+  public String getModuleRelativePath() {
+    return indexedFile.getModuleRelativePath();
+  }
+
+  public String getProjectRelativePath() {
+    return indexedFile.getProjectRelativePath();
+  }
+
+  @Override
+  public String absolutePath() {
+    return indexedFile.absolutePath();
+  }
+
+  @Override
+  public File file() {
+    return indexedFile.file();
+  }
+
+  @Override
+  public Path path() {
+    return indexedFile.path();
+  }
+
+  @CheckForNull
+  @Override
+  public String language() {
+    return indexedFile.language();
+  }
+
+  @Override
+  public Type type() {
+    return indexedFile.type();
+  }
+
+  /**
+   * Component key (without branch).
+   */
+  @Override
+  public String key() {
+    return indexedFile.key();
+  }
+
+  @Override
+  public int hashCode() {
+    return indexedFile.hashCode();
+  }
+
+  @Override
+  public String toString() {
+    return indexedFile.toString();
+  }
+
+  /**
+   * {@link #setStatus(Status)}
+   */
+  @Override
+  public Status status() {
+    checkMetadata();
+    return status;
+  }
+
+  @Override
+  public int lines() {
+    checkMetadata();
+    return metadata.lines();
+  }
+
+  @Override
+  public boolean isEmpty() {
+    checkMetadata();
+    return metadata.isEmpty();
+  }
+
+  @Override
+  public Charset charset() {
+    checkMetadata();
+    return charset;
+  }
+
+  public int lastValidOffset() {
+    checkMetadata();
+    return metadata.lastValidOffset();
+  }
+
+  /**
+   * Digest hash of the file.
+   */
+  public String hash() {
+    checkMetadata();
+    return metadata.hash();
+  }
+
+  public int nonBlankLines() {
+    checkMetadata();
+    return metadata.nonBlankLines();
+  }
+
+  public int[] originalLineStartOffsets() {
+    checkMetadata();
+    checkState(metadata.originalLineStartOffsets() != null, "InputFile is not properly initialized.");
+    checkState(metadata.originalLineStartOffsets().length == metadata.lines(),
+      "InputFile is not properly initialized. 'originalLineStartOffsets' property length should be equal to 'lines'");
+    return metadata.originalLineStartOffsets();
+  }
+
+  public int[] originalLineEndOffsets() {
+    checkMetadata();
+    checkState(metadata.originalLineEndOffsets() != null, "InputFile is not properly initialized.");
+    checkState(metadata.originalLineEndOffsets().length == metadata.lines(),
+      "InputFile is not properly initialized. 'originalLineEndOffsets' property length should be equal to 'lines'");
+    return metadata.originalLineEndOffsets();
+  }
+
+  @Override
+  public TextPointer newPointer(int line, int lineOffset) {
+    checkMetadata();
+    DefaultTextPointer textPointer = new DefaultTextPointer(line, lineOffset);
+    checkValid(textPointer, "pointer");
+    return textPointer;
+  }
+
+  @Override
+  public TextRange newRange(TextPointer start, TextPointer end) {
+    checkMetadata();
+    checkValid(start, "start pointer");
+    checkValid(end, "end pointer");
+    return newRangeValidPointers(start, end, false);
+  }
+
+  @Override
+  public TextRange newRange(int startLine, int startLineOffset, int endLine, int endLineOffset) {
+    checkMetadata();
+    TextPointer start = newPointer(startLine, startLineOffset);
+    TextPointer end = newPointer(endLine, endLineOffset);
+    return newRangeValidPointers(start, end, false);
+  }
+
+  @Override
+  public TextRange selectLine(int line) {
+    checkMetadata();
+    TextPointer startPointer = newPointer(line, 0);
+    TextPointer endPointer = newPointer(line, lineLength(line));
+    return newRangeValidPointers(startPointer, endPointer, true);
+  }
+
+  public void validate(TextRange range) {
+    checkMetadata();
+    checkValid(range.start(), "start pointer");
+    checkValid(range.end(), "end pointer");
+  }
+
+  /**
+   * Create Range from global offsets. Used for backward compatibility with older API.
+   */
+  public TextRange newRange(int startOffset, int endOffset) {
+    checkMetadata();
+    return newRangeValidPointers(newPointer(startOffset), newPointer(endOffset), false);
+  }
+
+  public TextPointer newPointer(int globalOffset) {
+    checkMetadata();
+    checkArgument(globalOffset >= 0, "%s is not a valid offset for a file", globalOffset);
+    checkArgument(globalOffset <= lastValidOffset(), "%s is not a valid offset for file %s. Max offset is %s", globalOffset, this, lastValidOffset());
+    int line = findLine(globalOffset);
+    int startLineOffset = originalLineStartOffsets()[line - 1];
+    // In case the global offset is between \r and \n, move the pointer to a valid location
+    return new DefaultTextPointer(line, Math.min(globalOffset, originalLineEndOffsets()[line - 1]) - startLineOffset);
+  }
+
+  public DefaultInputFile setStatus(Status status) {
+    this.status = status;
+    return this;
+  }
+
+  public DefaultInputFile setCharset(Charset charset) {
+    this.charset = charset;
+    return this;
+  }
+
+  private void checkValid(TextPointer pointer, String owner) {
+    checkArgument(pointer.line() >= 1, "%s is not a valid line for a file", pointer.line());
+    checkArgument(pointer.line() <= this.metadata.lines(), "%s is not a valid line for %s. File %s has %s line(s)", pointer.line(), owner, this, metadata.lines());
+    checkArgument(pointer.lineOffset() >= 0, "%s is not a valid line offset for a file", pointer.lineOffset());
+    int lineLength = lineLength(pointer.line());
+    checkArgument(pointer.lineOffset() <= lineLength,
+      "%s is not a valid line offset for %s. File %s has %s character(s) at line %s", pointer.lineOffset(), owner, this, lineLength, pointer.line());
+  }
+
+  private int lineLength(int line) {
+    return originalLineEndOffsets()[line - 1] - originalLineStartOffsets()[line - 1];
+  }
+
+  private static TextRange newRangeValidPointers(TextPointer start, TextPointer end, boolean acceptEmptyRange) {
+    checkArgument(acceptEmptyRange ? (start.compareTo(end) <= 0) : (start.compareTo(end) < 0),
+      "Start pointer %s should be before end pointer %s", start, end);
+    return new DefaultTextRange(start, end);
+  }
+
+  private int findLine(int globalOffset) {
+    return Math.abs(Arrays.binarySearch(originalLineStartOffsets(), globalOffset) + 1);
+  }
+
+  public DefaultInputFile setMetadata(Metadata metadata) {
+    this.metadata = metadata;
+    return this;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) {
+      return false;
+    }
+
+    if (this.getClass() != obj.getClass()) {
+      return false;
+    }
+
+    DefaultInputFile that = (DefaultInputFile) obj;
+    return this.getProjectRelativePath().equals(that.getProjectRelativePath());
+  }
+
+  @Override
+  public boolean isFile() {
+    return true;
+  }
+
+  @Override
+  public String filename() {
+    return indexedFile.filename();
+  }
+
+  @Override
+  public URI uri() {
+    return indexedFile.uri();
+  }
+
+  public void noSonarAt(Set<Integer> noSonarLines) {
+    if (this.noSonarLines == null) {
+      this.noSonarLines = new BitSet(lines());
+    }
+    noSonarLines.forEach(l -> this.noSonarLines.set(l - 1));
+  }
+
+  public boolean hasNoSonarAt(int line) {
+    if (this.noSonarLines == null) {
+      return false;
+    }
+    return this.noSonarLines.get(line - 1);
+  }
+
+  public boolean isIgnoreAllIssues() {
+    return ignoreAllIssues;
+  }
+
+  public void setIgnoreAllIssues(boolean ignoreAllIssues) {
+    this.ignoreAllIssues = ignoreAllIssues;
+  }
+
+  public void addIgnoreIssuesOnLineRanges(Collection<int[]> lineRanges) {
+    if (this.ignoreIssuesOnlineRanges == null) {
+      this.ignoreIssuesOnlineRanges = new ArrayList<>();
+    }
+    this.ignoreIssuesOnlineRanges.addAll(lineRanges);
+  }
+
+  public boolean isIgnoreAllIssuesOnLine(@Nullable Integer line) {
+    if (line == null || ignoreIssuesOnlineRanges == null) {
+      return false;
+    }
+    return ignoreIssuesOnlineRanges.stream().anyMatch(r -> r[0] <= line && line <= r[1]);
+  }
+
+  public void setExecutableLines(Set<Integer> executableLines) {
+    checkState(this.executableLines == null, "Executable lines have already been saved for file: {}", this.toString());
+    this.executableLines = new BitSet(lines());
+    executableLines.forEach(l -> this.executableLines.set(l - 1));
+  }
+
+  public Optional<Set<Integer>> getExecutableLines() {
+    if (this.executableLines == null) {
+      return Optional.empty();
+    }
+    return Optional.of(this.executableLines.stream().map(i -> i + 1).boxed().collect(Collectors.toSet()));
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputModule.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputModule.java
new file mode 100644 (file)
index 0000000..a729bff
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.InputModule;
+import org.sonar.api.scan.filesystem.PathResolver;
+
+import static org.sonar.api.config.internal.MultivalueProperty.parseAsCsv;
+
+@Immutable
+public class DefaultInputModule extends AbstractProjectOrModule implements InputModule {
+
+  private final List<Path> sourceDirsOrFiles;
+  private final List<Path> testDirsOrFiles;
+
+  /**
+   * For testing only!
+   */
+  public DefaultInputModule(ProjectDefinition definition) {
+    this(definition, 0);
+  }
+
+  public DefaultInputModule(ProjectDefinition definition, int scannerComponentId) {
+    super(definition, scannerComponentId);
+
+    this.sourceDirsOrFiles = initSources(definition, ProjectDefinition.SOURCES_PROPERTY);
+    this.testDirsOrFiles = initSources(definition, ProjectDefinition.TESTS_PROPERTY);
+  }
+
+  @CheckForNull
+  private List<Path> initSources(ProjectDefinition module, String propertyKey) {
+    if (!module.properties().containsKey(propertyKey)) {
+      return null;
+    }
+    List<Path> result = new ArrayList<>();
+    PathResolver pathResolver = new PathResolver();
+    String srcPropValue = module.properties().get(propertyKey);
+    if (srcPropValue != null) {
+      for (String sourcePath : parseAsCsv(propertyKey, srcPropValue)) {
+        File dirOrFile = pathResolver.relativeFile(getBaseDir().toFile(), sourcePath);
+        if (dirOrFile.exists()) {
+          result.add(dirOrFile.toPath());
+        }
+      }
+    }
+    return result;
+  }
+
+  public Optional<List<Path>> getSourceDirsOrFiles() {
+    return Optional.ofNullable(sourceDirsOrFiles);
+  }
+
+  public Optional<List<Path>> getTestDirsOrFiles() {
+    return Optional.ofNullable(testDirsOrFiles);
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputProject.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultInputProject.java
new file mode 100644 (file)
index 0000000..9dcd025
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.scanner.fs.InputProject;
+
+@Immutable
+public class DefaultInputProject extends AbstractProjectOrModule implements InputProject {
+
+  /**
+   * For testing only!
+   */
+  public DefaultInputProject(ProjectDefinition definition) {
+    super(definition, 0);
+  }
+
+  public DefaultInputProject(ProjectDefinition definition, int scannerComponentId) {
+    super(definition, scannerComponentId);
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultTextPointer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultTextPointer.java
new file mode 100644 (file)
index 0000000..5cfc84f
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import org.sonar.api.batch.fs.TextPointer;
+
+/**
+ * @since 5.2
+ */
+public class DefaultTextPointer implements TextPointer {
+
+  private final int line;
+  private final int lineOffset;
+
+  public DefaultTextPointer(int line, int lineOffset) {
+    this.line = line;
+    this.lineOffset = lineOffset;
+  }
+
+  @Override
+  public int line() {
+    return line;
+  }
+
+  @Override
+  public int lineOffset() {
+    return lineOffset;
+  }
+
+  @Override
+  public String toString() {
+    return "[line=" + line + ", lineOffset=" + lineOffset + "]";
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (!(obj instanceof DefaultTextPointer)) {
+      return false;
+    }
+    DefaultTextPointer other = (DefaultTextPointer) obj;
+    return other.line == this.line && other.lineOffset == this.lineOffset;
+  }
+
+  @Override
+  public int hashCode() {
+    return 37 * this.line + lineOffset;
+  }
+
+  @Override
+  public int compareTo(TextPointer o) {
+    if (this.line == o.line()) {
+      return Integer.compare(this.lineOffset, o.lineOffset());
+    }
+    return Integer.compare(this.line, o.line());
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultTextRange.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/DefaultTextRange.java
new file mode 100644 (file)
index 0000000..f83c330
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import org.sonar.api.batch.fs.TextPointer;
+import org.sonar.api.batch.fs.TextRange;
+
+/**
+ * @since 5.2
+ */
+public class DefaultTextRange implements TextRange {
+
+  private final TextPointer start;
+  private final TextPointer end;
+
+  public DefaultTextRange(TextPointer start, TextPointer end) {
+    this.start = start;
+    this.end = end;
+  }
+
+  @Override
+  public TextPointer start() {
+    return start;
+  }
+
+  @Override
+  public TextPointer end() {
+    return end;
+  }
+
+  @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;
+  }
+
+  @Override
+  public String toString() {
+    return "Range[from " + start + " to " + end + "]";
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (!(obj instanceof DefaultTextRange)) {
+      return false;
+    }
+    DefaultTextRange other = (DefaultTextRange) obj;
+    return start.equals(other.start) && end.equals(other.end);
+  }
+
+  @Override
+  public int hashCode() {
+    return start.hashCode() * 17 + end.hashCode();
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/FileMetadata.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/FileMetadata.java
new file mode 100644 (file)
index 0000000..1b98416
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.Metadata;
+import org.sonar.scanner.fs.charhandler.CharHandler;
+import org.sonar.scanner.fs.charhandler.FileHashComputer;
+import org.sonar.scanner.fs.charhandler.LineCounter;
+import org.sonar.scanner.fs.charhandler.LineHashComputer;
+import org.sonar.scanner.fs.charhandler.LineOffsetCounter;
+
+/**
+ * Computes hash of files. Ends of Lines are ignored, so files with
+ * same content but different EOL encoding have the same hash.
+ */
+@Immutable
+public class FileMetadata {
+  private static final char LINE_FEED = '\n';
+  private static final char CARRIAGE_RETURN = '\r';
+
+  /**
+   * Compute hash of a file ignoring line ends differences.
+   * Maximum performance is needed.
+   */
+  public Metadata readMetadata(InputStream stream, Charset encoding, String filePath, @Nullable CharHandler otherHandler) {
+    LineCounter lineCounter = new LineCounter(filePath, encoding);
+    FileHashComputer fileHashComputer = new FileHashComputer(filePath);
+    LineOffsetCounter lineOffsetCounter = new LineOffsetCounter();
+
+    if (otherHandler != null) {
+      CharHandler[] handlers = {lineCounter, fileHashComputer, lineOffsetCounter, otherHandler};
+      readFile(stream, encoding, filePath, handlers);
+    } else {
+      CharHandler[] handlers = {lineCounter, fileHashComputer, lineOffsetCounter};
+      readFile(stream, encoding, filePath, handlers);
+    }
+    return new Metadata(lineCounter.lines(), lineCounter.nonBlankLines(), fileHashComputer.getHash(), lineOffsetCounter.getOriginalLineStartOffsets(),
+      lineOffsetCounter.getOriginalLineEndOffsets(),
+      lineOffsetCounter.getLastValidOffset());
+  }
+
+  public Metadata readMetadata(InputStream stream, Charset encoding, String filePath) {
+    return readMetadata(stream, encoding, filePath, null);
+  }
+
+  /**
+   * For testing purpose
+   */
+  public Metadata readMetadata(Reader reader) {
+    LineCounter lineCounter = new LineCounter("fromString", StandardCharsets.UTF_16);
+    FileHashComputer fileHashComputer = new FileHashComputer("fromString");
+    LineOffsetCounter lineOffsetCounter = new LineOffsetCounter();
+    CharHandler[] handlers = {lineCounter, fileHashComputer, lineOffsetCounter};
+
+    try {
+      read(reader, handlers);
+    } catch (IOException e) {
+      throw new IllegalStateException("Should never occur", e);
+    }
+    return new Metadata(lineCounter.lines(), lineCounter.nonBlankLines(), fileHashComputer.getHash(), lineOffsetCounter.getOriginalLineStartOffsets(),
+      lineOffsetCounter.getOriginalLineEndOffsets(),
+      lineOffsetCounter.getLastValidOffset());
+  }
+
+  public static void readFile(InputStream stream, Charset encoding, String filePath, CharHandler[] handlers) {
+    try (Reader reader = new BufferedReader(new InputStreamReader(stream, encoding))) {
+      read(reader, handlers);
+    } catch (IOException e) {
+      throw new IllegalStateException(String.format("Fail to read file '%s' with encoding '%s'", filePath, encoding), e);
+    }
+  }
+
+  private static void read(Reader reader, CharHandler[] handlers) throws IOException {
+    char c;
+    int i = reader.read();
+    boolean afterCR = false;
+    while (i != -1) {
+      c = (char) i;
+      if (afterCR) {
+        for (CharHandler handler : handlers) {
+          if (c == CARRIAGE_RETURN) {
+            handler.newLine();
+            handler.handleAll(c);
+          } else if (c == LINE_FEED) {
+            handler.handleAll(c);
+            handler.newLine();
+          } else {
+            handler.newLine();
+            handler.handleIgnoreEoL(c);
+            handler.handleAll(c);
+          }
+        }
+        afterCR = c == CARRIAGE_RETURN;
+      } else if (c == LINE_FEED) {
+        for (CharHandler handler : handlers) {
+          handler.handleAll(c);
+          handler.newLine();
+        }
+      } else if (c == CARRIAGE_RETURN) {
+        afterCR = true;
+        for (CharHandler handler : handlers) {
+          handler.handleAll(c);
+        }
+      } else {
+        for (CharHandler handler : handlers) {
+          handler.handleIgnoreEoL(c);
+          handler.handleAll(c);
+        }
+      }
+      i = reader.read();
+    }
+    for (CharHandler handler : handlers) {
+      if (afterCR) {
+        handler.newLine();
+      }
+      handler.eof();
+    }
+  }
+
+  @FunctionalInterface
+  public interface LineHashConsumer {
+    void consume(int lineIdx, @Nullable byte[] hash);
+  }
+
+  /**
+   * Compute a MD5 hash of each line of the file after removing of all blank chars
+   */
+  public static void computeLineHashesForIssueTracking(InputFile f, LineHashConsumer consumer) {
+    try {
+      readFile(f.inputStream(), f.charset(), f.absolutePath(), new CharHandler[] {new LineHashComputer(consumer, f.file())});
+    } catch (IOException e) {
+      throw new IllegalStateException("Failed to compute line hashes for " + f.absolutePath(), e);
+    }
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/InputModuleHierarchy.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/InputModuleHierarchy.java
new file mode 100644 (file)
index 0000000..9048824
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import java.util.Collection;
+import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
+public interface InputModuleHierarchy {
+  DefaultInputModule root();
+
+  boolean isRoot(DefaultInputModule module);
+
+  Collection<DefaultInputModule> children(DefaultInputModule module);
+
+  @CheckForNull
+  DefaultInputModule parent(DefaultInputModule module);
+
+  @CheckForNull
+  String relativePath(DefaultInputModule module);
+
+  @CheckForNull
+  String relativePathToRoot(DefaultInputModule module);
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/PathPattern.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/PathPattern.java
new file mode 100644 (file)
index 0000000..5d5947d
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import java.nio.file.Path;
+import javax.annotation.concurrent.ThreadSafe;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.utils.PathUtils;
+import org.sonar.api.utils.WildcardPattern;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+
+@ThreadSafe
+public abstract class PathPattern {
+
+  private static final Logger LOG = Loggers.get(PathPattern.class);
+
+  /**
+   * @deprecated since 6.6
+   */
+  @Deprecated
+  private static final String ABSOLUTE_PATH_PATTERN_PREFIX = "file:";
+  final WildcardPattern pattern;
+
+  PathPattern(String pattern) {
+    this.pattern = WildcardPattern.create(pattern);
+  }
+
+  public abstract boolean match(Path absolutePath, Path relativePath);
+
+  public abstract boolean match(Path absolutePath, Path relativePath, boolean caseSensitiveFileExtension);
+
+  public static PathPattern create(String s) {
+    String trimmed = StringUtils.trim(s);
+    if (StringUtils.startsWithIgnoreCase(trimmed, ABSOLUTE_PATH_PATTERN_PREFIX)) {
+      LOG.warn("Using absolute path pattern is deprecated. Please use relative path instead of '" + trimmed + "'");
+      return new AbsolutePathPattern(StringUtils.substring(trimmed, ABSOLUTE_PATH_PATTERN_PREFIX.length()));
+    }
+    return new RelativePathPattern(trimmed);
+  }
+
+  public static PathPattern[] create(String[] s) {
+    PathPattern[] result = new PathPattern[s.length];
+    for (int i = 0; i < s.length; i++) {
+      result[i] = create(s[i]);
+    }
+    return result;
+  }
+
+  /**
+   * @deprecated since 6.6
+   */
+  @Deprecated
+  private static class AbsolutePathPattern extends PathPattern {
+    private AbsolutePathPattern(String pattern) {
+      super(pattern);
+    }
+
+    @Override
+    public boolean match(Path absolutePath, Path relativePath) {
+      return match(absolutePath, relativePath, true);
+    }
+
+    @Override
+    public boolean match(Path absolutePath, Path relativePath, boolean caseSensitiveFileExtension) {
+      String path = PathUtils.sanitize(absolutePath.toString());
+      if (!caseSensitiveFileExtension) {
+        String extension = sanitizeExtension(FilenameUtils.getExtension(path));
+        if (StringUtils.isNotBlank(extension)) {
+          path = StringUtils.removeEndIgnoreCase(path, extension);
+          path = path + extension;
+        }
+      }
+      return pattern.match(path);
+    }
+
+    @Override
+    public String toString() {
+      return ABSOLUTE_PATH_PATTERN_PREFIX + pattern.toString();
+    }
+  }
+
+  /**
+   * Path relative to module basedir
+   */
+  private static class RelativePathPattern extends PathPattern {
+    private RelativePathPattern(String pattern) {
+      super(pattern);
+    }
+
+    @Override
+    public boolean match(Path absolutePath, Path relativePath) {
+      return match(absolutePath, relativePath, true);
+    }
+
+    @Override
+    public boolean match(Path absolutePath, Path relativePath, boolean caseSensitiveFileExtension) {
+      String path = PathUtils.sanitize(relativePath.toString());
+      if (!caseSensitiveFileExtension) {
+        String extension = sanitizeExtension(FilenameUtils.getExtension(path));
+        if (StringUtils.isNotBlank(extension)) {
+          path = StringUtils.removeEndIgnoreCase(path, extension);
+          path = path + extension;
+        }
+      }
+      return path != null && pattern.match(path);
+    }
+
+    @Override
+    public String toString() {
+      return pattern.toString();
+    }
+  }
+
+  static String sanitizeExtension(String suffix) {
+    return StringUtils.lowerCase(StringUtils.removeStart(suffix, "."));
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/SensorStrategy.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/SensorStrategy.java
new file mode 100644 (file)
index 0000000..244a01b
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * A shared, mutable object in the project container.
+ * It's used during the execution of sensors to decide whether
+ * sensors should be executed once for the entire project, or per-module.
+ * It is also injected into each InputFile to change the behavior of {@link InputFile#relativePath()}
+ */
+public class SensorStrategy {
+
+  private boolean global = true;
+
+  public boolean isGlobal() {
+    return global;
+  }
+
+  public void setGlobal(boolean global) {
+    this.global = global;
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/TestInputFileBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/TestInputFileBuilder.java
new file mode 100644 (file)
index 0000000..5d6e8b9
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.Metadata;
+import org.sonar.api.utils.PathUtils;
+
+/**
+ * Intended to be used in unit tests that need to create {@link InputFile}s.
+ * An InputFile is unambiguously identified by a <b>module key</b> and a <b>relative path</b>, so these parameters are mandatory.
+ * <p>
+ * A module base directory is only needed to construct absolute paths.
+ * <p>
+ * Examples of usage of the constructors:
+ *
+ * <pre>
+ * InputFile file1 = TestInputFileBuilder.create("module1", "myfile.java").build();
+ * InputFile file2 = TestInputFileBuilder.create("", fs.baseDir(), myfile).build();
+ * </pre>
+ * <p>
+ * file1 will have the "module1" as both module key and module base directory.
+ * file2 has an empty string as module key, and a relative path which is the path from the filesystem base directory to myfile.
+ *
+ * @since 6.3
+ */
+public class TestInputFileBuilder {
+  private static int batchId = 1;
+
+  private final int id;
+  private final String relativePath;
+  private final String projectKey;
+  @CheckForNull
+  private Path projectBaseDir;
+  private Path moduleBaseDir;
+  private String language;
+  private InputFile.Type type = InputFile.Type.MAIN;
+  private InputFile.Status status;
+  private int lines = -1;
+  private Charset charset;
+  private String hash;
+  private int nonBlankLines;
+  private int[] originalLineStartOffsets = new int[0];
+  private int[] originalLineEndOffsets = new int[0];
+  private int lastValidOffset = -1;
+  private boolean publish = true;
+  private String contents;
+
+  /**
+   * Create a InputFile identified by the given project key and relative path.
+   */
+  public TestInputFileBuilder(String projectKey, String relativePath) {
+    this(projectKey, relativePath, batchId++);
+  }
+
+  /**
+   * Create a InputFile with a given module key and module base directory.
+   * The relative path is generated comparing the file path to the module base directory.
+   * filePath must point to a file that is within the module base directory.
+   */
+  public TestInputFileBuilder(String projectKey, File moduleBaseDir, File filePath) {
+    String relativePath = moduleBaseDir.toPath().relativize(filePath.toPath()).toString();
+    this.projectKey = projectKey;
+    setModuleBaseDir(moduleBaseDir.toPath());
+    this.relativePath = PathUtils.sanitize(relativePath);
+    this.id = batchId++;
+  }
+
+  public TestInputFileBuilder(String projectKey, String relativePath, int id) {
+    this.projectKey = projectKey;
+    setModuleBaseDir(Paths.get(projectKey));
+    this.relativePath = PathUtils.sanitize(relativePath);
+    this.id = id;
+  }
+
+  public static TestInputFileBuilder create(String moduleKey, File moduleBaseDir, File filePath) {
+    return new TestInputFileBuilder(moduleKey, moduleBaseDir, filePath);
+  }
+
+  public static TestInputFileBuilder create(String moduleKey, String relativePath) {
+    return new TestInputFileBuilder(moduleKey, relativePath);
+  }
+
+  public static int nextBatchId() {
+    return batchId++;
+  }
+
+  public TestInputFileBuilder setProjectBaseDir(Path projectBaseDir) {
+    this.projectBaseDir = normalize(projectBaseDir);
+    return this;
+  }
+
+  public TestInputFileBuilder setModuleBaseDir(Path moduleBaseDir) {
+    this.moduleBaseDir = normalize(moduleBaseDir);
+    return this;
+  }
+
+  private static Path normalize(Path path) {
+    try {
+      return path.normalize().toRealPath(LinkOption.NOFOLLOW_LINKS);
+    } catch (IOException e) {
+      return path.normalize();
+    }
+  }
+
+  public TestInputFileBuilder setLanguage(@Nullable String language) {
+    this.language = language;
+    return this;
+  }
+
+  public TestInputFileBuilder setType(InputFile.Type type) {
+    this.type = type;
+    return this;
+  }
+
+  public TestInputFileBuilder setStatus(InputFile.Status status) {
+    this.status = status;
+    return this;
+  }
+
+  public TestInputFileBuilder setLines(int lines) {
+    this.lines = lines;
+    return this;
+  }
+
+  public TestInputFileBuilder setCharset(Charset charset) {
+    this.charset = charset;
+    return this;
+  }
+
+  public TestInputFileBuilder setHash(String hash) {
+    this.hash = hash;
+    return this;
+  }
+
+  /**
+   * Set contents of the file and calculates metadata from it.
+   * The contents will be returned by {@link InputFile#contents()} and {@link InputFile#inputStream()} and can be
+   * inconsistent with the actual physical file pointed by {@link InputFile#path()}, {@link InputFile#absolutePath()}, etc.
+   */
+  public TestInputFileBuilder setContents(String content) {
+    this.contents = content;
+    initMetadata(content);
+    return this;
+  }
+
+  public TestInputFileBuilder setNonBlankLines(int nonBlankLines) {
+    this.nonBlankLines = nonBlankLines;
+    return this;
+  }
+
+  public TestInputFileBuilder setLastValidOffset(int lastValidOffset) {
+    this.lastValidOffset = lastValidOffset;
+    return this;
+  }
+
+  public TestInputFileBuilder setOriginalLineStartOffsets(int[] originalLineStartOffsets) {
+    this.originalLineStartOffsets = originalLineStartOffsets;
+    return this;
+  }
+
+  public TestInputFileBuilder setOriginalLineEndOffsets(int[] originalLineEndOffsets) {
+    this.originalLineEndOffsets = originalLineEndOffsets;
+    return this;
+  }
+
+  public TestInputFileBuilder setPublish(boolean publish) {
+    this.publish = publish;
+    return this;
+  }
+
+  public TestInputFileBuilder setMetadata(Metadata metadata) {
+    this.setLines(metadata.lines());
+    this.setLastValidOffset(metadata.lastValidOffset());
+    this.setNonBlankLines(metadata.nonBlankLines());
+    this.setHash(metadata.hash());
+    this.setOriginalLineStartOffsets(metadata.originalLineStartOffsets());
+    this.setOriginalLineEndOffsets(metadata.originalLineEndOffsets());
+    return this;
+  }
+
+  public TestInputFileBuilder initMetadata(String content) {
+    return setMetadata(new FileMetadata().readMetadata(new StringReader(content)));
+  }
+
+  public DefaultInputFile build() {
+    Path absolutePath = moduleBaseDir.resolve(relativePath);
+    if (projectBaseDir == null) {
+      projectBaseDir = moduleBaseDir;
+    }
+    String projectRelativePath = projectBaseDir.relativize(absolutePath).toString();
+    DefaultIndexedFile indexedFile = new DefaultIndexedFile(absolutePath, projectKey, projectRelativePath, relativePath, type, language, id, new SensorStrategy());
+    DefaultInputFile inputFile = new DefaultInputFile(indexedFile,
+      f -> f.setMetadata(new Metadata(lines, nonBlankLines, hash, originalLineStartOffsets, originalLineEndOffsets, lastValidOffset)),
+      contents);
+    inputFile.setStatus(status);
+    inputFile.setCharset(charset);
+    inputFile.setPublished(publish);
+    return inputFile;
+  }
+
+  public static DefaultInputModule newDefaultInputModule(String moduleKey, File baseDir) {
+    ProjectDefinition definition = ProjectDefinition.create()
+      .setKey(moduleKey)
+      .setBaseDir(baseDir)
+      .setWorkDir(new File(baseDir, ".sonar"));
+    return newDefaultInputModule(definition);
+  }
+
+  public static DefaultInputModule newDefaultInputModule(ProjectDefinition projectDefinition) {
+    return new DefaultInputModule(projectDefinition, TestInputFileBuilder.nextBatchId());
+  }
+
+  public static DefaultInputModule newDefaultInputModule(AbstractProjectOrModule parent, String key) throws IOException {
+    Path basedir = parent.getBaseDir().resolve(key);
+    Files.createDirectory(basedir);
+    return newDefaultInputModule(key, basedir.toFile());
+  }
+
+  public static DefaultInputProject newDefaultInputProject(String projectKey, File baseDir) {
+    ProjectDefinition definition = ProjectDefinition.create()
+      .setKey(projectKey)
+      .setBaseDir(baseDir)
+      .setWorkDir(new File(baseDir, ".sonar"));
+    return newDefaultInputProject(definition);
+  }
+
+  public static DefaultInputProject newDefaultInputProject(ProjectDefinition projectDefinition) {
+    return new DefaultInputProject(projectDefinition, TestInputFileBuilder.nextBatchId());
+  }
+
+  public static DefaultInputProject newDefaultInputProject(String key, Path baseDir) throws IOException {
+    Files.createDirectory(baseDir);
+    return newDefaultInputProject(key, baseDir.toFile());
+  }
+
+  public static DefaultInputDir newDefaultInputDir(AbstractProjectOrModule module, String relativePath) throws IOException {
+    Path basedir = module.getBaseDir().resolve(relativePath);
+    Files.createDirectory(basedir);
+    return new DefaultInputDir(module.key(), relativePath)
+      .setModuleBaseDir(module.getBaseDir());
+  }
+
+  public static DefaultInputFile newDefaultInputFile(Path projectBaseDir, AbstractProjectOrModule module, String relativePath) {
+    return new TestInputFileBuilder(module.key(), relativePath)
+      .setStatus(InputFile.Status.SAME)
+      .setProjectBaseDir(projectBaseDir)
+      .setModuleBaseDir(module.getBaseDir())
+      .build();
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/CharHandler.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/CharHandler.java
new file mode 100644 (file)
index 0000000..737f6c2
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.charhandler;
+
+public abstract class CharHandler {
+
+  public void handleAll(char c) {
+  }
+
+  public void handleIgnoreEoL(char c) {
+  }
+
+  public void newLine() {
+  }
+
+  public void eof() {
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/FileHashComputer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/FileHashComputer.java
new file mode 100644 (file)
index 0000000..46229a5
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.charhandler;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.codec.digest.DigestUtils;
+
+public class FileHashComputer extends CharHandler {
+  private static final char LINE_FEED = '\n';
+
+  
+  private MessageDigest globalMd5Digest = DigestUtils.getMd5Digest();
+  private StringBuilder sb = new StringBuilder();
+  private final CharsetEncoder encoder;
+  private final String filePath;
+
+  public FileHashComputer(String filePath) {
+    encoder = StandardCharsets.UTF_8.newEncoder()
+      .onMalformedInput(CodingErrorAction.REPLACE)
+      .onUnmappableCharacter(CodingErrorAction.REPLACE);
+    this.filePath = filePath;
+  }
+
+  @Override
+  public void handleIgnoreEoL(char c) {
+    sb.append(c);
+  }
+
+  @Override
+  public void newLine() {
+    sb.append(LINE_FEED);
+    processBuffer();
+    sb.setLength(0);
+  }
+
+  @Override
+  public void eof() {
+    if (sb.length() > 0) {
+      processBuffer();
+    }
+  }
+
+  private void processBuffer() {
+    try {
+      if (sb.length() > 0) {
+        ByteBuffer encoded = encoder.encode(CharBuffer.wrap(sb));
+        globalMd5Digest.update(encoded.array(), 0, encoded.limit());
+      }
+    } catch (CharacterCodingException e) {
+      throw new IllegalStateException("Error encoding line hash in file: " + filePath, e);
+    }
+  }
+
+  public String getHash() {
+    return Hex.encodeHexString(globalMd5Digest.digest());
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/IntArrayList.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/IntArrayList.java
new file mode 100644 (file)
index 0000000..1267414
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.charhandler;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * Specialization of {@link java.util.ArrayList} to create a list of int (only append elements) and then produce an int[].
+ */
+class IntArrayList {
+
+  /**
+   * Default initial capacity.
+   */
+  private static final int DEFAULT_CAPACITY = 10;
+
+  /**
+   * Shared empty array instance used for default sized empty instances. We
+   * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
+   * first element is added.
+   */
+  private static final int[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
+
+  /**
+   * The array buffer into which the elements of the ArrayList are stored.
+   * The capacity of the IntArrayList is the length of this array buffer. Any
+   * empty IntArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
+   * will be expanded to DEFAULT_CAPACITY when the first element is added.
+   */
+  private int[] elementData;
+
+  /**
+   * The size of the IntArrayList (the number of elements it contains).
+   */
+  private int size;
+
+  /**
+   * Constructs an empty list with an initial capacity of ten.
+   */
+  public IntArrayList() {
+    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
+  }
+
+  /**
+   * Trims the capacity of this <tt>IntArrayList</tt> instance to be the
+   * list's current size and return the internal array. An application can use this operation to minimize
+   * the storage of an <tt>IntArrayList</tt> instance.
+   */
+  public int[] trimAndGet() {
+    if (size < elementData.length) {
+      elementData = Arrays.copyOf(elementData, size);
+    }
+    return elementData;
+  }
+
+  private void ensureCapacityInternal(int minCapacity) {
+    int capacity = minCapacity;
+    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+      capacity = Math.max(DEFAULT_CAPACITY, minCapacity);
+    }
+
+    ensureExplicitCapacity(capacity);
+  }
+
+  private void ensureExplicitCapacity(int minCapacity) {
+    if (minCapacity - elementData.length > 0) {
+      grow(minCapacity);
+    }
+  }
+
+  /**
+   * Increases the capacity to ensure that it can hold at least the
+   * number of elements specified by the minimum capacity argument.
+   *
+   * @param minCapacity the desired minimum capacity
+   */
+  private void grow(int minCapacity) {
+    int oldCapacity = elementData.length;
+    int newCapacity = oldCapacity + (oldCapacity >> 1);
+    if (newCapacity - minCapacity < 0) {
+      newCapacity = minCapacity;
+    }
+    elementData = Arrays.copyOf(elementData, newCapacity);
+  }
+
+  /**
+   * Appends the specified element to the end of this list.
+   *
+   * @param e element to be appended to this list
+   * @return <tt>true</tt> (as specified by {@link Collection#add})
+   */
+  public boolean add(int e) {
+    ensureCapacityInternal(size + 1);
+    elementData[size] = e;
+    size++;
+    return true;
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/LineCounter.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/LineCounter.java
new file mode 100644 (file)
index 0000000..7cac0f2
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.charhandler;
+
+import java.nio.charset.Charset;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+
+public class LineCounter extends CharHandler {
+  private static final Logger LOG = Loggers.get(LineCounter.class);
+    
+  private int lines = 1;
+  private int nonBlankLines = 0;
+  private boolean blankLine = true;
+  boolean alreadyLoggedInvalidCharacter = false;
+  private final String filePath;
+  private final Charset encoding;
+
+  public LineCounter(String filePath, Charset encoding) {
+    this.filePath = filePath;
+    this.encoding = encoding;
+  }
+
+  @Override
+  public void handleAll(char c) {
+    if (!alreadyLoggedInvalidCharacter && c == '\ufffd') {
+      LOG.warn("Invalid character encountered in file {} at line {} for encoding {}. Please fix file content or configure the encoding to be used using property '{}'.", filePath,
+        lines, encoding, CoreProperties.ENCODING_PROPERTY);
+      alreadyLoggedInvalidCharacter = true;
+    }
+  }
+
+  @Override
+  public void newLine() {
+    lines++;
+    if (!blankLine) {
+      nonBlankLines++;
+    }
+    blankLine = true;
+  }
+
+  @Override
+  public void handleIgnoreEoL(char c) {
+    if (!Character.isWhitespace(c)) {
+      blankLine = false;
+    }
+  }
+
+  @Override
+  public void eof() {
+    if (!blankLine) {
+      nonBlankLines++;
+    }
+  }
+
+  public int lines() {
+    return lines;
+  }
+
+  public int nonBlankLines() {
+    return nonBlankLines;
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/LineHashComputer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/LineHashComputer.java
new file mode 100644 (file)
index 0000000..5435499
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.charhandler;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.sonar.scanner.fs.FileMetadata;
+
+public class LineHashComputer extends CharHandler {
+  private final MessageDigest lineMd5Digest = DigestUtils.getMd5Digest();
+  private final CharsetEncoder encoder;
+  private final StringBuilder sb = new StringBuilder();
+  private final FileMetadata.LineHashConsumer consumer;
+  private final File file;
+  private int line = 1;
+
+  public LineHashComputer(FileMetadata.LineHashConsumer consumer, File f) {
+    this.consumer = consumer;
+    this.file = f;
+    this.encoder = StandardCharsets.UTF_8.newEncoder()
+      .onMalformedInput(CodingErrorAction.REPLACE)
+      .onUnmappableCharacter(CodingErrorAction.REPLACE);
+  }
+
+  @Override
+  public void handleIgnoreEoL(char c) {
+    if (!Character.isWhitespace(c)) {
+      sb.append(c);
+    }
+  }
+
+  @Override
+  public void newLine() {
+    processBuffer();
+    sb.setLength(0);
+    line++;
+  }
+
+  @Override
+  public void eof() {
+    if (this.line > 0) {
+      processBuffer();
+    }
+  }
+
+  private void processBuffer() {
+    try {
+      if (sb.length() > 0) {
+        ByteBuffer encoded = encoder.encode(CharBuffer.wrap(sb));
+        lineMd5Digest.update(encoded.array(), 0, encoded.limit());
+        consumer.consume(line, lineMd5Digest.digest());
+      }
+    } catch (CharacterCodingException e) {
+      throw new IllegalStateException("Error encoding line hash in file: " + file.getAbsolutePath(), e);
+    }
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/LineOffsetCounter.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/LineOffsetCounter.java
new file mode 100644 (file)
index 0000000..74b7f48
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.charhandler;
+
+public class LineOffsetCounter extends CharHandler {
+  private long currentOriginalLineStartOffset = 0;
+  private long currentOriginalLineEndOffset = 0;
+  private final IntArrayList originalLineStartOffsets = new IntArrayList();
+  private final IntArrayList originalLineEndOffsets = new IntArrayList();
+  private long lastValidOffset = 0;
+
+  public LineOffsetCounter() {
+    originalLineStartOffsets.add(0);
+  }
+
+  @Override
+  public void handleAll(char c) {
+    currentOriginalLineStartOffset++;
+  }
+
+  @Override
+  public void handleIgnoreEoL(char c) {
+    currentOriginalLineEndOffset++;
+  }
+
+  @Override
+  public void newLine() {
+    if (currentOriginalLineStartOffset > Integer.MAX_VALUE) {
+      throw new IllegalStateException("File is too big: " + currentOriginalLineStartOffset);
+    }
+    originalLineStartOffsets.add((int) currentOriginalLineStartOffset);
+    originalLineEndOffsets.add((int) currentOriginalLineEndOffset);
+    currentOriginalLineEndOffset = currentOriginalLineStartOffset;
+  }
+
+  @Override
+  public void eof() {
+    originalLineEndOffsets.add((int) currentOriginalLineEndOffset);
+    lastValidOffset = currentOriginalLineStartOffset;
+  }
+
+  public int[] getOriginalLineStartOffsets() {
+    return originalLineStartOffsets.trimAndGet();
+  }
+
+  public int[] getOriginalLineEndOffsets() {
+    return originalLineEndOffsets.trimAndGet();
+  }
+
+  public int getLastValidOffset() {
+    if (lastValidOffset > Integer.MAX_VALUE) {
+      throw new IllegalStateException("File is too big: " + lastValidOffset);
+    }
+    return (int) lastValidOffset;
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/package-info.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/charhandler/package-info.java
new file mode 100644 (file)
index 0000000..4e5375f
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.scanner.fs.charhandler;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/package-info.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/package-info.java
new file mode 100644 (file)
index 0000000..e971997
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.scanner.fs;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/AbsolutePathPredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/AbsolutePathPredicate.java
new file mode 100644 (file)
index 0000000..c55099a
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collections;
+import org.sonar.api.batch.fs.FileSystem.Index;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.scan.filesystem.PathResolver;
+import org.sonar.api.utils.PathUtils;
+
+/**
+ * @since 4.2
+ */
+class AbsolutePathPredicate extends AbstractFilePredicate {
+
+  private final String path;
+  private final Path baseDir;
+
+  AbsolutePathPredicate(String path, Path baseDir) {
+    this.baseDir = baseDir;
+    this.path = PathUtils.sanitize(path);
+  }
+
+  @Override
+  public boolean apply(InputFile f) {
+    return path.equals(f.absolutePath());
+  }
+
+  @Override
+  public Iterable<InputFile> get(Index index) {
+    String relative = PathUtils.sanitize(new PathResolver().relativePath(baseDir.toFile(), new File(path)));
+    if (relative == null) {
+      return Collections.emptyList();
+    }
+    InputFile f = index.inputFile(relative);
+    return f != null ? Arrays.asList(f) : Collections.<InputFile>emptyList();
+  }
+
+  @Override
+  public int priority() {
+    return USE_INDEX;
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/AbstractFilePredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/AbstractFilePredicate.java
new file mode 100644 (file)
index 0000000..4f69526
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import java.util.stream.StreamSupport;
+import org.sonar.api.batch.fs.FileSystem.Index;
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * Partial implementation of {@link OptimizedFilePredicate}.
+ * @since 5.1
+ */
+public abstract class AbstractFilePredicate implements OptimizedFilePredicate {
+
+  protected static final int DEFAULT_PRIORITY = 10;
+  protected static final int USE_INDEX = 20;
+
+  @Override
+  public Iterable<InputFile> filter(Iterable<InputFile> target) {
+    return () -> StreamSupport.stream(target.spliterator(), false)
+      .filter(this::apply)
+      .iterator();
+  }
+
+  @Override
+  public Iterable<InputFile> get(Index index) {
+    return filter(index.inputFiles());
+  }
+
+  @Override
+  public int priority() {
+    return DEFAULT_PRIORITY;
+  }
+
+  @Override
+  public final int compareTo(OptimizedFilePredicate o) {
+    return o.priority() - priority();
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/AndPredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/AndPredicate.java
new file mode 100644 (file)
index 0000000..0249642
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.FileSystem.Index;
+import org.sonar.api.batch.fs.InputFile;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * @since 4.2
+ */
+class AndPredicate extends AbstractFilePredicate implements OperatorPredicate {
+
+  private final List<OptimizedFilePredicate> predicates = new ArrayList<>();
+
+  private AndPredicate() {
+  }
+
+  public static FilePredicate create(Collection<FilePredicate> predicates) {
+    if (predicates.isEmpty()) {
+      return TruePredicate.TRUE;
+    }
+    AndPredicate result = new AndPredicate();
+    for (FilePredicate filePredicate : predicates) {
+      if (filePredicate == TruePredicate.TRUE) {
+        continue;
+      } else if (filePredicate == FalsePredicate.FALSE) {
+        return FalsePredicate.FALSE;
+      } else if (filePredicate instanceof AndPredicate) {
+        result.predicates.addAll(((AndPredicate) filePredicate).predicates);
+      } else {
+        result.predicates.add(OptimizedFilePredicateAdapter.create(filePredicate));
+      }
+    }
+    Collections.sort(result.predicates);
+    return result;
+  }
+
+  @Override
+  public boolean apply(InputFile f) {
+    for (OptimizedFilePredicate predicate : predicates) {
+      if (!predicate.apply(f)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public Iterable<InputFile> filter(Iterable<InputFile> target) {
+    Iterable<InputFile> result = target;
+    for (OptimizedFilePredicate predicate : predicates) {
+      result = predicate.filter(result);
+    }
+    return result;
+  }
+
+  @Override
+  public Iterable<InputFile> get(Index index) {
+    if (predicates.isEmpty()) {
+      return index.inputFiles();
+    }
+    // Optimization, use get on first predicate then filter with next predicates
+    Iterable<InputFile> result = predicates.get(0).get(index);
+    for (int i = 1; i < predicates.size(); i++) {
+      result = predicates.get(i).filter(result);
+    }
+    return result;
+  }
+
+  Collection<OptimizedFilePredicate> predicates() {
+    return predicates;
+  }
+
+  @Override
+  public List<FilePredicate> operands() {
+    return predicates.stream().map(p -> (FilePredicate) p).collect(toList());
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/DefaultFilePredicates.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/DefaultFilePredicates.java
new file mode 100644 (file)
index 0000000..60bb37e
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import java.io.File;
+import java.net.URI;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.FilePredicates;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputFile.Status;
+import org.sonar.scanner.fs.PathPattern;
+
+/**
+ * Factory of {@link FilePredicate}
+ *
+ * @since 4.2
+ */
+public class DefaultFilePredicates implements FilePredicates {
+
+  private final Path baseDir;
+
+  /**
+   * Client code should use {@link org.sonar.api.batch.fs.FileSystem#predicates()} to get an instance
+   */
+  public DefaultFilePredicates(Path baseDir) {
+    this.baseDir = baseDir;
+  }
+
+  /**
+   * Returns a predicate that always evaluates to true
+   */
+  @Override
+  public FilePredicate all() {
+    return TruePredicate.TRUE;
+  }
+
+  /**
+   * Returns a predicate that always evaluates to false
+   */
+  @Override
+  public FilePredicate none() {
+    return FalsePredicate.FALSE;
+  }
+
+  @Override
+  public FilePredicate hasAbsolutePath(String s) {
+    return new AbsolutePathPredicate(s, baseDir);
+  }
+
+  /**
+   * non-normalized path and Windows-style path are supported
+   */
+  @Override
+  public FilePredicate hasRelativePath(String s) {
+    return new RelativePathPredicate(s);
+  }
+
+  @Override
+  public FilePredicate hasFilename(String s) {
+    return new FilenamePredicate(s);
+  }
+
+  @Override
+  public FilePredicate hasExtension(String s) {
+    return new FileExtensionPredicate(s);
+  }
+
+  @Override
+  public FilePredicate hasURI(URI uri) {
+    return new URIPredicate(uri, baseDir);
+  }
+
+  @Override
+  public FilePredicate matchesPathPattern(String inclusionPattern) {
+    return new PathPatternPredicate(PathPattern.create(inclusionPattern));
+  }
+
+  @Override
+  public FilePredicate matchesPathPatterns(String[] inclusionPatterns) {
+    if (inclusionPatterns.length == 0) {
+      return TruePredicate.TRUE;
+    }
+    FilePredicate[] predicates = new FilePredicate[inclusionPatterns.length];
+    for (int i = 0; i < inclusionPatterns.length; i++) {
+      predicates[i] = new PathPatternPredicate(PathPattern.create(inclusionPatterns[i]));
+    }
+    return or(predicates);
+  }
+
+  @Override
+  public FilePredicate doesNotMatchPathPattern(String exclusionPattern) {
+    return not(matchesPathPattern(exclusionPattern));
+  }
+
+  @Override
+  public FilePredicate doesNotMatchPathPatterns(String[] exclusionPatterns) {
+    if (exclusionPatterns.length == 0) {
+      return TruePredicate.TRUE;
+    }
+    return not(matchesPathPatterns(exclusionPatterns));
+  }
+
+  @Override
+  public FilePredicate hasPath(String s) {
+    File file = new File(s);
+    if (file.isAbsolute()) {
+      return hasAbsolutePath(s);
+    }
+    return hasRelativePath(s);
+  }
+
+  @Override
+  public FilePredicate is(File ioFile) {
+    if (ioFile.isAbsolute()) {
+      return hasAbsolutePath(ioFile.getAbsolutePath());
+    }
+    return hasRelativePath(ioFile.getPath());
+  }
+
+  @Override
+  public FilePredicate hasLanguage(String language) {
+    return new LanguagePredicate(language);
+  }
+
+  @Override
+  public FilePredicate hasLanguages(Collection<String> languages) {
+    List<FilePredicate> list = new ArrayList<>();
+    for (String language : languages) {
+      list.add(hasLanguage(language));
+    }
+    return or(list);
+  }
+
+  @Override
+  public FilePredicate hasLanguages(String... languages) {
+    List<FilePredicate> list = new ArrayList<>();
+    for (String language : languages) {
+      list.add(hasLanguage(language));
+    }
+    return or(list);
+  }
+
+  @Override
+  public FilePredicate hasType(InputFile.Type type) {
+    return new TypePredicate(type);
+  }
+
+  @Override
+  public FilePredicate not(FilePredicate p) {
+    return new NotPredicate(p);
+  }
+
+  @Override
+  public FilePredicate or(Collection<FilePredicate> or) {
+    return OrPredicate.create(or);
+  }
+
+  @Override
+  public FilePredicate or(FilePredicate... or) {
+    return OrPredicate.create(Arrays.asList(or));
+  }
+
+  @Override
+  public FilePredicate or(FilePredicate first, FilePredicate second) {
+    return OrPredicate.create(Arrays.asList(first, second));
+  }
+
+  @Override
+  public FilePredicate and(Collection<FilePredicate> and) {
+    return AndPredicate.create(and);
+  }
+
+  @Override
+  public FilePredicate and(FilePredicate... and) {
+    return AndPredicate.create(Arrays.asList(and));
+  }
+
+  @Override
+  public FilePredicate and(FilePredicate first, FilePredicate second) {
+    return AndPredicate.create(Arrays.asList(first, second));
+  }
+
+  @Override
+  public FilePredicate hasStatus(Status status) {
+    return new StatusPredicate(status);
+  }
+
+  @Override
+  public FilePredicate hasAnyStatus() {
+    return new StatusPredicate(null);
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/FalsePredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/FalsePredicate.java
new file mode 100644 (file)
index 0000000..e76bb84
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import java.util.Collections;
+import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.FileSystem.Index;
+import org.sonar.api.batch.fs.InputFile;
+
+class FalsePredicate extends AbstractFilePredicate {
+
+  static final FilePredicate FALSE = new FalsePredicate();
+
+  @Override
+  public boolean apply(InputFile inputFile) {
+    return false;
+  }
+
+  @Override
+  public Iterable<InputFile> filter(Iterable<InputFile> target) {
+    return Collections.emptyList();
+  }
+
+  @Override
+  public Iterable<InputFile> get(Index index) {
+    return Collections.emptyList();
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/FileExtensionPredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/FileExtensionPredicate.java
new file mode 100644 (file)
index 0000000..46f257f
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import java.util.Locale;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * @since 6.3
+ */
+public class FileExtensionPredicate extends AbstractFilePredicate {
+
+  private final String extension;
+
+  public FileExtensionPredicate(String extension) {
+    this.extension = lowercase(extension);
+  }
+
+  @Override
+  public boolean apply(InputFile inputFile) {
+    return extension.equals(getExtension(inputFile));
+  }
+
+  @Override
+  public Iterable<InputFile> get(FileSystem.Index index) {
+    return index.getFilesByExtension(extension);
+  }
+
+  public static String getExtension(InputFile inputFile) {
+    return getExtension(inputFile.filename());
+  }
+
+  static String getExtension(String name) {
+    int index = name.lastIndexOf('.');
+    if (index < 0) {
+      return "";
+    }
+    return lowercase(name.substring(index + 1));
+  }
+
+  private static String lowercase(String extension) {
+    return extension.toLowerCase(Locale.ENGLISH);
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/FilenamePredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/FilenamePredicate.java
new file mode 100644 (file)
index 0000000..a99d931
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * @since 6.3
+ */
+public class FilenamePredicate extends AbstractFilePredicate {
+  private final String filename;
+
+  public FilenamePredicate(String filename) {
+    this.filename = filename;
+  }
+
+  @Override
+  public boolean apply(InputFile inputFile) {
+    return filename.equals(inputFile.filename());
+  }
+
+  @Override
+  public Iterable<InputFile> get(FileSystem.Index index) {
+    return index.getFilesByName(filename);
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/LanguagePredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/LanguagePredicate.java
new file mode 100644 (file)
index 0000000..5492e22
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * @since 4.2
+ */
+class LanguagePredicate extends AbstractFilePredicate {
+  private final String language;
+
+  LanguagePredicate(String language) {
+    this.language = language;
+  }
+
+  @Override
+  public boolean apply(InputFile f) {
+    return language.equals(f.language());
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/NotPredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/NotPredicate.java
new file mode 100644 (file)
index 0000000..5bc68ad
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import java.util.Arrays;
+import java.util.List;
+import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.scanner.fs.predicates.AbstractFilePredicate;
+import org.sonar.scanner.fs.predicates.OperatorPredicate;
+
+/**
+ * @since 4.2
+ */
+class NotPredicate extends AbstractFilePredicate implements OperatorPredicate {
+
+  private final FilePredicate predicate;
+
+  NotPredicate(FilePredicate predicate) {
+    this.predicate = predicate;
+  }
+
+  @Override
+  public boolean apply(InputFile f) {
+    return !predicate.apply(f);
+  }
+
+  @Override
+  public List<FilePredicate> operands() {
+    return Arrays.asList(predicate);
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/OperatorPredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/OperatorPredicate.java
new file mode 100644 (file)
index 0000000..c449309
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import java.util.List;
+import org.sonar.api.batch.fs.FilePredicate;
+
+/**
+ * A predicate that associate other predicates
+ */
+public interface OperatorPredicate extends FilePredicate {
+
+  List<FilePredicate> operands();
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/OptimizedFilePredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/OptimizedFilePredicate.java
new file mode 100644 (file)
index 0000000..9b5216d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * Optimized version of FilePredicate allowing to speed up query by looking at InputFile by index.
+ */
+public interface OptimizedFilePredicate extends FilePredicate, Comparable<OptimizedFilePredicate> {
+
+  /**
+   * Filter provided files to keep only the ones that are valid for this predicate
+   */
+  Iterable<InputFile> filter(Iterable<InputFile> inputFiles);
+
+  /**
+   * Get all files that are valid for this predicate.
+   */
+  Iterable<InputFile> get(FileSystem.Index index);
+
+  /**
+   * For optimization. FilePredicates will be applied in priority order. For example when doing
+   * p.and(p1, p2, p3) then p1, p2 and p3 will be applied according to their priority value. Higher priority value
+   * are applied first.
+   * Assign a high priority when the predicate will likely highly reduce the set of InputFiles to filter. Also
+   * {@link RelativePathPredicate} and AbsolutePathPredicate have a high priority since they are using cache index.
+   */
+  int priority();
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/OptimizedFilePredicateAdapter.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/OptimizedFilePredicateAdapter.java
new file mode 100644 (file)
index 0000000..ba46aff
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.InputFile;
+
+public class OptimizedFilePredicateAdapter extends AbstractFilePredicate {
+
+  private FilePredicate unoptimizedPredicate;
+
+  private OptimizedFilePredicateAdapter(FilePredicate unoptimizedPredicate) {
+    this.unoptimizedPredicate = unoptimizedPredicate;
+  }
+
+  @Override
+  public boolean apply(InputFile inputFile) {
+    return unoptimizedPredicate.apply(inputFile);
+  }
+
+  public static OptimizedFilePredicate create(FilePredicate predicate) {
+    if (predicate instanceof OptimizedFilePredicate) {
+      return (OptimizedFilePredicate) predicate;
+    } else {
+      return new OptimizedFilePredicateAdapter(predicate);
+    }
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/OrPredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/OrPredicate.java
new file mode 100644 (file)
index 0000000..2690a12
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * @since 4.2
+ */
+class OrPredicate extends AbstractFilePredicate implements OperatorPredicate {
+
+  private final List<FilePredicate> predicates = new ArrayList<>();
+
+  private OrPredicate() {
+  }
+
+  public static FilePredicate create(Collection<FilePredicate> predicates) {
+    if (predicates.isEmpty()) {
+      return TruePredicate.TRUE;
+    }
+    OrPredicate result = new OrPredicate();
+    for (FilePredicate filePredicate : predicates) {
+      if (filePredicate == TruePredicate.TRUE) {
+        return TruePredicate.TRUE;
+      } else if (filePredicate == FalsePredicate.FALSE) {
+        continue;
+      } else if (filePredicate instanceof OrPredicate) {
+        result.predicates.addAll(((OrPredicate) filePredicate).predicates);
+      } else {
+        result.predicates.add(filePredicate);
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public boolean apply(InputFile f) {
+    for (FilePredicate predicate : predicates) {
+      if (predicate.apply(f)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  Collection<FilePredicate> predicates() {
+    return predicates;
+  }
+
+  @Override
+  public List<FilePredicate> operands() {
+    return predicates;
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/PathPatternPredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/PathPatternPredicate.java
new file mode 100644 (file)
index 0000000..0fdabcb
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import java.nio.file.Paths;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.scanner.fs.PathPattern;
+
+/**
+ * @since 4.2
+ */
+class PathPatternPredicate extends AbstractFilePredicate {
+
+  private final PathPattern pattern;
+
+  PathPatternPredicate(PathPattern pattern) {
+    this.pattern = pattern;
+  }
+
+  @Override
+  public boolean apply(InputFile f) {
+    return pattern.match(f.path(), Paths.get(f.relativePath()));
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/RelativePathPredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/RelativePathPredicate.java
new file mode 100644 (file)
index 0000000..d79221e
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import java.util.Collections;
+import javax.annotation.Nullable;
+import org.sonar.api.batch.fs.FileSystem.Index;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.utils.PathUtils;
+
+/**
+ * @since 4.2
+ */
+public class RelativePathPredicate extends AbstractFilePredicate {
+
+  @Nullable
+  private final String path;
+
+  RelativePathPredicate(String path) {
+    this.path = PathUtils.sanitize(path);
+  }
+
+  public String path() {
+    return path;
+  }
+
+  @Override
+  public boolean apply(InputFile f) {
+    if (path == null) {
+      return false;
+    }
+
+    return path.equals(f.relativePath());
+  }
+
+  @Override
+  public Iterable<InputFile> get(Index index) {
+    if (path != null) {
+      InputFile f = index.inputFile(this.path);
+      if (f != null) {
+        return Collections.singletonList(f);
+      }
+    }
+    return Collections.emptyList();
+  }
+
+  @Override
+  public int priority() {
+    return USE_INDEX;
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/StatusPredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/StatusPredicate.java
new file mode 100644 (file)
index 0000000..fb88b96
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import javax.annotation.Nullable;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.scanner.fs.predicates.AbstractFilePredicate;
+
+/**
+ * @deprecated since 7.8
+ */
+@Deprecated
+public class StatusPredicate extends AbstractFilePredicate {
+
+  private final InputFile.Status status;
+
+  StatusPredicate(@Nullable InputFile.Status status) {
+    this.status = status;
+  }
+
+  @Override
+  public boolean apply(InputFile f) {
+    return status == null || status == f.status();
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/TruePredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/TruePredicate.java
new file mode 100644 (file)
index 0000000..562515c
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.FileSystem.Index;
+import org.sonar.api.batch.fs.InputFile;
+
+class TruePredicate extends AbstractFilePredicate {
+
+  static final FilePredicate TRUE = new TruePredicate();
+
+  @Override
+  public boolean apply(InputFile inputFile) {
+    return true;
+  }
+
+  @Override
+  public Iterable<InputFile> get(Index index) {
+    return index.inputFiles();
+  }
+
+  @Override
+  public Iterable<InputFile> filter(Iterable<InputFile> target) {
+    return target;
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/TypePredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/TypePredicate.java
new file mode 100644 (file)
index 0000000..3cddc59
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * @since 4.2
+ */
+class TypePredicate extends AbstractFilePredicate {
+
+  private final InputFile.Type type;
+
+  TypePredicate(InputFile.Type type) {
+    this.type = type;
+  }
+
+  @Override
+  public boolean apply(InputFile f) {
+    return type == f.type();
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/URIPredicate.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/fs/predicates/URIPredicate.java
new file mode 100644 (file)
index 0000000..9e25267
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.fs.predicates;
+
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Optional;
+import org.sonar.api.batch.fs.FileSystem.Index;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.scan.filesystem.PathResolver;
+
+/**
+ * @since 6.6
+ */
+class URIPredicate extends AbstractFilePredicate {
+
+  private final URI uri;
+  private final Path baseDir;
+
+  URIPredicate(URI uri, Path baseDir) {
+    this.baseDir = baseDir;
+    this.uri = uri;
+  }
+
+  @Override
+  public boolean apply(InputFile f) {
+    return uri.equals(f.uri());
+  }
+
+  @Override
+  public Iterable<InputFile> get(Index index) {
+    Path path = Paths.get(uri);
+    Optional<String> relative = PathResolver.relativize(baseDir, path);
+    if (!relative.isPresent()) {
+      return Collections.emptyList();
+    }
+    InputFile f = index.inputFile(relative.get());
+    return f != null ? Arrays.asList(f) : Collections.<InputFile>emptyList();
+  }
+
+  @Override
+  public int priority() {
+    return USE_INDEX;
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/AbstractDefaultIssue.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/AbstractDefaultIssue.java
new file mode 100644 (file)
index 0000000..113d445
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.issue;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import javax.annotation.Nullable;
+import org.sonar.api.batch.fs.InputComponent;
+import org.sonar.api.batch.fs.internal.DefaultInputDir;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.DefaultInputProject;
+import org.sonar.api.batch.sensor.internal.DefaultStorable;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
+import org.sonar.api.batch.sensor.issue.Issue.Flow;
+import org.sonar.api.batch.sensor.issue.IssueLocation;
+import org.sonar.api.batch.sensor.issue.NewIssueLocation;
+import org.sonar.api.utils.PathUtils;
+
+import static java.util.Collections.unmodifiableList;
+import static java.util.stream.Collectors.toList;
+import static org.sonar.api.utils.Preconditions.checkArgument;
+import static org.sonar.api.utils.Preconditions.checkState;
+
+public abstract class AbstractDefaultIssue<T extends AbstractDefaultIssue> extends DefaultStorable {
+  protected IssueLocation primaryLocation;
+  protected List<List<IssueLocation>> flows = new ArrayList<>();
+  protected DefaultInputProject project;
+
+  protected AbstractDefaultIssue(DefaultInputProject project) {
+    this(project, null);
+  }
+
+  public AbstractDefaultIssue(DefaultInputProject project, @Nullable SensorStorage storage) {
+    super(storage);
+    this.project = project;
+  }
+
+  public IssueLocation primaryLocation() {
+    return primaryLocation;
+  }
+
+  public List<Flow> flows() {
+    return this.flows.stream()
+      .<Flow>map(l -> () -> unmodifiableList(new ArrayList<>(l)))
+      .collect(toList());
+  }
+
+  public NewIssueLocation newLocation() {
+    return new DefaultIssueLocation();
+  }
+
+  public T at(NewIssueLocation primaryLocation) {
+    checkArgument(primaryLocation != null, "Cannot use a location that is null");
+    checkState(this.primaryLocation == null, "at() already called");
+    this.primaryLocation = rewriteLocation((DefaultIssueLocation) primaryLocation);
+    checkArgument(this.primaryLocation.inputComponent() != null, "Cannot use a location with no input component");
+    return (T) this;
+  }
+
+  public T addLocation(NewIssueLocation secondaryLocation) {
+    flows.add(Collections.singletonList(rewriteLocation((DefaultIssueLocation) secondaryLocation)));
+    return (T) this;
+  }
+
+  public T addFlow(Iterable<NewIssueLocation> locations) {
+    List<IssueLocation> flowAsList = new ArrayList<>();
+    for (NewIssueLocation issueLocation : locations) {
+      flowAsList.add(rewriteLocation((DefaultIssueLocation) issueLocation));
+    }
+    flows.add(flowAsList);
+    return (T) this;
+  }
+
+  private DefaultIssueLocation rewriteLocation(DefaultIssueLocation location) {
+    InputComponent component = location.inputComponent();
+    Optional<Path> dirOrModulePath = Optional.empty();
+
+    if (component instanceof DefaultInputDir) {
+      DefaultInputDir dirComponent = (DefaultInputDir) component;
+      dirOrModulePath = Optional.of(project.getBaseDir().relativize(dirComponent.path()));
+    } else if (component instanceof DefaultInputModule && !Objects.equals(project.key(), component.key())) {
+      DefaultInputModule moduleComponent = (DefaultInputModule) component;
+      dirOrModulePath = Optional.of(project.getBaseDir().relativize(moduleComponent.getBaseDir()));
+    }
+
+    if (dirOrModulePath.isPresent()) {
+      String path = PathUtils.sanitize(dirOrModulePath.get().toString());
+      DefaultIssueLocation fixedLocation = new DefaultIssueLocation();
+      fixedLocation.on(project);
+      StringBuilder fullMessage = new StringBuilder();
+      if (path != null && !path.isEmpty()) {
+        fullMessage.append("[").append(path).append("] ");
+      }
+      fullMessage.append(location.message());
+      fixedLocation.message(fullMessage.toString());
+      return fixedLocation;
+    } else {
+      return location;
+    }
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssue.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssue.java
new file mode 100644 (file)
index 0000000..bf36bf5
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.issue;
+
+import javax.annotation.Nullable;
+import org.sonar.api.batch.fs.internal.DefaultInputProject;
+import org.sonar.api.batch.rule.Severity;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
+import org.sonar.api.batch.sensor.issue.Issue;
+import org.sonar.api.batch.sensor.issue.IssueLocation;
+import org.sonar.api.batch.sensor.issue.NewIssue;
+import org.sonar.api.rule.RuleKey;
+
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+import static org.sonar.api.utils.Preconditions.checkArgument;
+import static org.sonar.api.utils.Preconditions.checkState;
+
+public class DefaultIssue extends AbstractDefaultIssue<DefaultIssue> implements Issue, NewIssue {
+  private RuleKey ruleKey;
+  private Double gap;
+  private Severity overriddenSeverity;
+
+  public DefaultIssue(DefaultInputProject project) {
+    this(project, null);
+  }
+
+  public DefaultIssue(DefaultInputProject project, @Nullable SensorStorage storage) {
+    super(project, storage);
+  }
+
+  public DefaultIssue forRule(RuleKey ruleKey) {
+    this.ruleKey = ruleKey;
+    return this;
+  }
+
+  public RuleKey ruleKey() {
+    return this.ruleKey;
+  }
+
+  @Override
+  public DefaultIssue gap(@Nullable Double gap) {
+    checkArgument(gap == null || gap >= 0, format("Gap must be greater than or equal 0 (got %s)", gap));
+    this.gap = gap;
+    return this;
+  }
+
+  @Override
+  public DefaultIssue overrideSeverity(@Nullable Severity severity) {
+    this.overriddenSeverity = severity;
+    return this;
+  }
+
+  @Override
+  public Severity overriddenSeverity() {
+    return this.overriddenSeverity;
+  }
+
+  @Override
+  public Double gap() {
+    return this.gap;
+  }
+
+  @Override
+  public IssueLocation primaryLocation() {
+    return primaryLocation;
+  }
+
+  @Override
+  public void doSave() {
+    requireNonNull(this.ruleKey, "ruleKey is mandatory on issue");
+    checkState(primaryLocation != null, "Primary location is mandatory on every issue");
+    storage.store(this);
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssueLocation.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssueLocation.java
new file mode 100644 (file)
index 0000000..95147bc
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.issue;
+
+import javax.annotation.Nullable;
+import org.sonar.api.batch.fs.InputComponent;
+import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.issue.IssueLocation;
+import org.sonar.api.batch.sensor.issue.NewIssueLocation;
+
+import static java.util.Objects.requireNonNull;
+import static org.apache.commons.lang.StringUtils.abbreviate;
+import static org.apache.commons.lang.StringUtils.trim;
+import static org.sonar.api.utils.Preconditions.checkArgument;
+import static org.sonar.api.utils.Preconditions.checkState;
+
+public class DefaultIssueLocation implements NewIssueLocation, IssueLocation {
+
+  private InputComponent component;
+  private TextRange textRange;
+  private String message;
+
+  @Override
+  public DefaultIssueLocation on(InputComponent component) {
+    checkArgument(component != null, "Component can't be null");
+    checkState(this.component == null, "on() already called");
+    this.component = component;
+    return this;
+  }
+
+  @Override
+  public DefaultIssueLocation at(TextRange location) {
+    checkState(this.component != null, "at() should be called after on()");
+    checkState(this.component.isFile(), "at() should be called only for an InputFile.");
+    DefaultInputFile file = (DefaultInputFile) this.component;
+    file.validate(location);
+    this.textRange = location;
+    return this;
+  }
+
+  @Override
+  public DefaultIssueLocation message(String message) {
+    requireNonNull(message, "Message can't be null");
+    if (message.contains("\u0000")) {
+      throw new IllegalArgumentException(unsupportedCharacterError(message, component));
+    }
+    this.message = abbreviate(trim(message), MESSAGE_MAX_SIZE);
+    return this;
+  }
+
+  private static String unsupportedCharacterError(String message, @Nullable InputComponent component) {
+    String error = "Character \\u0000 is not supported in issue message '" + message + "'";
+    if (component != null) {
+      error += ", on component: " + component.toString();
+    }
+    return error;
+  }
+
+  @Override
+  public InputComponent inputComponent() {
+    return this.component;
+  }
+
+  @Override
+  public TextRange textRange() {
+    return textRange;
+  }
+
+  @Override
+  public String message() {
+    return this.message;
+  }
+
+}
index 6e639385b097c4a051e9ba71aded135938cf2969..858a4dd5d2f06c7792dc5812afe8609bcb0c4acf 100644 (file)
@@ -24,10 +24,10 @@ import java.util.List;
 import javax.annotation.CheckForNull;
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.fs.internal.charhandler.CharHandler;
 import org.sonar.api.notifications.AnalysisWarnings;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
+import org.sonar.scanner.fs.charhandler.CharHandler;
 import org.sonar.scanner.issue.ignore.IgnoreIssuesFilter;
 import org.sonar.scanner.issue.ignore.pattern.BlockIssuePattern;
 import org.sonar.scanner.issue.ignore.pattern.IssueExclusionPatternInitializer;
index a08ba55a5939b89a745ab40a64ca2c1c2da820e7..19079dbac4f42dc24acfbb94d8d0674d64d8a1ae 100644 (file)
@@ -26,9 +26,9 @@ import java.util.Set;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.fs.internal.charhandler.CharHandler;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
+import org.sonar.scanner.fs.charhandler.CharHandler;
 import org.sonar.scanner.issue.ignore.pattern.LineRange;
 import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader.DoubleRegexpMatcher;
 
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultRules.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultRules.java
new file mode 100644 (file)
index 0000000..def0012
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.rule.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.batch.rule.Rule;
+import org.sonar.api.batch.rule.Rules;
+import org.sonar.api.rule.RuleKey;
+
+@Immutable
+class DefaultRules implements Rules {
+  private final Map<String, List<Rule>> rulesByRepository;
+  private final Map<String, Map<String, List<Rule>>> rulesByRepositoryAndInternalKey;
+  private final Map<RuleKey, Rule> rulesByRuleKey;
+
+  DefaultRules(Collection<NewRule> newRules) {
+    Map<String, List<Rule>> rulesByRepositoryBuilder = new HashMap<>();
+    Map<String, Map<String, List<Rule>>> rulesByRepositoryAndInternalKeyBuilder = new HashMap<>();
+    Map<RuleKey, Rule> rulesByRuleKeyBuilder = new HashMap<>();
+
+    for (NewRule newRule : newRules) {
+      DefaultRule r = new DefaultRule(newRule);
+      rulesByRuleKeyBuilder.put(r.key(), r);
+      rulesByRepositoryBuilder.computeIfAbsent(r.key().repository(), x -> new ArrayList<>()).add(r);
+      addToTable(rulesByRepositoryAndInternalKeyBuilder, r);
+    }
+
+    rulesByRuleKey = Collections.unmodifiableMap(rulesByRuleKeyBuilder);
+    rulesByRepository = Collections.unmodifiableMap(rulesByRepositoryBuilder);
+    rulesByRepositoryAndInternalKey = Collections.unmodifiableMap(rulesByRepositoryAndInternalKeyBuilder);
+  }
+
+  private static void addToTable(Map<String, Map<String, List<Rule>>> rulesByRepositoryAndInternalKeyBuilder, DefaultRule r) {
+    if (r.internalKey() == null) {
+      return;
+    }
+
+    rulesByRepositoryAndInternalKeyBuilder
+      .computeIfAbsent(r.key().repository(), x -> new HashMap<>())
+      .computeIfAbsent(r.internalKey(), x -> new ArrayList<>())
+      .add(r);
+  }
+
+  @Override
+  public Rule find(RuleKey ruleKey) {
+    return rulesByRuleKey.get(ruleKey);
+  }
+
+  @Override
+  public Collection<Rule> findAll() {
+    return rulesByRepository.values().stream().flatMap(List::stream).collect(Collectors.toList());
+  }
+
+  @Override
+  public Collection<Rule> findByRepository(String repository) {
+    return rulesByRepository.getOrDefault(repository, Collections.emptyList());
+  }
+
+  @Override
+  public Collection<Rule> findByInternalKey(String repository, String internalKey) {
+    return rulesByRepositoryAndInternalKey
+      .getOrDefault(repository, Collections.emptyMap())
+      .getOrDefault(internalKey, Collections.emptyList());
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/RulesBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/RulesBuilder.java
new file mode 100644 (file)
index 0000000..f7ebe36
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.rule.internal;
+
+import org.sonar.api.batch.rule.Rules;
+import org.sonar.api.rule.RuleKey;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * For unit testing and internal use only.
+ *
+ * @since 4.2
+ */
+
+public class RulesBuilder {
+
+  private final Map<RuleKey, NewRule> map = new HashMap<>();
+
+  public NewRule add(RuleKey key) {
+    if (map.containsKey(key)) {
+      throw new IllegalStateException(String.format("Rule '%s' already exists", key));
+    }
+    NewRule newRule = new NewRule(key);
+    map.put(key, newRule);
+    return newRule;
+  }
+
+  public Rules build() {
+    return new DefaultRules(map.values());
+  }
+}
index 71ab90450b8222ca666c7f66b98de366bbdb6516..411625c7546b8abf4c958a015965678c214e4f71 100644 (file)
@@ -24,7 +24,6 @@ import javax.annotation.Nullable;
 import org.sonar.api.SonarEdition;
 import org.sonar.api.SonarRuntime;
 import org.sonar.api.batch.fs.internal.DefaultInputModule;
-import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
 import org.sonar.api.batch.fs.internal.SensorStrategy;
 import org.sonar.api.batch.rule.CheckFactory;
@@ -67,6 +66,7 @@ import org.sonar.scanner.cpd.CpdSettings;
 import org.sonar.scanner.cpd.index.SonarCpdBlockIndex;
 import org.sonar.scanner.deprecated.test.TestPlanBuilder;
 import org.sonar.scanner.deprecated.test.TestableBuilder;
+import org.sonar.scanner.fs.FileMetadata;
 import org.sonar.scanner.issue.IssueFilters;
 import org.sonar.scanner.issue.IssuePublisher;
 import org.sonar.scanner.issue.ignore.EnforceIssuesFilter;
index cdc9c74f49908a89c3fc7606b67ebe6b6cad903e..6c1bb65332ad6914db14c8264031ecbc657a7f3a 100644 (file)
@@ -20,7 +20,7 @@
 package org.sonar.scanner.scan.filesystem;
 
 import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.internal.AbstractFilePredicate;
+import org.sonar.scanner.fs.predicates.AbstractFilePredicate;
 
 /**
  * Additional {@link org.sonar.api.batch.fs.FilePredicate}s that are
index dd13c11c1b3e10549ff2a08b688734f6bc076608..74e82cd853594abf9a9b2f43301c13e77706bc08 100644 (file)
@@ -20,8 +20,8 @@
 package org.sonar.scanner.scan.filesystem;
 
 import com.google.common.annotations.VisibleForTesting;
-import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.scanner.fs.DefaultFileSystem;
 
 public class DefaultModuleFileSystem extends DefaultFileSystem {
 
index f81ccc42432efc0e20d287eb0e8c3dff19339e5f..7e0dbc6f6af5dcb797e791c55abeae6cf1466d57 100644 (file)
@@ -20,8 +20,8 @@
 package org.sonar.scanner.scan.filesystem;
 
 import com.google.common.annotations.VisibleForTesting;
-import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputProject;
+import org.sonar.scanner.fs.DefaultFileSystem;
 
 public class DefaultProjectFileSystem extends DefaultFileSystem {
 
index e4cb0672f7a05d1e06ab185717fe2d28e7c0c667..cd9f0db73472696c1005640221e32d787b05c8b2 100644 (file)
@@ -35,10 +35,10 @@ import java.util.stream.Stream;
 import javax.annotation.CheckForNull;
 import org.sonar.api.batch.fs.InputComponent;
 import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.fs.internal.DefaultInputModule;
-import org.sonar.api.batch.fs.internal.FileExtensionPredicate;
+import org.sonar.scanner.fs.DefaultFileSystem;
+import org.sonar.scanner.fs.predicates.FileExtensionPredicate;
 import org.sonar.scanner.scan.branch.BranchConfiguration;
 
 /**
index cf7833e18fcf590a3bcb67ee2a3f8df7df5f7de9..80731033ac01fc3b9dffe79fb0a2ed5ceb829f25 100644 (file)
@@ -24,10 +24,10 @@ import java.io.InputStream;
 import java.nio.charset.Charset;
 import org.sonar.api.batch.fs.InputFile.Type;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.batch.fs.internal.Metadata;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
+import org.sonar.scanner.fs.FileMetadata;
 import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader;
 
 public class MetadataGenerator {
index c9d1d3c115ea2f04e2d543693b0d009597e07e4e..e6d52849c6393b7e899ca355c09a2c88a17638d9 100644 (file)
@@ -23,8 +23,8 @@ import java.util.SortedSet;
 import org.sonar.api.batch.ScannerSide;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.InputModule;
-import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.SensorStrategy;
+import org.sonar.scanner.fs.DefaultFileSystem;
 
 @ScannerSide
 public class ModuleInputComponentStore extends DefaultFileSystem.Cache {
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/InMemorySensorStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/InMemorySensorStorage.java
new file mode 100644 (file)
index 0000000..c0ec117
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.sensor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.sonar.api.batch.sensor.code.internal.DefaultSignificantCode;
+import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
+import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
+import org.sonar.api.batch.sensor.error.AnalysisError;
+import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
+import org.sonar.api.batch.sensor.issue.ExternalIssue;
+import org.sonar.api.batch.sensor.issue.Issue;
+import org.sonar.api.batch.sensor.issue.internal.DefaultExternalIssue;
+import org.sonar.api.batch.sensor.measure.Measure;
+import org.sonar.api.batch.sensor.rule.AdHocRule;
+import org.sonar.api.batch.sensor.rule.internal.DefaultAdHocRule;
+import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable;
+
+import static org.sonar.api.utils.Preconditions.checkArgument;
+
+class InMemorySensorStorage implements SensorStorage {
+
+  Map<String, Map<String, Measure>> measuresByComponentAndMetric = new HashMap<>();
+
+  Collection<Issue> allIssues = new ArrayList<>();
+  Collection<ExternalIssue> allExternalIssues = new ArrayList<>();
+  Collection<AdHocRule> allAdHocRules = new ArrayList<>();
+  Collection<AnalysisError> allAnalysisErrors = new ArrayList<>();
+
+  Map<String, DefaultHighlighting> highlightingByComponent = new HashMap<>();
+  Map<String, DefaultCpdTokens> cpdTokensByComponent = new HashMap<>();
+  Map<String, List<DefaultCoverage>> coverageByComponent = new HashMap<>();
+  Map<String, DefaultSymbolTable> symbolsPerComponent = new HashMap<>();
+  Map<String, String> contextProperties = new HashMap<>();
+  Map<String, DefaultSignificantCode> significantCodePerComponent = new HashMap<>();
+
+  @Override
+  public void store(Measure measure) {
+    // Emulate duplicate measure check
+    String componentKey = measure.inputComponent().key();
+    String metricKey = measure.metric().key();
+    if (measuresByComponentAndMetric.getOrDefault(componentKey, Collections.emptyMap()).containsKey(metricKey)) {
+      throw new IllegalStateException("Can not add the same measure twice");
+    }
+    measuresByComponentAndMetric.computeIfAbsent(componentKey, x -> new HashMap<>()).put(metricKey, measure);
+  }
+
+  @Override
+  public void store(Issue issue) {
+    allIssues.add(issue);
+  }
+
+  @Override
+  public void store(DefaultAdHocRule adHocRule) {
+    allAdHocRules.add(adHocRule);
+  }
+
+  @Override
+  public void store(DefaultHighlighting highlighting) {
+    String fileKey = highlighting.inputFile().key();
+    // Emulate duplicate storage check
+    if (highlightingByComponent.containsKey(fileKey)) {
+      throw new UnsupportedOperationException("Trying to save highlighting twice for the same file is not supported: " + highlighting.inputFile());
+    }
+    highlightingByComponent.put(fileKey, highlighting);
+  }
+
+  @Override
+  public void store(DefaultCoverage defaultCoverage) {
+    String fileKey = defaultCoverage.inputFile().key();
+    coverageByComponent.computeIfAbsent(fileKey, x -> new ArrayList<>()).add(defaultCoverage);
+  }
+
+  @Override
+  public void store(DefaultCpdTokens defaultCpdTokens) {
+    String fileKey = defaultCpdTokens.inputFile().key();
+    // Emulate duplicate storage check
+    if (cpdTokensByComponent.containsKey(fileKey)) {
+      throw new UnsupportedOperationException("Trying to save CPD tokens twice for the same file is not supported: " + defaultCpdTokens.inputFile());
+    }
+    cpdTokensByComponent.put(fileKey, defaultCpdTokens);
+  }
+
+  @Override
+  public void store(DefaultSymbolTable symbolTable) {
+    String fileKey = symbolTable.inputFile().key();
+    // Emulate duplicate storage check
+    if (symbolsPerComponent.containsKey(fileKey)) {
+      throw new UnsupportedOperationException("Trying to save symbol table twice for the same file is not supported: " + symbolTable.inputFile());
+    }
+    symbolsPerComponent.put(fileKey, symbolTable);
+  }
+
+  @Override
+  public void store(AnalysisError analysisError) {
+    allAnalysisErrors.add(analysisError);
+  }
+
+  @Override
+  public void storeProperty(String key, String value) {
+    checkArgument(key != null, "Key of context property must not be null");
+    checkArgument(value != null, "Value of context property must not be null");
+    contextProperties.put(key, value);
+  }
+
+  @Override
+  public void store(DefaultExternalIssue issue) {
+    allExternalIssues.add(issue);
+  }
+
+  @Override
+  public void store(DefaultSignificantCode significantCode) {
+    String fileKey = significantCode.inputFile().key();
+    // Emulate duplicate storage check
+    if (significantCodePerComponent.containsKey(fileKey)) {
+      throw new UnsupportedOperationException("Trying to save significant code information twice for the same file is not supported: " + significantCode.inputFile());
+    }
+    significantCodePerComponent.put(fileKey, significantCode);
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/SensorContextTester.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/SensorContextTester.java
new file mode 100644 (file)
index 0000000..6e81544
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.sensor;
+
+import java.io.File;
+import java.io.Serializable;
+import java.nio.charset.Charset;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Stream;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.SonarQubeSide;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputModule;
+import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.DefaultInputProject;
+import org.sonar.api.batch.fs.internal.DefaultTextPointer;
+import org.sonar.api.batch.rule.ActiveRules;
+import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.code.NewSignificantCode;
+import org.sonar.api.batch.sensor.code.internal.DefaultSignificantCode;
+import org.sonar.api.batch.sensor.coverage.NewCoverage;
+import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
+import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
+import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
+import org.sonar.api.batch.sensor.cpd.internal.TokensLine;
+import org.sonar.api.batch.sensor.error.AnalysisError;
+import org.sonar.api.batch.sensor.error.NewAnalysisError;
+import org.sonar.api.batch.sensor.error.internal.DefaultAnalysisError;
+import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
+import org.sonar.api.batch.sensor.highlighting.TypeOfText;
+import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
+import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule;
+import org.sonar.api.batch.sensor.issue.ExternalIssue;
+import org.sonar.api.batch.sensor.issue.Issue;
+import org.sonar.api.batch.sensor.issue.NewExternalIssue;
+import org.sonar.api.batch.sensor.issue.NewIssue;
+import org.sonar.api.batch.sensor.issue.internal.DefaultExternalIssue;
+import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
+import org.sonar.api.batch.sensor.measure.Measure;
+import org.sonar.api.batch.sensor.measure.NewMeasure;
+import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
+import org.sonar.api.batch.sensor.rule.AdHocRule;
+import org.sonar.api.batch.sensor.rule.NewAdHocRule;
+import org.sonar.api.batch.sensor.rule.internal.DefaultAdHocRule;
+import org.sonar.api.batch.sensor.symbol.NewSymbolTable;
+import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.config.Settings;
+import org.sonar.api.config.internal.ConfigurationBridge;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.internal.MetadataLoader;
+import org.sonar.api.internal.SonarRuntimeImpl;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.scanner.fs.InputProject;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.Version;
+import org.sonar.scanner.fs.DefaultFileSystem;
+
+import static java.util.Collections.unmodifiableMap;
+
+/**
+ * Utility class to help testing {@link Sensor}. This is not an API and method signature may evolve.
+ * <p>
+ * Usage: call {@link #create(File)} to create an "in memory" implementation of {@link SensorContext} with a filesystem initialized with provided baseDir.
+ * <p>
+ * You have to manually register inputFiles using:
+ * <pre>
+ *   sensorContextTester.fileSystem().add(new DefaultInputFile("myProjectKey", "src/Foo.java")
+ * .setLanguage("java")
+ * .initMetadata("public class Foo {\n}"));
+ * </pre>
+ * <p>
+ * Then pass it to your {@link Sensor}. You can then query elements provided by your sensor using methods {@link #allIssues()}, ...
+ */
+public class SensorContextTester implements SensorContext {
+
+  private Settings settings;
+  private DefaultFileSystem fs;
+  private ActiveRules activeRules;
+  private InMemorySensorStorage sensorStorage;
+  private DefaultInputProject project;
+  private DefaultInputModule module;
+  private SonarRuntime runtime;
+  private boolean cancelled;
+
+  private SensorContextTester(Path moduleBaseDir) {
+    this.settings = new MapSettings();
+    this.fs = new DefaultFileSystem(moduleBaseDir).setEncoding(Charset.defaultCharset());
+    this.activeRules = new ActiveRulesBuilder().build();
+    this.sensorStorage = new InMemorySensorStorage();
+    this.project = new DefaultInputProject(ProjectDefinition.create().setKey("projectKey").setBaseDir(moduleBaseDir.toFile()).setWorkDir(moduleBaseDir.resolve(".sonar").toFile()));
+    this.module = new DefaultInputModule(ProjectDefinition.create().setKey("projectKey").setBaseDir(moduleBaseDir.toFile()).setWorkDir(moduleBaseDir.resolve(".sonar").toFile()));
+    this.runtime = SonarRuntimeImpl.forSonarQube(MetadataLoader.loadVersion(System2.INSTANCE), SonarQubeSide.SCANNER, MetadataLoader.loadEdition(System2.INSTANCE));
+  }
+
+  public static SensorContextTester create(File moduleBaseDir) {
+    return new SensorContextTester(moduleBaseDir.toPath());
+  }
+
+  public static SensorContextTester create(Path moduleBaseDir) {
+    return new SensorContextTester(moduleBaseDir);
+  }
+
+  @Override
+  public Settings settings() {
+    return settings;
+  }
+
+  @Override
+  public Configuration config() {
+    return new ConfigurationBridge(settings);
+  }
+
+  public SensorContextTester setSettings(Settings settings) {
+    this.settings = settings;
+    return this;
+  }
+
+  @Override
+  public DefaultFileSystem fileSystem() {
+    return fs;
+  }
+
+  public SensorContextTester setFileSystem(DefaultFileSystem fs) {
+    this.fs = fs;
+    return this;
+  }
+
+  @Override
+  public ActiveRules activeRules() {
+    return activeRules;
+  }
+
+  public SensorContextTester setActiveRules(ActiveRules activeRules) {
+    this.activeRules = activeRules;
+    return this;
+  }
+
+  /**
+   * Default value is the version of this API at compilation time. You can override it
+   * using {@link #setRuntime(SonarRuntime)} to test your Sensor behaviour.
+   */
+  @Override
+  public Version getSonarQubeVersion() {
+    return runtime().getApiVersion();
+  }
+
+  /**
+   * @see #setRuntime(SonarRuntime) to override defaults (SonarQube scanner with version
+   * of this API as used at compilation time).
+   */
+  @Override
+  public SonarRuntime runtime() {
+    return runtime;
+  }
+
+  public SensorContextTester setRuntime(SonarRuntime runtime) {
+    this.runtime = runtime;
+    return this;
+  }
+
+  @Override
+  public boolean isCancelled() {
+    return cancelled;
+  }
+
+  public void setCancelled(boolean cancelled) {
+    this.cancelled = cancelled;
+  }
+
+  @Override
+  public InputModule module() {
+    return module;
+  }
+
+  @Override
+  public InputProject project() {
+    return project;
+  }
+
+  @Override
+  public <G extends Serializable> NewMeasure<G> newMeasure() {
+    return new DefaultMeasure<>(sensorStorage);
+  }
+
+  public Collection<Measure> measures(String componentKey) {
+    return sensorStorage.measuresByComponentAndMetric.getOrDefault(componentKey, Collections.emptyMap()).values();
+  }
+
+  public <G extends Serializable> Measure<G> measure(String componentKey, Metric<G> metric) {
+    return measure(componentKey, metric.key());
+  }
+
+  public <G extends Serializable> Measure<G> measure(String componentKey, String metricKey) {
+    return sensorStorage.measuresByComponentAndMetric.getOrDefault(componentKey, Collections.emptyMap()).get(metricKey);
+  }
+
+  @Override
+  public NewIssue newIssue() {
+    return new DefaultIssue(project, sensorStorage);
+  }
+
+  public Collection<Issue> allIssues() {
+    return sensorStorage.allIssues;
+  }
+
+  @Override
+  public NewExternalIssue newExternalIssue() {
+    return new DefaultExternalIssue(project, sensorStorage);
+  }
+
+  @Override
+  public NewAdHocRule newAdHocRule() {
+    return new DefaultAdHocRule(sensorStorage);
+  }
+
+  public Collection<ExternalIssue> allExternalIssues() {
+    return sensorStorage.allExternalIssues;
+  }
+
+  public Collection<AdHocRule> allAdHocRules() {
+    return sensorStorage.allAdHocRules;
+  }
+
+  public Collection<AnalysisError> allAnalysisErrors() {
+    return sensorStorage.allAnalysisErrors;
+  }
+
+  @CheckForNull
+  public Integer lineHits(String fileKey, int line) {
+    return sensorStorage.coverageByComponent.getOrDefault(fileKey, Collections.emptyList()).stream()
+      .map(c -> c.hitsByLine().get(line))
+      .flatMap(Stream::of)
+      .filter(Objects::nonNull)
+      .reduce(null, SensorContextTester::sumOrNull);
+  }
+
+  @CheckForNull
+  public static Integer sumOrNull(@Nullable Integer o1, @Nullable Integer o2) {
+    return o1 == null ? o2 : (o1 + o2);
+  }
+
+  @CheckForNull
+  public Integer conditions(String fileKey, int line) {
+    return sensorStorage.coverageByComponent.getOrDefault(fileKey, Collections.emptyList()).stream()
+      .map(c -> c.conditionsByLine().get(line))
+      .flatMap(Stream::of)
+      .filter(Objects::nonNull)
+      .reduce(null, SensorContextTester::maxOrNull);
+  }
+
+  @CheckForNull
+  public Integer coveredConditions(String fileKey, int line) {
+    return sensorStorage.coverageByComponent.getOrDefault(fileKey, Collections.emptyList()).stream()
+      .map(c -> c.coveredConditionsByLine().get(line))
+      .flatMap(Stream::of)
+      .filter(Objects::nonNull)
+      .reduce(null, SensorContextTester::maxOrNull);
+  }
+
+  @CheckForNull
+  public TextRange significantCodeTextRange(String fileKey, int line) {
+    if (sensorStorage.significantCodePerComponent.containsKey(fileKey)) {
+      return sensorStorage.significantCodePerComponent.get(fileKey)
+        .significantCodePerLine()
+        .get(line);
+    }
+    return null;
+
+  }
+
+  @CheckForNull
+  public static Integer maxOrNull(@Nullable Integer o1, @Nullable Integer o2) {
+    return o1 == null ? o2 : Math.max(o1, o2);
+  }
+
+  @CheckForNull
+  public List<TokensLine> cpdTokens(String componentKey) {
+    DefaultCpdTokens defaultCpdTokens = sensorStorage.cpdTokensByComponent.get(componentKey);
+    return defaultCpdTokens != null ? defaultCpdTokens.getTokenLines() : null;
+  }
+
+  @Override
+  public NewHighlighting newHighlighting() {
+    return new DefaultHighlighting(sensorStorage);
+  }
+
+  @Override
+  public NewCoverage newCoverage() {
+    return new DefaultCoverage(sensorStorage);
+  }
+
+  @Override
+  public NewCpdTokens newCpdTokens() {
+    return new DefaultCpdTokens(sensorStorage);
+  }
+
+  @Override
+  public NewSymbolTable newSymbolTable() {
+    return new DefaultSymbolTable(sensorStorage);
+  }
+
+  @Override
+  public NewAnalysisError newAnalysisError() {
+    return new DefaultAnalysisError(sensorStorage);
+  }
+
+  /**
+   * 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;
+  }
+
+  /**
+   * 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 void addContextProperty(String key, String value) {
+    sensorStorage.storeProperty(key, value);
+  }
+
+  /**
+   * @return an immutable map of the context properties defined with {@link SensorContext#addContextProperty(String, String)}.
+   * @since 6.1
+   */
+  public Map<String, String> getContextProperties() {
+    return unmodifiableMap(sensorStorage.contextProperties);
+  }
+
+  @Override
+  public void markForPublishing(InputFile inputFile) {
+    DefaultInputFile file = (DefaultInputFile) inputFile;
+    file.setPublished(true);
+  }
+
+  @Override
+  public NewSignificantCode newSignificantCode() {
+    return new DefaultSignificantCode(sensorStorage);
+  }
+}